Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapping Guidelines #3060

Merged
merged 110 commits into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from 107 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
8db5826
Snapping proof-of-concept
swissspidy Aug 21, 2019
042c0f5
First pass at snapping while dragging
swissspidy Aug 21, 2019
a66ed1e
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 22, 2019
477b326
Bail early if we haven’t changed position
swissspidy Aug 22, 2019
e2a193d
Fix drag snaps
swissspidy Aug 22, 2019
095fc85
Ensure clientId is passed down
swissspidy Aug 22, 2019
11c0f6e
Rename var
swissspidy Aug 22, 2019
76407cf
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 23, 2019
0bbaf1a
Take block rotation into account when building list of snap targets
swissspidy Aug 23, 2019
5cab0d5
Add todos
swissspidy Aug 23, 2019
c0132df
Try clearing snap lines if there are old ones but not new ones
swissspidy Aug 23, 2019
d5b9947
Performance improvements for dragging
swissspidy Aug 23, 2019
d7ad7d7
Hide resize and rotation handles when dragging
swissspidy Aug 23, 2019
28fb06d
Use getBoundingClientRect when dragging a rotated block
swissspidy Aug 23, 2019
0c1dd96
Check for existing snap lines in reducer
swissspidy Aug 26, 2019
6ddfa88
Return early when parent element wasn’t found
swissspidy Aug 26, 2019
be6cf10
Update return description
swissspidy Aug 26, 2019
0632437
Update snapping position while dragging rotated blocks
swissspidy Aug 26, 2019
3874521
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 26, 2019
693b7be
Allow disabling snapping when dragging
swissspidy Aug 26, 2019
7f78824
Fix center calculation
swissspidy Aug 26, 2019
1cca639
Add todo comment
swissspidy Aug 26, 2019
329ff32
Make leftSnap const
swissspidy Aug 27, 2019
416e26f
Add additional resizing snap targets
swissspidy Aug 27, 2019
78ba592
Add some docs
swissspidy Aug 27, 2019
2d1c5a7
Make resize snapping a bit more robust
swissspidy Aug 27, 2019
9e16d06
Fix snap checks when dragging
swissspidy Aug 27, 2019
929554c
Simplify drag-snapping a big
swissspidy Aug 27, 2019
9d68191
Fix getting stuck with left position
miina Aug 27, 2019
0350b01
Fix flickering for right horizontal snap.
miina Aug 27, 2019
64e5ee1
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 27, 2019
eb0f48c
Apply fixes for snapping and flickering for vertical snaps as well
swissspidy Aug 27, 2019
bb30343
Make findClosestSnap return null
swissspidy Aug 28, 2019
86834b5
Show snap lines when resizing a rotated block
swissspidy Aug 28, 2019
ba3c859
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 29, 2019
4f7cc16
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 30, 2019
46b92e6
Merge branch 'develop' into add/2875-snapping
swissspidy Aug 30, 2019
1ea5776
Use same position calculation also for non-rotated blocks
swissspidy Aug 30, 2019
8b078ab
Remove unused consts
swissspidy Aug 30, 2019
67a10f0
Resolve conflits.
miina Sep 3, 2019
9d6a2b3
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 9, 2019
4a74bf7
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 10, 2019
2225b70
Remove specific resizing snap targets for now
swissspidy Sep 10, 2019
bbaf355
Fix import order
swissspidy Sep 10, 2019
ea5f263
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 10, 2019
7c32015
Remove now unneeded binds
swissspidy Sep 10, 2019
063b24c
Remove actual snapping and only keep indicators for now
swissspidy Sep 11, 2019
301b633
Reimplement snap lines via React Context API
barklund Sep 16, 2019
781b6d9
Import React stuff from element package
swissspidy Sep 16, 2019
85c2577
Lowercase file names
swissspidy Sep 16, 2019
2b5e9e2
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 16, 2019
519efa9
Add missing HOC
swissspidy Sep 16, 2019
3741f23
Prevent erroneously displaying a number instead of snap lines
swissspidy Sep 16, 2019
8091a85
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 17, 2019
f2ac5eb
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 17, 2019
57a6923
Get the correct dimensions in case the block is rotated
swissspidy Sep 17, 2019
6ad1928
Add unit tests for new snapping context
barklund Sep 19, 2019
6423e07
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 23, 2019
7ec9388
Update package lock file
swissspidy Sep 23, 2019
398e4f2
Do not pass hasSnapLines to consuming component in SnapContext.Provider
swissspidy Sep 23, 2019
7208db1
Update position values for resizing e2e test
swissspidy Sep 23, 2019
981d6bd
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 27, 2019
a606d91
Use helper function to make snapping code more DRY
swissspidy Sep 30, 2019
8e1ebbd
Leverage getRelativeElementPosition helper
swissspidy Sep 30, 2019
5523652
Merge branch 'develop' into add/2875-snapping
swissspidy Sep 30, 2019
36d46a6
prop types fixes
swissspidy Sep 30, 2019
9e42965
Take rotation into account for snapping during resizing
swissspidy Sep 30, 2019
70d5326
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 2, 2019
ff01f93
Lint fixes
swissspidy Oct 2, 2019
a7e38fd
xMerge branch 'develop' into add/2875-snapping
swissspidy Oct 2, 2019
c3c196b
Fix prop type for draggable
swissspidy Oct 2, 2019
0a7b350
Fix prop type for BlockDraggable
swissspidy Oct 2, 2019
b66aa3b
Use correct blockElement cor CTA block
swissspidy Oct 2, 2019
81a41d8
Resizing: Prevent flickering by doing snapping _after_ correcting the…
swissspidy Oct 2, 2019
8030d5f
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 2, 2019
75edd44
Lint fix
swissspidy Oct 2, 2019
4fd6193
Move import to new line
swissspidy Oct 3, 2019
a9e7e10
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 3, 2019
eaef3d4
Fix selector in `getBlockInnerElement`
swissspidy Oct 3, 2019
1198cbb
UX improvements for snap targets
swissspidy Oct 3, 2019
e9938c8
Use correct lower and upper bounds for horizontal/vertical snap lines
swissspidy Oct 3, 2019
a6de413
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 3, 2019
b16e829
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 3, 2019
cb3ab61
Make sure snap props are always functions
swissspidy Oct 3, 2019
dc8f7d1
Reduce duplicated code
swissspidy Oct 3, 2019
0170cd8
Simplify center and start/end calculations
swissspidy Oct 3, 2019
2b1f977
Get rid of showSnapLines / hideSnapLines logic
swissspidy Oct 3, 2019
5ae88e7
Make sure selector is mocked when importing function
swissspidy Oct 3, 2019
9b4eb81
Moved snap line calculation to helper functions
barklund Oct 3, 2019
9bc8873
Add some tests for findClosestSnap
swissspidy Oct 4, 2019
9ab9c5c
Add test for getRelativeElementPosition
swissspidy Oct 4, 2019
5251aaa
Fix inverted condition to address duplicated snap line issue
swissspidy Oct 4, 2019
8b7a2aa
Add tests for createSnapList
swissspidy Oct 4, 2019
42f205b
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 4, 2019
6815295
Add a simple test for getSnapCalculatorByDimension
swissspidy Oct 4, 2019
e5dfb1b
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 4, 2019
1d09043
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 4, 2019
cb5c3c3
Fix eported name for isCTABlock
swissspidy Oct 4, 2019
afc7ef7
Use isCTABlock helper in more places
swissspidy Oct 4, 2019
11eab12
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 4, 2019
9515bed
Reorganised snap-related helpers
barklund Oct 4, 2019
ebff740
Extracted best snap finder to helper function
barklund Oct 4, 2019
aca7268
Added unit tests for `getBestMatch` and `getBestSnapLines`
barklund Oct 4, 2019
4e4a6b5
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 6, 2019
3cb2565
Lint fixes
swissspidy Oct 7, 2019
a661493
Optimize resizable snapping references
barklund Oct 7, 2019
f32cc1b
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 7, 2019
1852207
Make sure snap lines are above draggable overlay
swissspidy Oct 7, 2019
380673c
Merge branch 'develop' into add/2875-snapping
swissspidy Oct 7, 2019
2e2dc62
Update snapshot
swissspidy Oct 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
"no-template-curly-in-string": "error",
"no-throw-literal": "error",
"no-unmodified-loop-condition": "error",
"no-unused-vars": [
"error",
{
"ignoreRestSiblings": true,
}
],
"no-useless-call": "error",
"no-useless-concat": "error",
"prefer-object-spread": "error",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const BlockDraggable = ( { children, clientId, blockName, blockElementId, onDrag
transferData={ transferData }
onDragStart={ onDragStart }
onDragEnd={ onDragEnd }
clientId={ clientId }
clearHighlight={ clearHighlight }
setHighlightByOffset={ setHighlightByOffset }
dropElementByOffset={ dropElementByOffset }
Expand Down
89 changes: 80 additions & 9 deletions assets/src/stories-editor/components/block-mover/draggable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file is based on the core's <Draggable> Component.
* This file is based on core's <Draggable> Component.
**/

/**
Expand All @@ -12,20 +12,23 @@ import PropTypes from 'prop-types';
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withSafeTimeout } from '@wordpress/compose';
import { withSafeTimeout, compose } from '@wordpress/compose';

/**
* Internal dependencies
*/
import withSnapTargets from '../higher-order/with-snap-targets';
import {
getPixelsFromPercentage,
getPercentageFromPixels,
getRelativeElementPosition,
isCTABlock,
} from '../../helpers';
import { getBestSnapLines } from '../../helpers/snapping';
import {
STORY_PAGE_INNER_WIDTH,
STORY_PAGE_INNER_HEIGHT,
BLOCK_DRAGGING_SNAP_GAP,
STORY_PAGE_INNER_HEIGHT_FOR_CTA,
STORY_PAGE_MARGIN,
} from '../../constants';
Expand All @@ -39,6 +42,9 @@ const cloneWrapperClass = 'components-draggable__clone';
const isChromeUA = ( ) => /Chrome/i.test( navigator.userAgent );
const documentHasIframes = ( ) => [ ...document.getElementById( 'editor' ).querySelectorAll( 'iframe' ) ].length > 0;

let lastX;
let lastY;

class Draggable extends Component {
constructor( ...args ) {
super( ...args );
Expand Down Expand Up @@ -70,7 +76,7 @@ class Draggable extends Component {
* @param {Object} event The non-custom DragEvent.
*/
onDragEnd = ( event ) => {
const { clearHighlight, dropElementByOffset, blockName, setTimeout, onDragEnd = noop } = this.props;
const { clearHighlight, dropElementByOffset, blockName, setTimeout, clearSnapLines, onDragEnd = noop } = this.props;
if ( event ) {
event.preventDefault();
}
Expand Down Expand Up @@ -104,6 +110,9 @@ class Draggable extends Component {
}

this.resetDragState();

clearSnapLines();

setTimeout( onDragEnd );
}

Expand All @@ -112,9 +121,48 @@ class Draggable extends Component {
*
* @param {Object} event The non-custom DragEvent.
*/
onDragOver = ( event ) => {
const { setHighlightByOffset, blockName } = this.props;
onDragOver = ( event ) => { // eslint-disable-line complexity
const {
blockName,
setSnapLines,
clearSnapLines,
parentBlockElement,
horizontalSnaps,
verticalSnaps,
setHighlightByOffset,
} = this.props;

const top = parseInt( this.cloneWrapper.style.top ) + event.clientY - this.cursorTop;
const left = parseInt( this.cloneWrapper.style.left ) + event.clientX - this.cursorLeft;

if ( top === lastY && left === lastX ) {
return;
}

// Get the correct dimensions in case the block is rotated, as rotation is only applied to the clone's inner element(s).
// For CTA blocks, not the whole block is draggable, but only the button within.
const blockElement = isCTABlock( blockName ) ? this.cloneWrapper.querySelector( '.amp-story-cta-button' ) : this.cloneWrapper.querySelector( '.wp-block' );

// We calculate with the block's actual dimensions relative to the page it's on.
const {
top: actualTop,
right: actualRight,
bottom: actualBottom,
left: actualLeft,
} = getRelativeElementPosition( blockElement, parentBlockElement );

const snappingEnabled = ! event.getModifierState( 'Alt' );
Copy link
Contributor

@spacedmonkey spacedmonkey Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have a button that disables the snaps? Is this documented anywhere? How will users know about this?


if ( snappingEnabled ) {
const horizontalSnapsForPosition = horizontalSnaps( actualTop, actualBottom );
const verticalSnapsForPosition = verticalSnaps( actualLeft, actualRight );
setSnapLines( [
...getBestSnapLines( horizontalSnapsForPosition, actualLeft, actualRight, BLOCK_DRAGGING_SNAP_GAP ),
...getBestSnapLines( verticalSnapsForPosition, actualTop, actualBottom, BLOCK_DRAGGING_SNAP_GAP ),
] );
} else {
clearSnapLines();
}

// Don't allow the CTA button to go over its top limit.
if ( isCTABlock( blockName ) ) {
Expand All @@ -123,13 +171,15 @@ class Draggable extends Component {
this.cloneWrapper.style.top = `${ top }px`;
}

this.cloneWrapper.style.left =
`${ parseInt( this.cloneWrapper.style.left ) + event.clientX - this.cursorLeft }px`;
this.cloneWrapper.style.left = `${ left }px`;

// Update cursor coordinates.
this.cursorLeft = event.clientX;
this.cursorTop = event.clientY;

lastY = top;
lastX = left;

// Check if mouse (*not* element, but actual cursor) is over neighboring page to either side.
const currentElementLeft = parseInt( this.cloneWrapper.style.left );
const cursorLeftRelativeToPage = currentElementLeft + this.cursorLeftInsideElement;
Expand All @@ -143,6 +193,7 @@ class Draggable extends Component {
Math.ceil( ( cursorLeftRelativeToPage - PAGE_AND_MARGIN ) / PAGE_AND_MARGIN )
);
}

setHighlightByOffset( this.pageOffset );
}

Expand All @@ -160,7 +211,13 @@ class Draggable extends Component {
* @param {Object} event Custom DragEvent.
*/
onDragStart = ( event ) => {
const { blockName, elementId, transferData, onDragStart = noop } = this.props;
const {
blockName,
elementId,
transferData,
onDragStart = noop,
clearSnapLines,
} = this.props;
const blockIsCTA = isCTABlock( blockName );
// In the CTA block only the inner element (the button) is draggable, not the whole block.
const element = blockIsCTA ? document.getElementById( elementId ) : document.getElementById( elementId ).parentNode;
Expand Down Expand Up @@ -199,6 +256,7 @@ class Draggable extends Component {
this.cloneWrapper.style.height = `${ element.clientHeight }px`;

const clone = element.cloneNode( true );

this.cloneWrapper.style.transform = clone.style.transform;

// 20% of the full value in case of CTA block.
Expand Down Expand Up @@ -229,6 +287,7 @@ class Draggable extends Component {
// Mark the current cursor coordinates.
this.cursorLeft = event.clientX;
this.cursorTop = event.clientY;

// Update cursor to 'grabbing', document wide.
document.body.classList.add( 'is-dragging-components-draggable' );
document.addEventListener( 'dragover', this.onDragOver );
Expand All @@ -244,6 +303,8 @@ class Draggable extends Component {
document.addEventListener( 'drop', this.onDrop );
}

clearSnapLines();

this.props.setTimeout( onDragStart );
}

Expand Down Expand Up @@ -294,6 +355,16 @@ Draggable.propTypes = {
dropElementByOffset: PropTypes.func.isRequired,
setTimeout: PropTypes.func.isRequired,
children: PropTypes.func.isRequired,
horizontalSnaps: PropTypes.func.isRequired,
verticalSnaps: PropTypes.func.isRequired,
setSnapLines: PropTypes.func.isRequired,
clearSnapLines: PropTypes.func.isRequired,
parentBlockElement: PropTypes.object,
};

export default withSafeTimeout( Draggable );
const enhance = compose(
withSnapTargets,
withSafeTimeout,
);

export default enhance( Draggable );
9 changes: 9 additions & 0 deletions assets/src/stories-editor/components/block-mover/edit.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@
.wp-block[data-type="core/quote"] .editor-rich-text__editable:hover {
cursor: default;
}

.components-draggable__clone .components-resizable-box__handle,
.components-draggable__clone .rotatable-box-wrap::before,
.components-draggable__clone .rotatable-box-wrap__handle {
display: none;
opacity: 0;
visibility: hidden;
content: none;
}
77 changes: 77 additions & 0 deletions assets/src/stories-editor/components/contexts/snapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* External dependencies
*/
import PropTypes from 'prop-types';

/**
* WordPress dependencies
*/
import { SVG } from '@wordpress/components';
import { createHigherOrderComponent } from '@wordpress/compose';
import { createContext, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import { STORY_PAGE_INNER_HEIGHT, STORY_PAGE_INNER_WIDTH } from '../../constants';

const SnapContext = createContext();

const Snapping = ( { children } ) => {
const [ snapLines, setSnapLines ] = useState( [] );

const clearSnapLines = () => setSnapLines( [] );

const context = {
setSnapLines,
snapLines,
clearSnapLines,
};

return (
<SnapContext.Provider value={ context }>
{ children }
{ Boolean( snapLines.length ) && (
<SVG
viewBox={ `0 0 ${ STORY_PAGE_INNER_WIDTH } ${ STORY_PAGE_INNER_HEIGHT }` }
style={ {
position: 'absolute',
top: 0,
pointerEvents: 'none',
} }
>
{ snapLines.map( ( [ [ x1, y1 ], [ x2, y2 ] ], index ) => (
<line
key={ index }
x1={ x1 }
y1={ y1 }
x2={ x2 }
y2={ y2 }
stroke="red"
pointerEvents="none"
/>
) ) }
</SVG>
) }
</SnapContext.Provider>
);
};

Snapping.propTypes = {
children: PropTypes.object,
};

export default Snapping;

export const withSnapContext = createHigherOrderComponent(
( WrappedComponent ) => ( props ) => (
<SnapContext.Consumer>
{
( snappingProps ) => (
<WrappedComponent { ...props } { ...snappingProps } />
)
}
</SnapContext.Consumer>
),
'withSnapContext',
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Snapping should render a single snap line correctly when set 1`] = `
<line
key="0"
pointerEvents="none"
stroke="red"
x1={0}
x2={100}
y1={100}
y2={100}
/>
`;
Loading