Skip to content

Commit

Permalink
First working iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
fullofcaffeine committed May 31, 2024
1 parent 2ea58e7 commit 4ef5979
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 80 deletions.
88 changes: 70 additions & 18 deletions packages/components/src/drop-zone/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useState, useEffect } from '@wordpress/element';
import { upload, Icon } from '@wordpress/icons';
import { getFilesFromDataTransfer } from '@wordpress/dom';
import {
Expand All @@ -20,23 +20,72 @@ import {
*/
import type { DropType, DropZoneProps } from './types';
import type { WordPressComponentProps } from '../context';
import type { ReactNode } from 'react';

interface FadeProps {
show: boolean;
children: ReactNode;
duration?: number;
}

// Could call it Animate? Also, probably need to move out so it can be used
// across other components. The idea is to "hook" into the mount/unmount
// lifecycle to animate the component.
const Fade: React.FC< FadeProps > = ( { show, children, duration } ) => {
const [ shouldRender, setRender ] = useState( show );

useEffect( () => {
if ( show ) {
setRender( true );
}
}, [ show ] );

const onAnimationEnd = () => {
if ( ! show ) {
setRender( false );
}
};

function DropIndicator( { label }: { label?: string } ) {
return (
<div
className="components-drop-zone__content"
style={ { pointerEvents: 'none' } }
>
<div className="components-drop-zone__content-inner">
<Icon
icon={ upload }
className="components-drop-zone__content-icon"
/>
<span className="components-drop-zone__content-text">
{ label ? label : __( 'Drop files to upload' ) }
</span>
shouldRender && (
<div
className={ show ? 'fade-enter' : 'fade-exit' }
style={ { animationDuration: `${ duration }ms` } }
onAnimationEnd={ onAnimationEnd }
>
{ children }
</div>
</div>
)
);
};

function DropIndicator( {
label,
isActive,
}: {
label?: string;
isActive: boolean;
} ) {
const disableMotion = false;
return (
<Fade show={ isActive } duration={ disableMotion ? 0 : 200 }>
<div
className={ `components-drop-zone__content ${
disableMotion ? 'no-motion' : ''
}` }
style={ { pointerEvents: 'none' } }
>
<div className="components-drop-zone__content-inner">
<Icon
icon={ upload }
className="components-drop-zone__content-icon"
/>
<span className="components-drop-zone__content-text">
{ label ? label : __( 'Drop files to upload' ) }
</span>
</div>
</div>
</Fade>
);
}

Expand Down Expand Up @@ -132,10 +181,10 @@ export function DropZoneComponent( {
setIsDraggingOverElement( false );
},
} );
const isDraggingOver = isDraggingOverDocument || isDraggingOverElement;
const isDragging = isDraggingOverDocument || isDraggingOverElement;
const classes = clsx( 'components-drop-zone', className, {
'is-active':
isDraggingOver &&
isDragging &&
( ( type === 'file' && onFilesDrop ) ||
( type === 'html' && onHTMLDrop ) ||
( type === 'default' && onDrop ) ),
Expand All @@ -146,7 +195,10 @@ export function DropZoneComponent( {

return (
<div { ...restProps } ref={ ref } className={ classes }>
{ isDraggingOverElement && <DropIndicator label={ label } /> }
<DropIndicator
label={ label }
isActive={ isDraggingOverElement || false }
/>
</div>
);
}
Expand Down
119 changes: 57 additions & 62 deletions packages/components/src/drop-zone/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

.fade-enter {
animation: fadeIn 0.2s forwards;
}

.fade-exit {
animation: fadeOut 0.2s forwards;
}

.components-drop-zone {
position: absolute;
top: 0;
Expand All @@ -8,74 +34,43 @@
visibility: hidden;
opacity: 0;
border-radius: $radius-block-ui;
transition:
opacity 0.2s ease-in-out,
visibility 0.2s step-end;

&.is-active {
visibility: visible;
opacity: 1;
transition:
opacity 0.2s ease-in-out,
visibility 0s step-start;
}

&__content {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
display: flex;
background-color: $components-color-accent;
align-items: center;
justify-content: center;
z-index: z-index(".components-drop-zone__content");
text-align: center;
color: $white;
opacity: 0;
transform: scale(0.9);
transition:
opacity 0.2s ease-in-out,
transform 0.2s ease-in-out;
transition-delay: 0.1s;
pointer-events: none;

.components-drop-zone.is-active & {
opacity: 1;
transform: scale(1);
}
visibility: visible;
}
}

&__content-inner {
opacity: 0;
transform: scale(0.9);
transition:
opacity 0.1s ease-in-out,
transform 0.1s ease-in-out;

.components-drop-zone.is-active & {
opacity: 1;
transform: scale(1);
}
}
.components-drop-zone__content {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
display: flex;
background-color: $components-color-accent;
align-items: center;
justify-content: center;
z-index: z-index(".components-drop-zone__content");
text-align: center;
color: $white;
}

&__content-icon,
&__content-text {
display: block;
}
.components-drop-zone__content-icon,
.components-drop-zone__content-text {
display: block;
}

&__content-icon {
margin: 0 auto $grid-unit-10;
line-height: 0;
fill: currentColor;
pointer-events: none;
}
.components-drop-zone__content-icon {
margin: 0 auto $grid-unit-10;
line-height: 0;
fill: currentColor;
pointer-events: none;
}

&__content-text {
font-family: $default-font;
font-size: $default-font-size;
}
.components-drop-zone__content-text {
font-family: $default-font;
font-size: $default-font-size;
}

0 comments on commit 4ef5979

Please sign in to comment.