Explorar o código

dashboard: remove preact-css-transition-group (#2444)

Unfortunately I can't figure out how to make the dependencies and
versions work out well here.

Instead, this uses a custom slide transition component. It is much more
simple than preact-css-transition-group, which supports animating groups
of components. The `<Slide />` component:

- can only support one element, that either exists or does not exist
- requires that the child element takes a `className` property, that it
  passes onto its root element
- only uses `setTimeout()` for when the transition finishes
  (in preact-css-transition-group, the `transitionend` event is used,
  which is more _accurate_ but less _reliable_)
- hardcodes the transition duration
- hardcodes the transition name

This makes it much more limited. Hopefully the reduced complexity is
worth it.

If we have other transitions in the future, we can copy-paste this
component.
Renée Kooi %!s(int64=4) %!d(string=hai) anos
pai
achega
e6b876c419

+ 0 - 1
packages/@uppy/dashboard/package.json

@@ -35,7 +35,6 @@
     "lodash.throttle": "^4.1.1",
     "memoize-one": "^5.0.4",
     "preact": "8.2.9",
-    "preact-css-transition-group": "^1.3.0",
     "resize-observer-polyfill": "^1.5.0"
   },
   "peerDependencies": {

+ 2 - 1
packages/@uppy/dashboard/src/components/AddFilesPanel.js

@@ -1,10 +1,11 @@
 const { h } = require('preact')
+const classNames = require('classnames')
 const AddFiles = require('./AddFiles')
 
 const AddFilesPanel = (props) => {
   return (
     <div
-      class="uppy-Dashboard-AddFilesPanel"
+      class={classNames('uppy-Dashboard-AddFilesPanel', props.className)}
       data-uppy-panelType="AddFiles"
       aria-hidden={props.showAddFilesPanel}
     >

+ 9 - 21
packages/@uppy/dashboard/src/components/Dashboard.js

@@ -5,26 +5,14 @@ const PickerPanelContent = require('./PickerPanelContent')
 const EditorPanel = require('./EditorPanel')
 const PanelTopBar = require('./PickerPanelTopBar')
 const FileCard = require('./FileCard')
+const Slide = require('./Slide')
 const classNames = require('classnames')
 const isDragDropSupported = require('@uppy/utils/lib/isDragDropSupported')
 const { h } = require('preact')
-const PreactCSSTransitionGroup = require('preact-css-transition-group')
 
 // http://dev.edenspiekermann.com/2016/02/11/introducing-accessible-modal-dialog
 // https://github.com/ghosh/micromodal
 
-function TransitionWrapper (props) {
-  return (
-    <PreactCSSTransitionGroup
-      transitionName="uppy-transition-slideDownUp"
-      transitionEnterTimeout={250}
-      transitionLeaveTimeout={250}
-    >
-      {props.children}
-    </PreactCSSTransitionGroup>
-  )
-}
-
 const WIDTH_XL = 900
 const WIDTH_LG = 700
 const WIDTH_MD = 576
@@ -118,21 +106,21 @@ module.exports = function Dashboard (props) {
             <AddFiles {...props} isSizeMD={isSizeMD} />
           )}
 
-          <TransitionWrapper>
+          <Slide>
             {props.showAddFilesPanel ? <AddFilesPanel key="AddFiles" {...props} isSizeMD={isSizeMD} /> : null}
-          </TransitionWrapper>
+          </Slide>
 
-          <TransitionWrapper>
+          <Slide>
             {props.fileCardFor ? <FileCard key="FileCard" {...props} /> : null}
-          </TransitionWrapper>
+          </Slide>
 
-          <TransitionWrapper>
+          <Slide>
             {props.activePickerPanel ? <PickerPanelContent key="Picker" {...props} /> : null}
-          </TransitionWrapper>
+          </Slide>
 
-          <TransitionWrapper>
+          <Slide>
             {props.showFileEditor ? <EditorPanel key="Editor" {...props} /> : null}
-          </TransitionWrapper>
+          </Slide>
 
           <div class="uppy-Dashboard-progressindicators">
             {props.progressindicators.map((target) => {

+ 2 - 1
packages/@uppy/dashboard/src/components/EditorPanel.js

@@ -1,11 +1,12 @@
 const { h } = require('preact')
+const classNames = require('classnames')
 
 function EditorPanel (props) {
   const file = this.props.files[this.props.fileCardFor]
 
   return (
     <div
-      class="uppy-DashboardContent-panel"
+      class={classNames('uppy-DashboardContent-panel', props.className)}
       role="tabpanel"
       data-uppy-panelType="FileEditor"
       id="uppy-DashboardContent-panel--editor"

+ 2 - 1
packages/@uppy/dashboard/src/components/FileCard/index.js

@@ -1,4 +1,5 @@
 const { h, Component } = require('preact')
+const classNames = require('classnames')
 const getFileTypeIcon = require('../../utils/getFileTypeIcon')
 const ignoreEvent = require('../../utils/ignoreEvent.js')
 const FilePreview = require('../FilePreview')
@@ -89,7 +90,7 @@ class FileCard extends Component {
 
     return (
       <div
-        class="uppy-Dashboard-FileCard"
+        class={classNames('uppy-Dashboard-FileCard', this.props.className)}
         data-uppy-panelType="FileCard"
         onDragOver={ignoreEvent}
         onDragLeave={ignoreEvent}

+ 2 - 1
packages/@uppy/dashboard/src/components/PickerPanelContent.js

@@ -1,10 +1,11 @@
 const { h } = require('preact')
+const classNames = require('classnames')
 const ignoreEvent = require('../utils/ignoreEvent.js')
 
 function PickerPanelContent (props) {
   return (
     <div
-      class="uppy-DashboardContent-panel"
+      class={classNames('uppy-DashboardContent-panel', props.className)}
       role="tabpanel"
       data-uppy-panelType="PickerPanel"
       id={`uppy-DashboardContent-panel--${props.activePickerPanel.id}`}

+ 96 - 0
packages/@uppy/dashboard/src/components/Slide.js

@@ -0,0 +1,96 @@
+const { cloneElement, Component } = require('preact')
+const classNames = require('classnames')
+
+const transitionName = 'uppy-transition-slideDownUp'
+const duration = 250
+
+/**
+ * Vertical slide transition.
+ *
+ * This can take a _single_ child component, which _must_ accept a `className` prop.
+ *
+ * Currently this is specific to the `uppy-transition-slideDownUp` transition,
+ * but it should be simple to extend this for any type of single-element
+ * transition by setting the CSS name and duration as props.
+ */
+class Slide extends Component {
+  constructor (props) {
+    super(props)
+
+    this.state = {
+      cachedChildren: null,
+      className: ''
+    }
+  }
+
+  componentWillUpdate (nextProps) {
+    const { cachedChildren } = this.state
+    const child = nextProps.children[0]
+
+    if (cachedChildren === child) return
+
+    const patch = {
+      cachedChildren: child
+    }
+
+    // Enter transition
+    if (child && !cachedChildren) {
+      patch.className = `${transitionName}-enter`
+
+      cancelAnimationFrame(this.animationFrame)
+      clearTimeout(this.leaveTimeout)
+      this.leaveTimeout = undefined
+
+      this.animationFrame = requestAnimationFrame(() => {
+        // Force it to render before we add the active class
+        this.base.getBoundingClientRect()
+
+        this.setState({
+          className: `${transitionName}-enter ${transitionName}-enter-active`
+        })
+
+        this.enterTimeout = setTimeout(() => {
+          this.setState({ className: '' })
+        }, duration)
+      })
+    }
+
+    // Leave transition
+    if (cachedChildren && !child && this.leaveTimeout === undefined) {
+      patch.cachedChildren = cachedChildren
+      patch.className = `${transitionName}-leave`
+
+      cancelAnimationFrame(this.animationFrame)
+      clearTimeout(this.enterTimeout)
+      this.enterTimeout = undefined
+      this.animationFrame = requestAnimationFrame(() => {
+        this.setState({
+          className: `${transitionName}-leave ${transitionName}-leave-active`
+        })
+
+        this.leaveTimeout = setTimeout(() => {
+          this.setState({
+            cachedChildren: null,
+            className: ''
+          })
+        }, duration)
+      })
+    }
+
+    this.setState(patch)
+  }
+
+  render () {
+    const { cachedChildren, className } = this.state
+
+    if (!cachedChildren) {
+      return null
+    }
+
+    return cloneElement(cachedChildren, {
+      className: classNames(className, cachedChildren.attributes.className)
+    })
+  }
+}
+
+module.exports = Slide