Skip to content

Commit

Permalink
feat: Add CSS Grid grid-gap support (#657)
Browse files Browse the repository at this point in the history
* Added CSS grid-gap check to SortableContainer
* Add variable scale grid sorting story
* Update types and expose nodes and helper for all events

Co-authored-by: Matteo Mazzarolo <mazzarolomatteo@gmail.com>
  • Loading branch information
clauderic and mmazzarolo committed Jan 20, 2020
1 parent c8c5147 commit 4efcaa2
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 54 deletions.
78 changes: 49 additions & 29 deletions src/.stories/Storybook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -112,31 +112,31 @@ $focusedOutlineColor: #4c9ffe;

// Grid
.grid {
display: block;
width: 130 * 4px;
height: 350px;
display: grid;
height: 130 * 3px + 20px;
grid-gap: 10px;
grid-template-columns: auto auto auto auto;
width: auto;
white-space: nowrap;
border: 0;
background-color: transparent;
}

.gridItem {
float: left;
width: 130px;
padding: 8px;
background: transparent;
border: 0;
height: 130px;
padding: 0;
border: none;
background-color: transparent;

.wrapper {
display: flex;
align-items: center;
justify-content: center;

width: 100%;
height: 100%;
background: #fff;
border: 1px solid #efefef;

background-color: #fff;
transform: scale(1);
font-size: 28px;

span {
Expand All @@ -145,6 +145,31 @@ $focusedOutlineColor: #4c9ffe;
}
}

.gridVariableSized {
.gridItem {
&[data-index='0'] {
width: auto !important;
height: auto !important;
grid-column-end: span 2;
grid-row-end: span 2;
}
}

&.isSorting {
.gridItem {
transition: transform 150ms ease;
}
}
}

.gridItemVariableSized {
&[data-index='0'] {
.wrapper {
font-size: 56px;
}
}
}

// Nested
.category {
height: auto;
Expand Down Expand Up @@ -183,29 +208,24 @@ $focusedOutlineColor: #4c9ffe;
}

.stylizedHelper {
border: 1px solid #efefef;
box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2);
background-color: rgba(255, 255, 255, 0.9);
border-radius: 3px;

&.horizontalItem {
cursor: col-resize;
}
&:not(.gridItem),
&.gridItem .wrapper {
border: 1px solid #efefef;
box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.2);
background-color: rgba(255, 255, 255, 0.9);
border-radius: 3px;

&.gridItem {
background-color: transparent;
white-space: nowrap;
box-shadow: none;
border: none;
&.horizontalItem {
cursor: col-resize;
}

.wrapper {
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 0 7px rgba(0, 0, 0, 0.15);
&:focus {
box-shadow: 0 0px 5px 1px $focusedOutlineColor;
}
}

&:focus {
box-shadow: 0 0px 5px 1px $focusedOutlineColor;
&.gridItem .wrapper {
transition: transform 150ms ease-in-out;
}
}

Expand Down
113 changes: 94 additions & 19 deletions src/.stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function getItems(count, height) {
return range(count).map((value) => {
return {
value,
height: height || heights[random(0, heights.length - 1)],
height: height == null ? heights[random(0, heights.length - 1)] : height,
};
});
}
Expand All @@ -47,7 +47,7 @@ const Item = SortableElement(
style: propStyle,
shouldUseDragHandle,
value,
type,
itemIndex,
isSorting,
}) => {
const bodyTabIndex = tabbable && !shouldUseDragHandle ? 0 : -1;
Expand All @@ -66,6 +66,7 @@ const Item = SortableElement(
...propStyle,
}}
tabIndex={bodyTabIndex}
data-index={itemIndex}
>
{shouldUseDragHandle && <Handle tabIndex={handleTabIndex} />}
<div className={style.wrapper}>
Expand Down Expand Up @@ -99,6 +100,7 @@ const SortableList = SortableContainer(
isDisabled={disabled}
className={itemClass}
index={index}
itemIndex={index}
value={value}
height={height}
shouldUseDragHandle={shouldUseDragHandle}
Expand Down Expand Up @@ -178,14 +180,14 @@ class ListWrapper extends Component {
height: 600,
};

onSortStart = () => {
onSortStart = (...args) => {
const {onSortStart} = this.props;
this.setState({isSorting: true});

document.body.style.cursor = 'grabbing';

if (onSortStart) {
onSortStart(this.refs.component);
onSortStart(this.refs.component, ...args);
}
};

Expand Down Expand Up @@ -230,7 +232,7 @@ const SortableReactWindow = (Component) =>
return (
<Component
ref="VirtualList"
className={className}
className={classNames(className, style.isSorting)}
itemSize={
itemHeight == null ? (index) => items[index].height : itemHeight
}
Expand Down Expand Up @@ -510,20 +512,93 @@ storiesOf('General | Layout / Horizontal list', module).add(
},
);

storiesOf('General | Layout / Grid', module).add('Basic setup', () => {
return (
<div className={style.root}>
<ListWrapper
component={SortableList}
axis={'xy'}
items={getItems(10, 110)}
helperClass={style.stylizedHelper}
className={classNames(style.list, style.stylizedList, style.grid)}
itemClass={classNames(style.stylizedItem, style.gridItem)}
/>
</div>
);
});
storiesOf('General | Layout / Grid', module)
.add('Basic setup', () => {
const transformOrigin = {
x: 0,
y: 0,
};

return (
<div className={style.root}>
<ListWrapper
component={SortableList}
axis={'xy'}
items={getItems(10, false)}
helperClass={style.stylizedHelper}
className={classNames(style.list, style.stylizedList, style.grid)}
itemClass={classNames(style.stylizedItem, style.gridItem)}
/>
</div>
);
})
.add('Large first item', () => {
const transformOrigin = {
x: 0,
y: 0,
};

return (
<div className={style.root}>
<ListWrapper
component={SortableList}
axis={'xy'}
items={getItems(9, false)}
helperClass={style.stylizedHelper}
className={classNames(
style.list,
style.stylizedList,
style.grid,
style.gridVariableSized,
)}
itemClass={classNames(
style.stylizedItem,
style.gridItem,
style.gridItemVariableSized,
)}
onSortStart={(_, {node}, event) => {
const boundingClientRect = node.getBoundingClientRect();

transformOrigin.x =
((event.clientX - boundingClientRect.left) /
boundingClientRect.width) *
100;
transformOrigin.y =
((event.clientY - boundingClientRect.top) /
boundingClientRect.height) *
100;
}}
onSortOver={({nodes, newIndex, index, helper}) => {
const finalNodes = arrayMove(nodes, index, newIndex);
const oldNode = nodes[index].node;
const newNode = nodes[newIndex].node;
const helperScale = newNode.offsetWidth / oldNode.offsetWidth;
const helperWrapperNode = helper.childNodes[0];

helperWrapperNode.style.transform = `scale(${helperScale})`;
helperWrapperNode.style.transformOrigin = `${transformOrigin.x}% ${transformOrigin.y}%`;

finalNodes.forEach(({node}, i) => {
const oldNode = nodes[i].node;
const scale = oldNode.offsetWidth / node.offsetWidth;
const wrapperNode = node.childNodes[0];

wrapperNode.style.transform = `scale(${scale})`;
wrapperNode.style.transformOrigin =
newIndex > i ? '0 0' : '100% 0';
});
}}
onSortEnd={() => {
[...document.querySelectorAll(`.${style.wrapper}`)].forEach(
(node) => {
node.style.transform = '';
},
);
}}
/>
</div>
);
});

storiesOf('General | Configuration / Options', module)
.add('Drag handle', () => {
Expand Down
26 changes: 21 additions & 5 deletions src/SortableContainer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
closest,
events,
getScrollingParent,
getContainerGridGap,
getEdgeOffset,
getElementMargin,
getLockPixelOffsets,
Expand Down Expand Up @@ -260,16 +261,18 @@ export default function sortableContainer(
// Need to get the latest value for `index` in case it changes during `updateBeforeSortStart`
const {index} = node.sortableInfo;
const margin = getElementMargin(node);
const gridGap = getContainerGridGap(this.container);
const containerBoundingRect = this.scrollContainer.getBoundingClientRect();
const dimensions = getHelperDimensions({collection, index, node});
const dimensions = getHelperDimensions({index, node, collection});

this.node = node;
this.margin = margin;
this.gridGap = gridGap;
this.width = dimensions.width;
this.height = dimensions.height;
this.marginOffset = {
x: this.margin.left + this.margin.right,
y: Math.max(this.margin.top, this.margin.bottom),
x: this.margin.left + this.margin.right + this.gridGap.x,
y: Math.max(this.margin.top, this.margin.bottom, this.gridGap.y),
};
this.boundingClientRect = node.getBoundingClientRect();
this.containerBoundingRect = containerBoundingRect;
Expand Down Expand Up @@ -425,7 +428,17 @@ export default function sortableContainer(
});

if (onSortStart) {
onSortStart({node, index, collection, isKeySorting}, event);
onSortStart(
{
node,
index,
collection,
isKeySorting,
nodes: this.manager.getOrderedRefs(),
helper: this.helper,
},
event,
);
}

if (isKeySorting) {
Expand Down Expand Up @@ -458,7 +471,7 @@ export default function sortableContainer(
active: {collection},
isKeySorting,
} = this.manager;
const nodes = this.manager.refs[collection];
const nodes = this.manager.getOrderedRefs();

// Remove the event listeners if the node is still in the DOM
if (this.listenerNode) {
Expand Down Expand Up @@ -533,6 +546,7 @@ export default function sortableContainer(
newIndex: this.newIndex,
oldIndex: this.index,
isKeySorting,
nodes,
},
event,
);
Expand Down Expand Up @@ -818,6 +832,8 @@ export default function sortableContainer(
newIndex: this.newIndex,
oldIndex,
isKeySorting,
nodes,
helper: this.helper,
});
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@ export function getScrollingParent(el) {
}
}

export function getContainerGridGap(element) {
const style = window.getComputedStyle(element);

if (style.display === 'grid') {
return {
x: getPixelValue(style.gridColumnGap),
y: getPixelValue(style.gridRowGap),
};
}

return {x: 0, y: 0};
}

export const KEYCODE = {
TAB: 9,
ESC: 27,
Expand Down
Loading

0 comments on commit 4efcaa2

Please sign in to comment.