Prechádzať zdrojové kódy

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

Dashboard open/close animation
Artur Paikin 6 rokov pred
rodič
commit
51df805

+ 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)
 - [ ] 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)
 - [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
 - [ ] 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: 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: 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
 - [ ] 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) => {
 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" />
     <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>
   </svg><span class="uppy-Dashboard-poweredByUppy">Uppy</span></a>
 }
 }
@@ -33,6 +33,8 @@ module.exports = function Dashboard (props) {
     { 'uppy-Root': props.isTargetDOMEl },
     { 'uppy-Root': props.isTargetDOMEl },
     'uppy-Dashboard',
     'uppy-Dashboard',
     { 'Uppy--isTouchDevice': isTouchDevice() },
     { 'Uppy--isTouchDevice': isTouchDevice() },
+    { 'uppy-Dashboard--animateOpenClose': props.animateOpenClose },
+    { 'uppy-Dashboard--isClosing': props.isClosing },
     { 'uppy-Dashboard--modal': !props.inline },
     { 'uppy-Dashboard--modal': !props.inline },
     { 'uppy-Dashboard--wide': props.isWide }
     { '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
 // MIT licence, https://github.com/ghosh/micromodal/blob/master/LICENSE.md
 // Copyright (c) 2017 Indrashish Ghosh
 // Copyright (c) 2017 Indrashish Ghosh
 const FOCUSABLE_ELEMENTS = [
 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
  * Dashboard UI with previews, metadata editing, tabs for various services and more
  */
  */
@@ -106,6 +109,7 @@ module.exports = class Dashboard extends Plugin {
       disableInformer: false,
       disableInformer: false,
       disableThumbnailGenerator: false,
       disableThumbnailGenerator: false,
       disablePageScrollWhenModalOpen: true,
       disablePageScrollWhenModalOpen: true,
+      animateOpenClose: true,
       proudlyDisplayPoweredByUppy: true,
       proudlyDisplayPoweredByUppy: true,
       onRequestCloseModal: () => this.closeModal(),
       onRequestCloseModal: () => this.closeModal(),
       locale: defaultLocale
       locale: defaultLocale
@@ -242,23 +246,43 @@ module.exports = class Dashboard extends Plugin {
     this.savedActiveElement = document.activeElement
     this.savedActiveElement = document.activeElement
 
 
     if (this.opts.disablePageScrollWhenModalOpen) {
     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.rerender(this.uppy.getState())
     this.updateDashboardElWidth()
     this.updateDashboardElWidth()
     this.setFocusToBrowse()
     this.setFocusToBrowse()
   }
   }
 
 
   closeModal () {
   closeModal () {
-    this.setPluginState({
-      isHidden: true
-    })
-
     if (this.opts.disablePageScrollWhenModalOpen) {
     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()
     this.savedActiveElement.focus()
   }
   }
 
 
@@ -268,9 +292,9 @@ module.exports = class Dashboard extends Plugin {
 
 
   onKeydown (event) {
   onKeydown (event) {
     // close modal on esc key press
     // 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
     // maintainFocus on tab key press
-    if (event.keyCode === 9) this.maintainFocus(event)
+    if (event.keyCode === TAB_KEY) this.maintainFocus(event)
   }
   }
 
 
   handleClickOutside () {
   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')
       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
     // Drag Drop
     this.removeDragDropListener = dragDrop(this.el, (files) => {
     this.removeDragDropListener = dragDrop(this.el, (files) => {
       this.handleDrop(files)
       this.handleDrop(files)
@@ -342,10 +362,6 @@ module.exports = class Dashboard extends Plugin {
       showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
       showModalTrigger.forEach(trigger => trigger.removeEventListener('click', this.openModal))
     }
     }
 
 
-    if (!this.opts.inline) {
-      document.removeEventListener('keydown', this.onKeydown)
-    }
-
     this.removeDragDropListener()
     this.removeDragDropListener()
     window.removeEventListener('resize', this.updateDashboardElWidth)
     window.removeEventListener('resize', this.updateDashboardElWidth)
   }
   }
@@ -456,6 +472,8 @@ module.exports = class Dashboard extends Plugin {
       totalProgress: state.totalProgress,
       totalProgress: state.totalProgress,
       acquirers: acquirers,
       acquirers: acquirers,
       activePanel: pluginState.activePanel,
       activePanel: pluginState.activePanel,
+      animateOpenClose: this.opts.animateOpenClose,
+      isClosing: pluginState.isClosing,
       getPlugin: this.uppy.getPlugin,
       getPlugin: this.uppy.getPlugin,
       progressindicators: progressindicators,
       progressindicators: progressindicators,
       autoProceed: this.uppy.opts.autoProceed,
       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 {
 .uppy-Dashboard--modal {
   z-index: $zIndex-2;
   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
 // Added to body to prevent the page from scrolling when Modal is open
-.uppy-Dashboard-isOpen {
+.uppy-Dashboard-isFixed {
   overflow: hidden;
   overflow: hidden;
   height: 100vh;
   height: 100vh;
 }
 }
@@ -289,48 +323,31 @@
     font-size: 16px;
     font-size: 16px;
     line-height: 50px;
     line-height: 50px;
     max-width: 300px;
     max-width: 300px;
-    // top: 15px;
   }
   }
 }
 }
 
 
-.uppy-DashboardContent-titleFile {
-  // text-decoration: underline;
-}
-
 .uppy-DashboardContent-back {
 .uppy-DashboardContent-back {
   @include reset-button;
   @include reset-button;
-  // position: absolute;
-  // top: 0;
-  // left: 15px;
   font-size: 14px;
   font-size: 14px;
-  // line-height: 40px;
   font-weight: 500;
   font-weight: 500;
   cursor: pointer;
   cursor: pointer;
   color: $color-cornflower-blue;
   color: $color-cornflower-blue;
 
 
   .uppy-Dashboard--wide & {
   .uppy-Dashboard--wide & {
     font-size: 15px;
     font-size: 15px;
-    // line-height: 50px;
   }
   }
 }
 }
 
 
-// .uppy-DashboardContent-back .UppyIcon {
-//   position: relative;
-//   margin-right: 3px;
-// }
-
 .uppy-DashboardContent-panel {
 .uppy-DashboardContent-panel {
   position: absolute;
   position: absolute;
   top: 0;
   top: 0;
   bottom: 0;
   bottom: 0;
   left: 0;
   left: 0;
   right: 0;
   right: 0;
-  transform: translateY(-105%);
+  transform: translate3d(0, -105%, 0);
   transition: transform 0.2s ease-in-out;
   transition: transform 0.2s ease-in-out;
-  will-change: transform;
   background-color: darken($color-white, 4%);
   background-color: darken($color-white, 4%);
   box-shadow: 0 0 10px 5px rgba($color-black, 0.15);
   box-shadow: 0 0 10px 5px rgba($color-black, 0.15);
-  // padding: 15px;
   padding-top: 40px;
   padding-top: 40px;
   overflow: hidden;
   overflow: hidden;
   z-index: $zIndex-4;
   z-index: $zIndex-4;
@@ -338,14 +355,10 @@
   .uppy-Dashboard--wide & {
   .uppy-Dashboard--wide & {
     padding-top: 50px;
     padding-top: 50px;
   }
   }
-
-  // .uppy-Dashboard--modal & {
-  //   z-index: $zIndex-4;
-  // }
 }
 }
 
 
 .uppy-DashboardContent-panel[aria-hidden=false] {
 .uppy-DashboardContent-panel[aria-hidden=false] {
-  transform: none;
+  transform: translate3d(0, 0, 0);
 }
 }
 
 
 // Progress bar placeholder
 // Progress bar placeholder
@@ -429,29 +442,6 @@
   border-color: darken($color-white, 20%);
   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 {
 .uppy-Dashboard-bgIcon {
   height: 100%;
   height: 100%;
   display: flex;
   display: flex;
@@ -1012,8 +1002,9 @@
 //
 //
 
 
 .uppy-DashboardFileCard {
 .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%;
   width: 100%;
   height: 100%;
   height: 100%;
   position: absolute;
   position: absolute;
@@ -1026,9 +1017,9 @@
   background-color: $color-white;
   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 {
 .uppy-DashboardFileCard-inner {
   display: flex;
   display: flex;
@@ -1093,3 +1084,6 @@
   vertical-align: middle;
   vertical-align: middle;
   width: 78%;
   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.
 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`
 ### `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.
 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.