- MAJOR DND PROGRESS UPDATE
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
13
client/package-lock.json
generated
13
client/package-lock.json
generated
@@ -65,6 +65,7 @@
|
|||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
"react-sticky": "^6.0.3",
|
"react-sticky": "^6.0.3",
|
||||||
"react-virtualized": "^9.22.5",
|
"react-virtualized": "^9.22.5",
|
||||||
|
"react-virtuoso": "^4.7.11",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-actions": "^3.0.0",
|
"redux-actions": "^3.0.0",
|
||||||
@@ -15888,6 +15889,18 @@
|
|||||||
"react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0"
|
"react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-virtuoso": {
|
||||||
|
"version": "4.7.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.7.11.tgz",
|
||||||
|
"integrity": "sha512-Kdn9qEtQI2ulEuBMzW2BTkDsfijB05QUd6lpZ1K36oyA3k65Cz4lG4EKrh2pCfUafX4C2uMSZOwzMOhbrMOTFA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16 || >=17 || >= 18",
|
||||||
|
"react-dom": ">=16 || >=17 || >= 18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reactcss": {
|
"node_modules/reactcss": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
"react-router-dom": "^6.23.1",
|
"react-router-dom": "^6.23.1",
|
||||||
"react-sticky": "^6.0.3",
|
"react-sticky": "^6.0.3",
|
||||||
"react-virtualized": "^9.22.5",
|
"react-virtualized": "^9.22.5",
|
||||||
|
"react-virtuoso": "^4.7.11",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-actions": "^3.0.0",
|
"redux-actions": "^3.0.0",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { groupBy } from "lodash";
|
import { groupBy } from "lodash";
|
||||||
import fakeData from "./testData/board300.json";
|
import fakeData from "./testData/board1200.json";
|
||||||
|
|
||||||
const sortByParentId = (arr) => {
|
const sortByParentId = (arr) => {
|
||||||
// return arr.reduce((accumulator, currentValue) => {
|
// return arr.reduce((accumulator, currentValue) => {
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import Container from "../dnd/Container";
|
|
||||||
import Draggable from "../dnd/Draggable";
|
// import Container from "../dnd/Container";
|
||||||
|
// import Draggable from "../dnd/Draggable";
|
||||||
|
|
||||||
|
import { DragDropContext } from "../dnd/lib";
|
||||||
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import pick from "lodash/pick";
|
import pick from "lodash/pick";
|
||||||
import isEqual from "lodash/isEqual";
|
import isEqual from "lodash/isEqual";
|
||||||
@@ -151,6 +155,10 @@ const BoardContainer = ({
|
|||||||
[dispatch, handleLaneDragEnd]
|
[dispatch, handleLaneDragEnd]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onDragEnd = (...params) => {
|
||||||
|
console.dir(params);
|
||||||
|
};
|
||||||
|
|
||||||
const getCardDetails = useCallback(
|
const getCardDetails = useCallback(
|
||||||
(laneId, cardIndex) => {
|
(laneId, cardIndex) => {
|
||||||
return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
||||||
@@ -239,22 +247,25 @@ const BoardContainer = ({
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let cardIndex = 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.BoardWrapper style={style} orientation={orientation} draggable={false}>
|
<components.BoardWrapper style={style} orientation={orientation} draggable={false}>
|
||||||
<PopoverWrapper>
|
<PopoverWrapper>
|
||||||
<Container
|
<DragDropContext
|
||||||
orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
onDragEnd={onDragEnd}
|
||||||
onDragStart={onDragStart}
|
// orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
||||||
dragClass={laneDragClass}
|
// onDragStart={onDragStart}
|
||||||
dropClass={laneDropClass}
|
// dragClass={laneDragClass}
|
||||||
onDrop={onLaneDrop}
|
// dropClass={laneDropClass}
|
||||||
lockAxis={orientation === "vertical" ? "y" : "x"}
|
// onDrop={onLaneDrop}
|
||||||
getChildPayload={(index) => getLaneDetails(index)}
|
// lockAxis={orientation === "vertical" ? "y" : "x"}
|
||||||
groupName={groupName}
|
// getChildPayload={(index) => getLaneDetails(index)}
|
||||||
|
// groupName={groupName}
|
||||||
>
|
>
|
||||||
{currentReducerData.lanes.map((lane, index) => {
|
{currentReducerData.lanes.map((lane, index) => {
|
||||||
const { id, droppable, ...laneOtherProps } = lane;
|
const { id, droppable, ...laneOtherProps } = lane;
|
||||||
const laneToRender = (
|
return (
|
||||||
<Lane
|
<Lane
|
||||||
key={id}
|
key={id}
|
||||||
boardId={groupName}
|
boardId={groupName}
|
||||||
@@ -269,21 +280,21 @@ const BoardContainer = ({
|
|||||||
editable={editable && !lane.disallowAddingCard}
|
editable={editable && !lane.disallowAddingCard}
|
||||||
{...laneOtherProps}
|
{...laneOtherProps}
|
||||||
{...passThroughProps}
|
{...passThroughProps}
|
||||||
|
cards={lane.cards.map((card) => ({ ...card, idx: cardIndex++ }))}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return draggable || laneDraggable ? <Draggable key={lane.id}>{laneToRender}</Draggable> : laneToRender;
|
|
||||||
})}
|
})}
|
||||||
</Container>
|
</DragDropContext>
|
||||||
</PopoverWrapper>
|
</PopoverWrapper>
|
||||||
{canAddLanes && (
|
{/*{canAddLanes && (*/}
|
||||||
<Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>
|
{/* <Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>*/}
|
||||||
{editable && !addLaneMode ? (
|
{/* {editable && !addLaneMode ? (*/}
|
||||||
<components.NewLaneSection onClick={showEditableLane} />
|
{/* <components.NewLaneSection onClick={showEditableLane} />*/}
|
||||||
) : (
|
{/* ) : (*/}
|
||||||
addLaneMode && <components.NewLaneForm onCancel={hideEditableLane} onAdd={addNewLane} />
|
{/* addLaneMode && <components.NewLaneForm onCancel={hideEditableLane} onAdd={addNewLane} />*/}
|
||||||
)}
|
{/* )}*/}
|
||||||
</Container>
|
{/* </Container>*/}
|
||||||
)}
|
{/*)}*/}
|
||||||
</components.BoardWrapper>
|
</components.BoardWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { bindActionCreators } from "redux";
|
import { bindActionCreators } from "redux";
|
||||||
@@ -7,10 +7,8 @@ import isEqual from "lodash/isEqual";
|
|||||||
import cloneDeep from "lodash/cloneDeep";
|
import cloneDeep from "lodash/cloneDeep";
|
||||||
import { v1 } from "uuid";
|
import { v1 } from "uuid";
|
||||||
|
|
||||||
import Container from "../dnd/Container.jsx";
|
|
||||||
import Draggable from "../dnd/Draggable.jsx";
|
|
||||||
|
|
||||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||||
|
import { Droppable, Draggable } from "../dnd/lib/index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lane is a React component that represents a lane in a Trello-like board.
|
* Lane is a React component that represents a lane in a Trello-like board.
|
||||||
@@ -112,6 +110,15 @@ function Lane({
|
|||||||
|
|
||||||
const laneRef = useRef(null);
|
const laneRef = useRef(null);
|
||||||
|
|
||||||
|
const flexStyle = useMemo(() => {
|
||||||
|
return orientation === "vertical"
|
||||||
|
? {
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap"
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
}, [orientation]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isEqual(cards, currentPageFinal)) {
|
if (!isEqual(cards, currentPageFinal)) {
|
||||||
setCurrentPageFinal(currentPage);
|
setCurrentPageFinal(currentPage);
|
||||||
@@ -171,6 +178,7 @@ function Lane({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCardClick = (e, card) => {
|
const handleCardClick = (e, card) => {
|
||||||
|
console.log("hit");
|
||||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
@@ -265,32 +273,58 @@ function Lane({
|
|||||||
{...card}
|
{...card}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return cardDraggable && (!card.hasOwnProperty("draggable") || card.draggable) ? (
|
return cardDraggable && (!card.hasOwnProperty("draggable") || card.draggable) ? (
|
||||||
<Draggable key={card.id}>{cardToRender}</Draggable>
|
<Draggable key={card.id} draggableId={card.id} index={card.idx}>
|
||||||
|
{(provided, snapshot) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
style={{
|
||||||
|
...provided.draggableProps.style
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{cardToRender}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Draggable>
|
||||||
) : (
|
) : (
|
||||||
<span key={card.id}>{cardToRender}</span>
|
<span key={card.id}>{cardToRender}</span>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.ScrollableLane ref={laneRef} isDraggingOver={isDraggingOver}>
|
<Droppable
|
||||||
<Container
|
direction={orientation === "horizontal" ? "vertical" : "grid"}
|
||||||
orientation={orientation === "horizontal" ? "vertical" : "horizontal"}
|
droppableId={`lane-${index}`}
|
||||||
groupName={groupName}
|
// dragClass={cardDragClass}
|
||||||
dragClass={cardDragClass}
|
// dropClass={cardDropClass}
|
||||||
dropClass={cardDropClass}
|
// onDragStart={onDragStart}
|
||||||
onDragStart={onDragStart}
|
// onDrop={(e) => onDragEnd(id, e)}
|
||||||
onDrop={(e) => onDragEnd(id, e)}
|
// onDragEnter={() => setIsDraggingOver(true)}
|
||||||
onDragEnter={() => setIsDraggingOver(true)}
|
// onDragLeave={() => setIsDraggingOver(false)}
|
||||||
onDragLeave={() => setIsDraggingOver(false)}
|
// shouldAcceptDrop={shouldAcceptDrop}
|
||||||
shouldAcceptDrop={shouldAcceptDrop}
|
// getChildPayload={(index) => getCardDetails(id, index)}
|
||||||
getChildPayload={(index) => getCardDetails(id, index)}
|
>
|
||||||
>
|
{(provided, snapshot) => (
|
||||||
{cardList}
|
<div
|
||||||
</Container>
|
{...provided.droppableProps}
|
||||||
{editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />}
|
ref={provided.innerRef}
|
||||||
{addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />}
|
style={{
|
||||||
</components.ScrollableLane>
|
...provided.droppableProps.style,
|
||||||
|
...flexStyle
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{cardList}
|
||||||
|
{editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />}
|
||||||
|
{addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />}
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import container, { dropHandlers } from "../smooth-dnd";
|
|
||||||
|
|
||||||
container.dropHandler = dropHandlers.reactDropHandler().handler;
|
|
||||||
container.wrapChild = (p) => p; // don't wrap children they will already be wrapped
|
|
||||||
|
|
||||||
class Container extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.getContainerOptions = this.getContainerOptions.bind(this);
|
|
||||||
this.setRef = this.setRef.bind(this);
|
|
||||||
this.prevContainer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.prevContainer = this.containerDiv;
|
|
||||||
this.container = container(this.containerDiv, this.getContainerOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.container.dispose();
|
|
||||||
this.container = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
if (this.containerDiv) {
|
|
||||||
if (this.prevContainer && this.prevContainer !== this.containerDiv) {
|
|
||||||
this.container.dispose();
|
|
||||||
this.container = container(this.containerDiv, this.getContainerOptions());
|
|
||||||
this.prevContainer = this.containerDiv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.props.render) {
|
|
||||||
return this.props.render(this.setRef);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div style={this.props.style} ref={this.setRef}>
|
|
||||||
{this.props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setRef(element) {
|
|
||||||
this.containerDiv = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
getContainerOptions() {
|
|
||||||
const functionProps = {};
|
|
||||||
const propKeys = [
|
|
||||||
"onDragStart",
|
|
||||||
"onDragEnd",
|
|
||||||
"onDrop",
|
|
||||||
"getChildPayload",
|
|
||||||
"shouldAnimateDrop",
|
|
||||||
"shouldAcceptDrop",
|
|
||||||
"onDragEnter",
|
|
||||||
"onDragLeave",
|
|
||||||
"render",
|
|
||||||
"onDropReady",
|
|
||||||
"getGhostParent"
|
|
||||||
];
|
|
||||||
|
|
||||||
propKeys.forEach((key) => {
|
|
||||||
if (this.props[key]) {
|
|
||||||
functionProps[key] = (...p) => this.props[key](...p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { ...this.props, ...functionProps };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Container.propTypes = {
|
|
||||||
behaviour: PropTypes.oneOf(["move", "copy", "drag-zone"]),
|
|
||||||
groupName: PropTypes.string,
|
|
||||||
orientation: PropTypes.oneOf(["horizontal", "vertical"]),
|
|
||||||
style: PropTypes.object,
|
|
||||||
dragHandleSelector: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
|
||||||
nonDragAreaSelector: PropTypes.string,
|
|
||||||
dragBeginDelay: PropTypes.number,
|
|
||||||
animationDuration: PropTypes.number,
|
|
||||||
autoScrollEnabled: PropTypes.string,
|
|
||||||
lockAxis: PropTypes.string,
|
|
||||||
dragClass: PropTypes.string,
|
|
||||||
dropClass: PropTypes.string,
|
|
||||||
onDragStart: PropTypes.func,
|
|
||||||
onDragEnd: PropTypes.func,
|
|
||||||
onDrop: PropTypes.func,
|
|
||||||
getChildPayload: PropTypes.func,
|
|
||||||
shouldAnimateDrop: PropTypes.func,
|
|
||||||
shouldAcceptDrop: PropTypes.func,
|
|
||||||
onDragEnter: PropTypes.func,
|
|
||||||
onDragLeave: PropTypes.func,
|
|
||||||
render: PropTypes.func,
|
|
||||||
getGhostParent: PropTypes.func,
|
|
||||||
removeOnDropOut: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
Container.defaultProps = {
|
|
||||||
behaviour: "move",
|
|
||||||
orientation: "vertical",
|
|
||||||
className: "reactTrelloBoard"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Container;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { constants } from "../smooth-dnd";
|
|
||||||
const { wrapperClass } = constants;
|
|
||||||
|
|
||||||
class Draggable extends Component {
|
|
||||||
render() {
|
|
||||||
const { render, className, children, ...restProps } = this.props;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (render) {
|
|
||||||
return React.cloneElement(render(), { className: wrapperClass });
|
|
||||||
}
|
|
||||||
|
|
||||||
const clsName = className ? `${className} ` : "";
|
|
||||||
return (
|
|
||||||
<div {...restProps} className={`${clsName}${wrapperClass}`}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error rendering Draggable component:", error);
|
|
||||||
return null; // Return null if an error occurs to prevent crashing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Draggable.propTypes = {
|
|
||||||
render: PropTypes.func,
|
|
||||||
className: PropTypes.string,
|
|
||||||
children: PropTypes.node
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Draggable;
|
|
||||||
@@ -3,7 +3,7 @@ import { origin } from "../position";
|
|||||||
import getMaxScroll from "../get-max-scroll";
|
import getMaxScroll from "../get-max-scroll";
|
||||||
import getSubject from "./util/get-subject";
|
import getSubject from "./util/get-subject";
|
||||||
|
|
||||||
export default ({ descriptor, isEnabled, isCombineEnabled, isFixedOnPage, direction, client, page, closest }) => {
|
const getDroppable = ({ descriptor, isEnabled, isCombineEnabled, isFixedOnPage, direction, client, page, closest }) => {
|
||||||
const frame = (() => {
|
const frame = (() => {
|
||||||
if (!closest) {
|
if (!closest) {
|
||||||
return null;
|
return null;
|
||||||
@@ -54,3 +54,5 @@ export default ({ descriptor, isEnabled, isCombineEnabled, isFixedOnPage, direct
|
|||||||
};
|
};
|
||||||
return dimension;
|
return dimension;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default getDroppable;
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ const ConnectedDraggable = connect(
|
|||||||
// Using our own context for the store to avoid clashing with consumers
|
// Using our own context for the store to avoid clashing with consumers
|
||||||
context: StoreContext,
|
context: StoreContext,
|
||||||
// Default value, but being really clear
|
// Default value, but being really clear
|
||||||
pure: true,
|
|
||||||
// When pure, compares the result of mapStateToProps to its previous value.
|
// When pure, compares the result of mapStateToProps to its previous value.
|
||||||
// Default value: shallowEqual
|
// Default value: shallowEqual
|
||||||
// Switching to a strictEqual as we return a memoized object on changes
|
// Switching to a strictEqual as we return a memoized object on changes
|
||||||
|
|||||||
@@ -192,13 +192,13 @@ const ConnectedDroppable = connect(
|
|||||||
{
|
{
|
||||||
// Ensuring our context does not clash with consumers
|
// Ensuring our context does not clash with consumers
|
||||||
context: StoreContext,
|
context: StoreContext,
|
||||||
// pure: true is default value, but being really clear
|
|
||||||
pure: true,
|
|
||||||
// When pure, compares the result of mapStateToProps to its previous value.
|
// When pure, compares the result of mapStateToProps to its previous value.
|
||||||
// Default value: shallowEqual
|
// Default value: shallowEqual
|
||||||
// Switching to a strictEqual as we return a memoized object on changes
|
// Switching to a strictEqual as we return a memoized object on changes
|
||||||
areStatePropsEqual: isStrictEqual
|
areStatePropsEqual: isStrictEqual
|
||||||
}
|
}
|
||||||
)(Droppable);
|
)(Droppable);
|
||||||
|
|
||||||
ConnectedDroppable.defaultProps = defaultProps;
|
ConnectedDroppable.defaultProps = defaultProps;
|
||||||
export default ConnectedDroppable;
|
export default ConnectedDroppable;
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Draggable from "./dnd/Draggable.jsx";
|
|
||||||
import Container from "./dnd/Container.jsx";
|
|
||||||
import BoardContainer from "./controllers/BoardContainer.jsx";
|
import BoardContainer from "./controllers/BoardContainer.jsx";
|
||||||
import Board from "./controllers/Board.jsx";
|
import Board from "./controllers/Board.jsx";
|
||||||
import Lane from "./controllers/Lane.jsx";
|
import Lane from "./controllers/Lane.jsx";
|
||||||
@@ -12,7 +10,7 @@ import widgets from "./widgets/index";
|
|||||||
import { StyleSheetManager } from "styled-components";
|
import { StyleSheetManager } from "styled-components";
|
||||||
import isPropValid from "@emotion/is-prop-valid";
|
import isPropValid from "@emotion/is-prop-valid";
|
||||||
|
|
||||||
export { Draggable, Container, BoardContainer, Lane, widgets };
|
export { BoardContainer, Lane, widgets };
|
||||||
|
|
||||||
export { DefaultComponents as components };
|
export { DefaultComponents as components };
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import container from './src/container';
|
|
||||||
import * as constants from './src/constants';
|
|
||||||
import * as dropHandlers from './src/dropHandlers';
|
|
||||||
export default container;
|
|
||||||
export {
|
|
||||||
constants,
|
|
||||||
dropHandlers,
|
|
||||||
};
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
export const containerInstance = 'smooth-dnd-container-instance';
|
|
||||||
export const containersInDraggable = 'smooth-dnd-containers-in-draggable';
|
|
||||||
|
|
||||||
export const defaultGroupName = '@@smooth-dnd-default-group@@';
|
|
||||||
export const wrapperClass = 'smooth-dnd-draggable-wrapper';
|
|
||||||
export const defaultGrabHandleClass = 'smooth-dnd-default-grap-handle';
|
|
||||||
export const animationClass = 'animated';
|
|
||||||
export const translationValue = '__smooth_dnd_draggable_translation_value';
|
|
||||||
export const visibilityValue = '__smooth_dnd_draggable_visibility_value';
|
|
||||||
export const ghostClass = 'smooth-dnd-ghost';
|
|
||||||
|
|
||||||
export const containerClass = 'smooth-dnd-container';
|
|
||||||
|
|
||||||
export const extraSizeForInsertion = 'smooth-dnd-extra-size-for-insertion';
|
|
||||||
export const stretcherElementClass = 'smooth-dnd-stretcher-element';
|
|
||||||
export const stretcherElementInstance = 'smooth-dnd-stretcher-instance';
|
|
||||||
|
|
||||||
export const isDraggableDetached = 'smoth-dnd-is-draggable-detached';
|
|
||||||
|
|
||||||
export const disbaleTouchActions = 'smooth-dnd-disable-touch-action';
|
|
||||||
export const noUserSelectClass = 'smooth-dnd-no-user-select';
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
.smooth-dnd-container *{
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-disable-touch-action{
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-container{
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-container.vertical{
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-container.horizontal{
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-container.horizontal .smooth-dnd-draggable-wrapper{
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.smooth-dnd-draggable-wrapper {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-draggable-wrapper.animated{
|
|
||||||
transition: transform ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-ghost {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-ghost *{
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-ghost.animated{
|
|
||||||
transition: all ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .smooth-dnd-no-user-select{
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-stretcher-element{
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-stretcher-element.vertical{
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-dnd-stretcher-element.horizontal{
|
|
||||||
height: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
} */
|
|
||||||
@@ -1,777 +0,0 @@
|
|||||||
import Mediator from './mediator';
|
|
||||||
import layoutManager from './layoutManager';
|
|
||||||
import { hasClass, addClass, removeClass, getParent } from './utils';
|
|
||||||
import { domDropHandler } from './dropHandlers';
|
|
||||||
import {
|
|
||||||
defaultGroupName,
|
|
||||||
wrapperClass,
|
|
||||||
animationClass,
|
|
||||||
stretcherElementClass,
|
|
||||||
stretcherElementInstance,
|
|
||||||
translationValue,
|
|
||||||
containerClass,
|
|
||||||
containerInstance,
|
|
||||||
containersInDraggable
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
const defaultOptions = {
|
|
||||||
groupName: null,
|
|
||||||
behaviour: 'move', // move | copy
|
|
||||||
orientation: 'vertical', // vertical | horizontal
|
|
||||||
getChildPayload: null,
|
|
||||||
animationDuration: 250,
|
|
||||||
autoScrollEnabled: true,
|
|
||||||
shouldAcceptDrop: null,
|
|
||||||
shouldAnimateDrop: null
|
|
||||||
};
|
|
||||||
|
|
||||||
function setAnimation(element, add, animationDuration) {
|
|
||||||
if (add) {
|
|
||||||
addClass(element, animationClass);
|
|
||||||
element.style.transitionDuration = animationDuration + 'ms';
|
|
||||||
} else {
|
|
||||||
removeClass(element, animationClass);
|
|
||||||
element.style.removeProperty('transition-duration');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContainer(element) {
|
|
||||||
return element ? element[containerInstance] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initOptions(props = defaultOptions) {
|
|
||||||
return Object.assign({}, defaultOptions, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDragRelevant({ element, options }) {
|
|
||||||
return function(sourceContainer, payload) {
|
|
||||||
if (options.shouldAcceptDrop) {
|
|
||||||
return options.shouldAcceptDrop(sourceContainer.getOptions(), payload);
|
|
||||||
}
|
|
||||||
const sourceOptions = sourceContainer.getOptions();
|
|
||||||
if (options.behaviour === 'copy') return false;
|
|
||||||
|
|
||||||
const parentWrapper = getParent(element, '.' + wrapperClass);
|
|
||||||
if (parentWrapper === sourceContainer.element) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourceContainer.element === element) return true;
|
|
||||||
if (sourceOptions.groupName && sourceOptions.groupName === options.groupName) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapChild(child) {
|
|
||||||
if (SmoothDnD.wrapChild) {
|
|
||||||
return SmoothDnD.wrapChild(child);
|
|
||||||
}
|
|
||||||
const div = global.document.createElement('div');
|
|
||||||
div.className = `${wrapperClass}`;
|
|
||||||
child.parentElement.insertBefore(div, child);
|
|
||||||
div.appendChild(child);
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapChildren(element) {
|
|
||||||
const draggables = [];
|
|
||||||
Array.prototype.map.call(element.children, child => {
|
|
||||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
let wrapper = child;
|
|
||||||
if (!hasClass(child, wrapperClass)) {
|
|
||||||
wrapper = wrapChild(child);
|
|
||||||
}
|
|
||||||
wrapper[containersInDraggable] = [];
|
|
||||||
wrapper[translationValue] = 0;
|
|
||||||
draggables.push(wrapper);
|
|
||||||
} else {
|
|
||||||
if (typeof element.removeChild === "function") {
|
|
||||||
element.removeChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return draggables;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unwrapChildren(element) {
|
|
||||||
Array.prototype.map.call(element.children, child => {
|
|
||||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
let wrapper = child;
|
|
||||||
if (hasClass(child, wrapperClass)) {
|
|
||||||
element.insertBefore(wrapper, wrapChild.firstElementChild);
|
|
||||||
element.removeChild(wrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function findDraggebleAtPos({ layout }) {
|
|
||||||
const find = (draggables, pos, startIndex, endIndex, withRespectToMiddlePoints = false) => {
|
|
||||||
if (endIndex < startIndex) {
|
|
||||||
return startIndex;
|
|
||||||
}
|
|
||||||
// binary serach draggable
|
|
||||||
if (startIndex === endIndex) {
|
|
||||||
let { begin, end } = layout.getBeginEnd(draggables[startIndex]);
|
|
||||||
// mouse pos is inside draggable
|
|
||||||
// now decide which index to return
|
|
||||||
if (pos > begin && pos <= end) {
|
|
||||||
if (withRespectToMiddlePoints) {
|
|
||||||
return pos < (end + begin) / 2 ? startIndex : startIndex + 1;
|
|
||||||
} else {
|
|
||||||
return startIndex;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const middleIndex = Math.floor((endIndex + startIndex) / 2);
|
|
||||||
const { begin, end } = layout.getBeginEnd(draggables[middleIndex]);
|
|
||||||
if (pos < begin) {
|
|
||||||
return find(draggables, pos, startIndex, middleIndex - 1, withRespectToMiddlePoints);
|
|
||||||
} else if (pos > end) {
|
|
||||||
return find(draggables, pos, middleIndex + 1, endIndex, withRespectToMiddlePoints);
|
|
||||||
} else {
|
|
||||||
if (withRespectToMiddlePoints) {
|
|
||||||
return pos < (end + begin) / 2 ? middleIndex : middleIndex + 1;
|
|
||||||
} else {
|
|
||||||
return middleIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (draggables, pos, withRespectToMiddlePoints = false) => {
|
|
||||||
return find(draggables, pos, 0, draggables.length - 1, withRespectToMiddlePoints);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetDraggables({ element, draggables, layout, options }) {
|
|
||||||
return function() {
|
|
||||||
draggables.forEach(p => {
|
|
||||||
setAnimation(p, false);
|
|
||||||
layout.setTranslation(p, 0);
|
|
||||||
layout.setVisibility(p, true);
|
|
||||||
p[containersInDraggable] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (element[stretcherElementInstance]) {
|
|
||||||
element[stretcherElementInstance].parentNode.removeChild(element[stretcherElementInstance]);
|
|
||||||
element[stretcherElementInstance] = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTargetContainer(draggableInfo, element, set = true) {
|
|
||||||
if (element && set) {
|
|
||||||
draggableInfo.targetElement = element;
|
|
||||||
} else {
|
|
||||||
if (draggableInfo.targetElement === element) {
|
|
||||||
draggableInfo.targetElement = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDrop({ element, draggables, layout, options }) {
|
|
||||||
const draggablesReset = resetDraggables({ element, draggables, layout, options });
|
|
||||||
const dropHandler = (SmoothDnD.dropHandler || domDropHandler)({ element, draggables, layout, options });
|
|
||||||
return function(draggableInfo, { addedIndex, removedIndex }) {
|
|
||||||
draggablesReset();
|
|
||||||
// if drop zone is valid => complete drag else do nothing everything will be reverted by draggablesReset()
|
|
||||||
if (draggableInfo.targetElement || options.removeOnDropOut) {
|
|
||||||
let actualAddIndex =
|
|
||||||
addedIndex !== null ? (removedIndex !== null && removedIndex < addedIndex ? addedIndex - 1 : addedIndex) : null;
|
|
||||||
const dropHandlerParams = {
|
|
||||||
removedIndex,
|
|
||||||
addedIndex: actualAddIndex,
|
|
||||||
payload: draggableInfo.payload,
|
|
||||||
droppedElement: draggableInfo.element.firstElementChild
|
|
||||||
};
|
|
||||||
dropHandler(dropHandlerParams, options.onDrop);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContainerProps(element, initialOptions) {
|
|
||||||
const options = initOptions(initialOptions);
|
|
||||||
const draggables = wrapChildren(element, options.orientation, options.animationDuration);
|
|
||||||
// set flex classes before layout is inited for scroll listener
|
|
||||||
addClass(element, `${containerClass} ${options.orientation}`);
|
|
||||||
const layout = layoutManager(element, options.orientation, options.animationDuration);
|
|
||||||
return {
|
|
||||||
element,
|
|
||||||
draggables,
|
|
||||||
options,
|
|
||||||
layout
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRelaventParentContainer(container, relevantContainers) {
|
|
||||||
let current = container.element;
|
|
||||||
while (current) {
|
|
||||||
const containerOfParentElement = getContainer(current.parentElement);
|
|
||||||
if (containerOfParentElement && relevantContainers.indexOf(containerOfParentElement) > -1) {
|
|
||||||
return {
|
|
||||||
container: containerOfParentElement,
|
|
||||||
draggable: current
|
|
||||||
};
|
|
||||||
}
|
|
||||||
current = current.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerToParentContainer(container, relevantContainers) {
|
|
||||||
const parentInfo = getRelaventParentContainer(container, relevantContainers);
|
|
||||||
if (parentInfo) {
|
|
||||||
parentInfo.container.getChildContainers().push(container);
|
|
||||||
container.setParentContainer(parentInfo.container);
|
|
||||||
//current should be draggable
|
|
||||||
parentInfo.draggable[containersInDraggable].push(container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRemovedItem({ draggables, element, options }) {
|
|
||||||
let prevRemovedIndex = null;
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
let removedIndex = prevRemovedIndex;
|
|
||||||
if (prevRemovedIndex == null && draggableInfo.container.element === element && options.behaviour !== 'copy') {
|
|
||||||
removedIndex = prevRemovedIndex = draggableInfo.elementIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { removedIndex };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRemovedItemVisibilty({ draggables, layout }) {
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
if (dragResult.removedIndex !== null) {
|
|
||||||
layout.setVisibility(draggables[dragResult.removedIndex], false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPosition({ element, layout }) {
|
|
||||||
return ({ draggableInfo }) => {
|
|
||||||
return {
|
|
||||||
pos: !getContainer(element).isPosInChildContainer() ? layout.getPosition(draggableInfo.position) : null
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function notifyParentOnPositionCapture({ element }) {
|
|
||||||
let isCaptured = false;
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
if (getContainer(element).getParentContainer() && isCaptured !== (dragResult.pos !== null)) {
|
|
||||||
isCaptured = dragResult.pos !== null;
|
|
||||||
getContainer(element)
|
|
||||||
.getParentContainer()
|
|
||||||
.onChildPositionCaptured(isCaptured);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getElementSize({ layout }) {
|
|
||||||
let elementSize = null;
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
if (dragResult.pos === null) {
|
|
||||||
return (elementSize = null);
|
|
||||||
} else {
|
|
||||||
elementSize = elementSize || layout.getSize(draggableInfo.element);
|
|
||||||
}
|
|
||||||
return { elementSize };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTargetContainer({ element }) {
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
setTargetContainer(draggableInfo, element, !!dragResult.pos);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDragInsertionIndex({ draggables, layout }) {
|
|
||||||
const findDraggable = findDraggebleAtPos({ layout });
|
|
||||||
return ({ dragResult: { shadowBeginEnd, pos } }) => {
|
|
||||||
if (!shadowBeginEnd) {
|
|
||||||
const index = findDraggable(draggables, pos, true);
|
|
||||||
return index !== null ? index : draggables.length;
|
|
||||||
} else {
|
|
||||||
if (shadowBeginEnd.begin + shadowBeginEnd.beginAdjustment <= pos && shadowBeginEnd.end >= pos) {
|
|
||||||
// position inside ghost
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos < shadowBeginEnd.begin + shadowBeginEnd.beginAdjustment) {
|
|
||||||
return findDraggable(draggables, pos);
|
|
||||||
} else if (pos > shadowBeginEnd.end) {
|
|
||||||
return findDraggable(draggables, pos) + 1;
|
|
||||||
} else {
|
|
||||||
return draggables.length;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDragInsertionIndexForDropZone({ draggables, layout }) {
|
|
||||||
return ({ dragResult: { pos } }) => {
|
|
||||||
return pos !== null ? { addedIndex: 0 } : { addedIndex: null };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShadowBeginEndForDropZone({ draggables, layout }) {
|
|
||||||
let prevAddedIndex = null;
|
|
||||||
return ({ dragResult: { addedIndex } }) => {
|
|
||||||
if (addedIndex !== prevAddedIndex) {
|
|
||||||
prevAddedIndex = addedIndex;
|
|
||||||
const { begin, end } = layout.getBeginEndOfContainer();
|
|
||||||
return {
|
|
||||||
shadowBeginEnd: {
|
|
||||||
rect: layout.getTopLeftOfElementBegin(begin, end)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidateShadowBeginEndIfNeeded(params) {
|
|
||||||
const shadowBoundsGetter = getShadowBeginEnd(params);
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
if (draggableInfo.invalidateShadow) {
|
|
||||||
return shadowBoundsGetter({ draggableInfo, dragResult });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNextAddedIndex(params) {
|
|
||||||
const getIndexForPos = getDragInsertionIndex(params);
|
|
||||||
return ({ dragResult }) => {
|
|
||||||
let index = null;
|
|
||||||
if (dragResult.pos !== null) {
|
|
||||||
index = getIndexForPos({ dragResult });
|
|
||||||
if (index === null) {
|
|
||||||
index = dragResult.addedIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
addedIndex: index
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetShadowAdjustment() {
|
|
||||||
let lastAddedIndex = null;
|
|
||||||
return ({ dragResult: { addedIndex, shadowBeginEnd } }) => {
|
|
||||||
if (addedIndex !== lastAddedIndex && lastAddedIndex !== null && shadowBeginEnd) {
|
|
||||||
shadowBeginEnd.beginAdjustment = 0;
|
|
||||||
}
|
|
||||||
lastAddedIndex = addedIndex;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleInsertionSizeChange({ element, draggables, layout, options }) {
|
|
||||||
let strectherElement = null;
|
|
||||||
return function({ dragResult: { addedIndex, removedIndex, elementSize } }) {
|
|
||||||
if (removedIndex === null) {
|
|
||||||
if (addedIndex !== null) {
|
|
||||||
if (!strectherElement) {
|
|
||||||
const containerBeginEnd = layout.getBeginEndOfContainer();
|
|
||||||
containerBeginEnd.end = containerBeginEnd.begin + layout.getSize(element);
|
|
||||||
const hasScrollBar = layout.getScrollSize(element) > layout.getSize(element);
|
|
||||||
const containerEnd = hasScrollBar
|
|
||||||
? containerBeginEnd.begin + layout.getScrollSize(element) - layout.getScrollValue(element)
|
|
||||||
: containerBeginEnd.end;
|
|
||||||
const lastDraggableEnd =
|
|
||||||
draggables.length > 0
|
|
||||||
? layout.getBeginEnd(draggables[draggables.length - 1]).end -
|
|
||||||
draggables[draggables.length - 1][translationValue]
|
|
||||||
: containerBeginEnd.begin;
|
|
||||||
if (lastDraggableEnd + elementSize > containerEnd) {
|
|
||||||
strectherElement = global.document.createElement('div');
|
|
||||||
strectherElement.className = stretcherElementClass + ' ' + options.orientation;
|
|
||||||
const stretcherSize = elementSize + lastDraggableEnd - containerEnd;
|
|
||||||
layout.setSize(strectherElement.style, `${stretcherSize}px`);
|
|
||||||
element.appendChild(strectherElement);
|
|
||||||
element[stretcherElementInstance] = strectherElement;
|
|
||||||
return {
|
|
||||||
containerBoxChanged: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (strectherElement) {
|
|
||||||
layout.setTranslation(strectherElement, 0);
|
|
||||||
let toRemove = strectherElement;
|
|
||||||
strectherElement = null;
|
|
||||||
element.removeChild(toRemove);
|
|
||||||
element[stretcherElementInstance] = null;
|
|
||||||
return {
|
|
||||||
containerBoxChanged: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateTranslations({ element, draggables, layout }) {
|
|
||||||
let prevAddedIndex = null;
|
|
||||||
let prevRemovedIndex = null;
|
|
||||||
return function({ dragResult: { addedIndex, removedIndex, elementSize } }) {
|
|
||||||
if (addedIndex !== prevAddedIndex || removedIndex !== prevRemovedIndex) {
|
|
||||||
for (let index = 0; index < draggables.length; index++) {
|
|
||||||
if (index !== removedIndex) {
|
|
||||||
const draggable = draggables[index];
|
|
||||||
let translate = 0;
|
|
||||||
if (removedIndex !== null && removedIndex < index) {
|
|
||||||
translate -= layout.getSize(draggables[removedIndex]);
|
|
||||||
}
|
|
||||||
if (addedIndex !== null && addedIndex <= index) {
|
|
||||||
translate += elementSize;
|
|
||||||
}
|
|
||||||
layout.setTranslation(draggable, translate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prevAddedIndex = addedIndex;
|
|
||||||
prevRemovedIndex = removedIndex;
|
|
||||||
|
|
||||||
return { addedIndex, removedIndex };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShadowBeginEnd({ draggables, layout }) {
|
|
||||||
let prevAddedIndex = null;
|
|
||||||
return ({ draggableInfo, dragResult }) => {
|
|
||||||
const { addedIndex, removedIndex, elementSize, pos, shadowBeginEnd } = dragResult;
|
|
||||||
if (pos !== null) {
|
|
||||||
if (addedIndex !== null && (draggableInfo.invalidateShadow || addedIndex !== prevAddedIndex)) {
|
|
||||||
if (prevAddedIndex) prevAddedIndex = addedIndex;
|
|
||||||
let beforeIndex = addedIndex - 1;
|
|
||||||
let begin = 0;
|
|
||||||
let afterBounds = null;
|
|
||||||
let beforeBounds = null;
|
|
||||||
if (beforeIndex === removedIndex) {
|
|
||||||
beforeIndex--;
|
|
||||||
}
|
|
||||||
if (beforeIndex > -1) {
|
|
||||||
const beforeSize = layout.getSize(draggables[beforeIndex]);
|
|
||||||
beforeBounds = layout.getBeginEnd(draggables[beforeIndex]);
|
|
||||||
if (elementSize < beforeSize) {
|
|
||||||
const threshold = (beforeSize - elementSize) / 2;
|
|
||||||
begin = beforeBounds.end - threshold;
|
|
||||||
} else {
|
|
||||||
begin = beforeBounds.end;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
beforeBounds = { end: layout.getBeginEndOfContainer().begin };
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = 10000;
|
|
||||||
let afterIndex = addedIndex;
|
|
||||||
if (afterIndex === removedIndex) {
|
|
||||||
afterIndex++;
|
|
||||||
}
|
|
||||||
if (afterIndex < draggables.length) {
|
|
||||||
const afterSize = layout.getSize(draggables[afterIndex]);
|
|
||||||
afterBounds = layout.getBeginEnd(draggables[afterIndex]);
|
|
||||||
|
|
||||||
if (elementSize < afterSize) {
|
|
||||||
const threshold = (afterSize - elementSize) / 2;
|
|
||||||
end = afterBounds.begin + threshold;
|
|
||||||
} else {
|
|
||||||
end = afterBounds.begin;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
afterBounds = { begin: layout.getContainerRectangles().end };
|
|
||||||
}
|
|
||||||
|
|
||||||
const shadowRectTopLeft =
|
|
||||||
beforeBounds && afterBounds ? layout.getTopLeftOfElementBegin(beforeBounds.end, afterBounds.begin) : null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
shadowBeginEnd: {
|
|
||||||
begin,
|
|
||||||
end,
|
|
||||||
rect: shadowRectTopLeft,
|
|
||||||
beginAdjustment: shadowBeginEnd ? shadowBeginEnd.beginAdjustment : 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
prevAddedIndex = null;
|
|
||||||
return {
|
|
||||||
shadowBeginEnd: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFirstInsertShadowAdjustment() {
|
|
||||||
let lastAddedIndex = null;
|
|
||||||
return ({ dragResult: { pos, addedIndex, shadowBeginEnd }, draggableInfo: { invalidateShadow } }) => {
|
|
||||||
if (pos !== null) {
|
|
||||||
if (addedIndex != null && lastAddedIndex === null) {
|
|
||||||
if (pos < shadowBeginEnd.begin) {
|
|
||||||
const beginAdjustment = pos - shadowBeginEnd.begin - 5;
|
|
||||||
shadowBeginEnd.beginAdjustment = beginAdjustment;
|
|
||||||
}
|
|
||||||
lastAddedIndex = addedIndex;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastAddedIndex = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireDragEnterLeaveEvents({ options }) {
|
|
||||||
let wasDragIn = false;
|
|
||||||
return ({ dragResult: { pos } }) => {
|
|
||||||
const isDragIn = !!pos;
|
|
||||||
if (isDragIn !== wasDragIn) {
|
|
||||||
wasDragIn = isDragIn;
|
|
||||||
if (isDragIn) {
|
|
||||||
options.onDragEnter && options.onDragEnter();
|
|
||||||
} else {
|
|
||||||
options.onDragLeave && options.onDragLeave();
|
|
||||||
return {
|
|
||||||
dragLeft: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireOnDropReady({ options }) {
|
|
||||||
let lastAddedIndex = null;
|
|
||||||
return ({ dragResult: { addedIndex, removedIndex }, draggableInfo: { payload, element } }) => {
|
|
||||||
if (options.onDropReady && lastAddedIndex !== addedIndex) {
|
|
||||||
lastAddedIndex = addedIndex;
|
|
||||||
let adjustedAddedIndex = addedIndex;
|
|
||||||
|
|
||||||
if (removedIndex !== null && addedIndex > removedIndex) {
|
|
||||||
adjustedAddedIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.onDropReady({ addedIndex: adjustedAddedIndex, removedIndex, payload, element: element.firstElementChild });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDragHandler(params) {
|
|
||||||
if (params.options.behaviour === 'drop-zone') {
|
|
||||||
// sorting is disabled in container, addedIndex will always be 0 if dropped in
|
|
||||||
return compose(params)(
|
|
||||||
getRemovedItem,
|
|
||||||
setRemovedItemVisibilty,
|
|
||||||
getPosition,
|
|
||||||
notifyParentOnPositionCapture,
|
|
||||||
getElementSize,
|
|
||||||
handleTargetContainer,
|
|
||||||
getDragInsertionIndexForDropZone,
|
|
||||||
getShadowBeginEndForDropZone,
|
|
||||||
fireDragEnterLeaveEvents,
|
|
||||||
fireOnDropReady
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return compose(params)(
|
|
||||||
getRemovedItem,
|
|
||||||
setRemovedItemVisibilty,
|
|
||||||
getPosition,
|
|
||||||
notifyParentOnPositionCapture,
|
|
||||||
getElementSize,
|
|
||||||
handleTargetContainer,
|
|
||||||
invalidateShadowBeginEndIfNeeded,
|
|
||||||
getNextAddedIndex,
|
|
||||||
resetShadowAdjustment,
|
|
||||||
handleInsertionSizeChange,
|
|
||||||
calculateTranslations,
|
|
||||||
getShadowBeginEnd,
|
|
||||||
handleFirstInsertShadowAdjustment,
|
|
||||||
fireDragEnterLeaveEvents,
|
|
||||||
fireOnDropReady
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDefaultDragResult() {
|
|
||||||
return {
|
|
||||||
addedIndex: null,
|
|
||||||
removedIndex: null,
|
|
||||||
elementSize: null,
|
|
||||||
pos: null,
|
|
||||||
shadowBeginEnd: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function compose(params) {
|
|
||||||
return (...functions) => {
|
|
||||||
const hydratedFunctions = functions.map(p => p(params));
|
|
||||||
let result = null;
|
|
||||||
return draggableInfo => {
|
|
||||||
result = hydratedFunctions.reduce((dragResult, fn) => {
|
|
||||||
return Object.assign(dragResult, fn({ draggableInfo, dragResult }));
|
|
||||||
}, result || getDefaultDragResult());
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container definition begin
|
|
||||||
function Container(element) {
|
|
||||||
return function(options) {
|
|
||||||
let dragResult = null;
|
|
||||||
let lastDraggableInfo = null;
|
|
||||||
const props = getContainerProps(element, options);
|
|
||||||
let dragHandler = getDragHandler(props);
|
|
||||||
let dropHandler = handleDrop(props);
|
|
||||||
let parentContainer = null;
|
|
||||||
let posIsInChildContainer = false;
|
|
||||||
let childContainers = [];
|
|
||||||
|
|
||||||
function processLastDraggableInfo() {
|
|
||||||
if (lastDraggableInfo !== null) {
|
|
||||||
lastDraggableInfo.invalidateShadow = true;
|
|
||||||
dragResult = dragHandler(lastDraggableInfo);
|
|
||||||
lastDraggableInfo.invalidateShadow = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChildPositionCaptured(isCaptured) {
|
|
||||||
posIsInChildContainer = isCaptured;
|
|
||||||
if (parentContainer) {
|
|
||||||
parentContainer.onChildPositionCaptured(isCaptured);
|
|
||||||
if (lastDraggableInfo) {
|
|
||||||
dragResult = dragHandler(lastDraggableInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDraggables(draggables, element, options) {
|
|
||||||
const newDraggables = wrapChildren(element, options.orientation, options.animationDuration);
|
|
||||||
for (let i = 0; i < newDraggables.length; i++) {
|
|
||||||
draggables[i] = newDraggables[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < draggables.length - newDraggables.length; i++) {
|
|
||||||
draggables.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareDrag(container, relevantContainers) {
|
|
||||||
const element = container.element;
|
|
||||||
const draggables = props.draggables;
|
|
||||||
const options = container.getOptions();
|
|
||||||
setDraggables(draggables, element, options);
|
|
||||||
container.layout.invalidateRects();
|
|
||||||
registerToParentContainer(container, relevantContainers);
|
|
||||||
draggables.forEach(p => setAnimation(p, true, options.animationDuration));
|
|
||||||
}
|
|
||||||
|
|
||||||
props.layout.setScrollListener(function() {
|
|
||||||
processLastDraggableInfo();
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleDragLeftDeferedTranslation() {
|
|
||||||
if (dragResult.dragLeft && props.options.behaviour !== 'drop-zone') {
|
|
||||||
dragResult.dragLeft = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
if (dragResult) calculateTranslations(props)({ dragResult });
|
|
||||||
}, 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispose(container) {
|
|
||||||
unwrapChildren(container.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
element,
|
|
||||||
draggables: props.draggables,
|
|
||||||
isDragRelevant: isDragRelevant(props),
|
|
||||||
getScale: props.layout.getContainerScale,
|
|
||||||
layout: props.layout,
|
|
||||||
getChildContainers: () => childContainers,
|
|
||||||
onChildPositionCaptured,
|
|
||||||
dispose,
|
|
||||||
prepareDrag,
|
|
||||||
isPosInChildContainer: () => posIsInChildContainer,
|
|
||||||
handleDrag: function(draggableInfo) {
|
|
||||||
lastDraggableInfo = draggableInfo;
|
|
||||||
dragResult = dragHandler(draggableInfo);
|
|
||||||
handleDragLeftDeferedTranslation();
|
|
||||||
return dragResult;
|
|
||||||
},
|
|
||||||
handleDrop: function(draggableInfo) {
|
|
||||||
lastDraggableInfo = null;
|
|
||||||
onChildPositionCaptured(false);
|
|
||||||
dragHandler = getDragHandler(props);
|
|
||||||
dropHandler(draggableInfo, dragResult);
|
|
||||||
dragResult = null;
|
|
||||||
parentContainer = null;
|
|
||||||
childContainers = [];
|
|
||||||
},
|
|
||||||
getDragResult: function() {
|
|
||||||
return dragResult;
|
|
||||||
},
|
|
||||||
getTranslateCalculator: function(...params) {
|
|
||||||
return calculateTranslations(props)(...params);
|
|
||||||
},
|
|
||||||
setParentContainer: e => {
|
|
||||||
parentContainer = e;
|
|
||||||
},
|
|
||||||
getParentContainer: () => parentContainer,
|
|
||||||
onTranslated: () => {
|
|
||||||
processLastDraggableInfo();
|
|
||||||
},
|
|
||||||
getOptions: () => props.options,
|
|
||||||
setDraggables: () => {
|
|
||||||
setDraggables(props.draggables, element, props.options);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
behaviour: 'move',
|
|
||||||
groupName: 'bla bla', // if not defined => container will not interfere with other containers
|
|
||||||
orientation: 'vertical',
|
|
||||||
dragHandleSelector: null,
|
|
||||||
nonDragAreaSelector: 'some selector',
|
|
||||||
dragBeginDelay: 0,
|
|
||||||
animationDuration: 180,
|
|
||||||
autoScrollEnabled: true,
|
|
||||||
lockAxis: true,
|
|
||||||
dragClass: null,
|
|
||||||
dropClass: null,
|
|
||||||
onDragStart: (index, payload) => {},
|
|
||||||
onDrop: ({ removedIndex, addedIndex, payload, element }) => {},
|
|
||||||
getChildPayload: index => null,
|
|
||||||
shouldAnimateDrop: (sourceContainerOptions, payload) => true,
|
|
||||||
shouldAcceptDrop: (sourceContainerOptions, payload) => true,
|
|
||||||
onDragEnter: () => {},
|
|
||||||
onDragLeave: () => { },
|
|
||||||
onDropReady: ({ removedIndex, addedIndex, payload, element }) => { },
|
|
||||||
};
|
|
||||||
|
|
||||||
// exported part of container
|
|
||||||
function SmoothDnD(element, options) {
|
|
||||||
const containerIniter = Container(element);
|
|
||||||
const container = containerIniter(options);
|
|
||||||
element[containerInstance] = container;
|
|
||||||
Mediator.register(container);
|
|
||||||
return {
|
|
||||||
dispose: function() {
|
|
||||||
Mediator.unregister(container);
|
|
||||||
container.layout.dispose();
|
|
||||||
container.dispose(container);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SmoothDnD;
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
import { getScrollingAxis, getVisibleRect } from "./utils";
|
|
||||||
|
|
||||||
const maxSpeed = 1500; // px/s
|
|
||||||
// const minSpeed = 20; // px/s
|
|
||||||
|
|
||||||
function addScrollValue(element, axis, value) {
|
|
||||||
if (element) {
|
|
||||||
if (element !== window) {
|
|
||||||
if (axis === "x") {
|
|
||||||
element.scrollLeft += value;
|
|
||||||
} else {
|
|
||||||
element.scrollTop += value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (axis === "x") {
|
|
||||||
element.scrollBy(value, 0);
|
|
||||||
} else {
|
|
||||||
element.scrollBy(0, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createAnimator = (element, axis = "y") => {
|
|
||||||
let isAnimating = false;
|
|
||||||
let request = null;
|
|
||||||
let startTime = null;
|
|
||||||
let direction = null;
|
|
||||||
let speed = null;
|
|
||||||
|
|
||||||
function animate(_direction, _speed) {
|
|
||||||
direction = _direction;
|
|
||||||
speed = _speed;
|
|
||||||
isAnimating = true;
|
|
||||||
if (isAnimating) {
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
if (request === null) {
|
|
||||||
request = requestAnimationFrame((timestamp) => {
|
|
||||||
if (startTime === null) {
|
|
||||||
startTime = timestamp;
|
|
||||||
}
|
|
||||||
const timeDiff = timestamp - startTime;
|
|
||||||
startTime = timestamp;
|
|
||||||
let distanceDiff = (timeDiff / 1000) * speed;
|
|
||||||
distanceDiff = direction === "begin" ? 0 - distanceDiff : distanceDiff;
|
|
||||||
addScrollValue(element, axis, distanceDiff);
|
|
||||||
request = null;
|
|
||||||
start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stop() {
|
|
||||||
if (isAnimating) {
|
|
||||||
cancelAnimationFrame(request);
|
|
||||||
isAnimating = false;
|
|
||||||
startTime = null;
|
|
||||||
request = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
animate,
|
|
||||||
stop
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function getAutoScrollInfo(position, scrollableInfo) {
|
|
||||||
const { left, right, top, bottom } = scrollableInfo.rect;
|
|
||||||
const { x, y } = position;
|
|
||||||
if (x < left || x > right || y < top || y > bottom) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let begin;
|
|
||||||
let end;
|
|
||||||
let pos;
|
|
||||||
if (scrollableInfo.axis === "x") {
|
|
||||||
begin = left;
|
|
||||||
end = right;
|
|
||||||
pos = x;
|
|
||||||
} else {
|
|
||||||
begin = top;
|
|
||||||
end = bottom;
|
|
||||||
pos = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
const moveDistance = 100;
|
|
||||||
if (end - pos < moveDistance) {
|
|
||||||
return {
|
|
||||||
direction: "end",
|
|
||||||
speedFactor: (moveDistance - (end - pos)) / moveDistance
|
|
||||||
};
|
|
||||||
} else if (pos - begin < moveDistance) {
|
|
||||||
// console.log(pos - begin);
|
|
||||||
return {
|
|
||||||
direction: "begin",
|
|
||||||
speedFactor: (moveDistance - (pos - begin)) / moveDistance
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollableInfo(element) {
|
|
||||||
const result = {
|
|
||||||
element,
|
|
||||||
rect: getVisibleRect(element, element.getBoundingClientRect()),
|
|
||||||
descendants: [],
|
|
||||||
invalidate,
|
|
||||||
axis: null,
|
|
||||||
dispose
|
|
||||||
};
|
|
||||||
|
|
||||||
function dispose() {
|
|
||||||
element.removeEventListener("scroll", invalidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidate() {
|
|
||||||
result.rect = getVisibleRect(element, element.getBoundingClientRect());
|
|
||||||
result.descendants.forEach((p) => p.invalidate());
|
|
||||||
}
|
|
||||||
|
|
||||||
element.addEventListener("scroll", invalidate);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCurrentElement(current, scrollables, firstDescendentScrollable) {
|
|
||||||
const scrollingAxis = getScrollingAxis(current);
|
|
||||||
if (scrollingAxis) {
|
|
||||||
if (!scrollables.some((p) => p.element === current)) {
|
|
||||||
const info = scrollableInfo(current);
|
|
||||||
if (firstDescendentScrollable) {
|
|
||||||
info.descendants.push(firstDescendentScrollable);
|
|
||||||
}
|
|
||||||
firstDescendentScrollable = info;
|
|
||||||
if (scrollingAxis === "xy") {
|
|
||||||
scrollables.push(Object.assign({}, info, { axis: "x" }));
|
|
||||||
scrollables.push(Object.assign({}, info, { axis: "y" }, { descendants: [] }));
|
|
||||||
} else {
|
|
||||||
scrollables.push(Object.assign({}, info, { axis: scrollingAxis }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { current: current.parentElement, firstDescendentScrollable };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScrollableElements(containerElements) {
|
|
||||||
const scrollables = [];
|
|
||||||
let firstDescendentScrollable = null;
|
|
||||||
containerElements.forEach((el) => {
|
|
||||||
let current = el;
|
|
||||||
firstDescendentScrollable = null;
|
|
||||||
while (current) {
|
|
||||||
const result = handleCurrentElement(current, scrollables, firstDescendentScrollable);
|
|
||||||
current = result.current;
|
|
||||||
firstDescendentScrollable = result.firstDescendentScrollable;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return scrollables;
|
|
||||||
}
|
|
||||||
function getScrollableAnimator(scrollableInfo) {
|
|
||||||
return Object.assign(scrollableInfo, createAnimator(scrollableInfo.element, scrollableInfo.axis));
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWindowAnimators() {
|
|
||||||
function getWindowRect() {
|
|
||||||
return {
|
|
||||||
left: 0,
|
|
||||||
right: global.innerWidth,
|
|
||||||
top: 0,
|
|
||||||
bottom: global.innerHeight
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
Object.assign({ rect: getWindowRect(), axis: "y" }, createAnimator(global)),
|
|
||||||
Object.assign({ rect: getWindowRect(), axis: "x" }, createAnimator(global, "x"))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const dragScroller = (containers) => {
|
|
||||||
const scrollablesInfo = getScrollableElements(containers.map((p) => p.element));
|
|
||||||
const animators = [...scrollablesInfo.map(getScrollableAnimator), ...getWindowAnimators()];
|
|
||||||
return ({ draggableInfo, reset }) => {
|
|
||||||
if (animators.length) {
|
|
||||||
if (reset) {
|
|
||||||
animators.forEach((p) => p.stop());
|
|
||||||
scrollablesInfo.forEach((p) => p.dispose());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
animators.forEach((animator) => {
|
|
||||||
const scrollParams = getAutoScrollInfo(draggableInfo.mousePosition, animator);
|
|
||||||
if (scrollParams) {
|
|
||||||
animator.animate(scrollParams.direction, scrollParams.speedFactor * maxSpeed);
|
|
||||||
} else {
|
|
||||||
animator.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default dragScroller;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { addChildAt, removeChildAt } from './utils';
|
|
||||||
import {
|
|
||||||
wrapperClass,
|
|
||||||
animationClass,
|
|
||||||
containersInDraggable
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
|
|
||||||
export function domDropHandler({ element, draggables, layout, options }) {
|
|
||||||
return (dropResult, onDrop) => {
|
|
||||||
const { removedIndex, addedIndex, droppedElement } = dropResult;
|
|
||||||
let removedWrapper = null;
|
|
||||||
if (removedIndex !== null) {
|
|
||||||
removedWrapper = removeChildAt(element, removedIndex);
|
|
||||||
draggables.splice(removedIndex, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedIndex !== null) {
|
|
||||||
const wrapper = global.document.createElement('div');
|
|
||||||
wrapper.className = `${wrapperClass}`;
|
|
||||||
wrapper.appendChild(removedWrapper && removedWrapper.firstElementChild ? removedWrapper.firstElementChild : droppedElement);
|
|
||||||
wrapper[containersInDraggable] = [];
|
|
||||||
addChildAt(element, wrapper, addedIndex);
|
|
||||||
if (addedIndex >= draggables.length) {
|
|
||||||
draggables.push(wrapper);
|
|
||||||
} else {
|
|
||||||
draggables.splice(addedIndex, 0, wrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onDrop) {
|
|
||||||
onDrop(dropResult);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function reactDropHandler() {
|
|
||||||
const handler = ({ element, draggables, layout, options }) => {
|
|
||||||
return (dropResult, onDrop) => {
|
|
||||||
if (onDrop) {
|
|
||||||
onDrop(dropResult);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
handler
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
import * as Utils from './utils';
|
|
||||||
import { translationValue, visibilityValue, extraSizeForInsertion, containersInDraggable } from './constants';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const horizontalMap = {
|
|
||||||
size: 'offsetWidth',
|
|
||||||
distanceToParent: 'offsetLeft',
|
|
||||||
translate: 'transform',
|
|
||||||
begin: 'left',
|
|
||||||
end: 'right',
|
|
||||||
dragPosition: 'x',
|
|
||||||
scrollSize: 'scrollWidth',
|
|
||||||
offsetSize: 'offsetWidth',
|
|
||||||
scrollValue: 'scrollLeft',
|
|
||||||
scale: 'scaleX',
|
|
||||||
setSize: 'width',
|
|
||||||
setters: {
|
|
||||||
'translate': (val) => `translate3d(${val}px, 0, 0)`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const verticalMap = {
|
|
||||||
size: 'offsetHeight',
|
|
||||||
distanceToParent: 'offsetTop',
|
|
||||||
translate: 'transform',
|
|
||||||
begin: 'top',
|
|
||||||
end: 'bottom',
|
|
||||||
dragPosition: 'y',
|
|
||||||
scrollSize: 'scrollHeight',
|
|
||||||
offsetSize: 'offsetHeight',
|
|
||||||
scrollValue: 'scrollTop',
|
|
||||||
scale: 'scaleY',
|
|
||||||
setSize: 'height',
|
|
||||||
setters: {
|
|
||||||
'translate': (val) => `translate3d(0,${val}px, 0)`
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function orientationDependentProps(map) {
|
|
||||||
function get(obj, prop) {
|
|
||||||
const mappedProp = map[prop];
|
|
||||||
return obj[mappedProp || prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
function set(obj, prop, value) {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
obj[map[prop]] = map.setters[prop] ? map.setters[prop](value) : value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { get, set };
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function layoutManager(containerElement, orientation, _animationDuration) {
|
|
||||||
containerElement[extraSizeForInsertion] = 0;
|
|
||||||
const animationDuration = _animationDuration;
|
|
||||||
const map = orientation === 'horizontal' ? horizontalMap : verticalMap;
|
|
||||||
const propMapper = orientationDependentProps(map);
|
|
||||||
const values = {
|
|
||||||
translation: 0
|
|
||||||
};
|
|
||||||
let registeredScrollListener = null;
|
|
||||||
|
|
||||||
global.addEventListener('resize', function() {
|
|
||||||
invalidateContainerRectangles(containerElement);
|
|
||||||
// invalidateContainerScale(containerElement);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
invalidate();
|
|
||||||
}, 10);
|
|
||||||
// invalidate();
|
|
||||||
|
|
||||||
const scrollListener = Utils.listenScrollParent(containerElement, function() {
|
|
||||||
invalidateContainerRectangles(containerElement);
|
|
||||||
registeredScrollListener && registeredScrollListener();
|
|
||||||
});
|
|
||||||
function invalidate() {
|
|
||||||
invalidateContainerRectangles(containerElement);
|
|
||||||
invalidateContainerScale(containerElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
let visibleRect;
|
|
||||||
function invalidateContainerRectangles(containerElement) {
|
|
||||||
values.rect = Utils.getContainerRect(containerElement);
|
|
||||||
values.visibleRect = Utils.getVisibleRect(containerElement, values.rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidateContainerScale(containerElement) {
|
|
||||||
const rect = containerElement.getBoundingClientRect();
|
|
||||||
values.scaleX = containerElement.offsetWidth ? ((rect.right - rect.left) / containerElement.offsetWidth) : 1;
|
|
||||||
values.scaleY = containerElement.offsetHeight ? ((rect.bottom - rect.top) / containerElement.offsetHeight) : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContainerRectangles() {
|
|
||||||
return {
|
|
||||||
rect: values.rect,
|
|
||||||
visibleRect: values.visibleRect
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBeginEndOfDOMRect(rect) {
|
|
||||||
return {
|
|
||||||
begin: propMapper.get(rect, 'begin'),
|
|
||||||
end: propMapper.get(rect, 'end')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBeginEndOfContainer() {
|
|
||||||
const begin = propMapper.get(values.rect, 'begin') + values.translation;
|
|
||||||
const end = propMapper.get(values.rect, 'end') + values.translation;
|
|
||||||
return { begin, end };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBeginEndOfContainerVisibleRect() {
|
|
||||||
const begin = propMapper.get(values.visibleRect, 'begin') + values.translation;
|
|
||||||
const end = propMapper.get(values.visibleRect, 'end') + values.translation;
|
|
||||||
return { begin, end };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getContainerScale() {
|
|
||||||
return { scaleX: values.scaleX, scaleY: values.scaleY };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSize(element) {
|
|
||||||
return propMapper.get(element, 'size') * propMapper.get(values, 'scale');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDistanceToOffsetParent(element) {
|
|
||||||
const distance = propMapper.get(element, 'distanceToParent') + (element[translationValue] || 0);
|
|
||||||
return distance * propMapper.get(values, 'scale');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBeginEnd(element) {
|
|
||||||
const begin = getDistanceToOffsetParent(element) + (propMapper.get(values.rect, 'begin') + values.translation) - propMapper.get(containerElement, 'scrollValue');
|
|
||||||
return {
|
|
||||||
begin,
|
|
||||||
end: begin + getSize(element) * propMapper.get(values, 'scale')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSize(element, size) {
|
|
||||||
propMapper.set(element, 'setSize', size);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAxisValue(position) {
|
|
||||||
return propMapper.get(position, 'dragPosition');
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateDescendantContainerRects(container) {
|
|
||||||
container.layout.invalidateRects();
|
|
||||||
container.onTranslated();
|
|
||||||
if (container.getChildContainers()) {
|
|
||||||
container.getChildContainers().forEach(p => updateDescendantContainerRects(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTranslation(element, translation) {
|
|
||||||
if (!translation) {
|
|
||||||
element.style.removeProperty('transform');
|
|
||||||
} else {
|
|
||||||
propMapper.set(element.style, 'translate', translation);
|
|
||||||
}
|
|
||||||
element[translationValue] = translation;
|
|
||||||
|
|
||||||
if (element[containersInDraggable]) {
|
|
||||||
setTimeout(() => {
|
|
||||||
element[containersInDraggable].forEach(p => {
|
|
||||||
updateDescendantContainerRects(p);
|
|
||||||
});
|
|
||||||
}, animationDuration + 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTranslation(element) {
|
|
||||||
return element[translationValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
function setVisibility(element, isVisible) {
|
|
||||||
if (element[visibilityValue] === undefined || element[visibilityValue] !== isVisible) {
|
|
||||||
if (isVisible) {
|
|
||||||
element.style.removeProperty('visibility');
|
|
||||||
} else {
|
|
||||||
element.style.visibility = 'hidden';
|
|
||||||
}
|
|
||||||
element[visibilityValue] = isVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVisible(element) {
|
|
||||||
return element[visibilityValue] === undefined || element[visibilityValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
function isInVisibleRect(x, y) {
|
|
||||||
let { left, top, right, bottom } = values.visibleRect;
|
|
||||||
|
|
||||||
// if there is no wrapper in rect size will be 0 and wont accept any drop
|
|
||||||
// so make sure at least there is 30px difference
|
|
||||||
if (bottom - top < 2) {
|
|
||||||
bottom = top + 30;
|
|
||||||
}
|
|
||||||
const containerRect = values.rect;
|
|
||||||
if (orientation === 'vertical') {
|
|
||||||
return x > containerRect.left && x < containerRect.right && y > top && y < bottom;
|
|
||||||
} else {
|
|
||||||
return x > left && x < right && y > containerRect.top && y < containerRect.bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setScrollListener(callback) {
|
|
||||||
registeredScrollListener = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTopLeftOfElementBegin(begin) {
|
|
||||||
let top = 0;
|
|
||||||
let left = 0;
|
|
||||||
if (orientation === 'horizontal') {
|
|
||||||
left = begin;
|
|
||||||
top = values.rect.top;
|
|
||||||
} else {
|
|
||||||
left = values.rect.left;
|
|
||||||
top = begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
top, left
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScrollSize(element) {
|
|
||||||
return propMapper.get(element, 'scrollSize');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScrollValue(element) {
|
|
||||||
return propMapper.get(element, 'scrollValue');
|
|
||||||
}
|
|
||||||
|
|
||||||
function setScrollValue(element, val) {
|
|
||||||
return propMapper.set(element, 'scrollValue', val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispose() {
|
|
||||||
if (scrollListener) {
|
|
||||||
scrollListener.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibleRect) {
|
|
||||||
visibleRect.parentNode.removeChild(visibleRect);
|
|
||||||
visibleRect = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPosition(position) {
|
|
||||||
return isInVisibleRect(position.x, position.y) ? getAxisValue(position) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidateRects() {
|
|
||||||
invalidateContainerRectangles(containerElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getSize,
|
|
||||||
//getDistanceToContainerBegining,
|
|
||||||
getContainerRectangles,
|
|
||||||
getBeginEndOfDOMRect,
|
|
||||||
getBeginEndOfContainer,
|
|
||||||
getBeginEndOfContainerVisibleRect,
|
|
||||||
getBeginEnd,
|
|
||||||
getAxisValue,
|
|
||||||
setTranslation,
|
|
||||||
getTranslation,
|
|
||||||
setVisibility,
|
|
||||||
isVisible,
|
|
||||||
isInVisibleRect,
|
|
||||||
dispose,
|
|
||||||
getContainerScale,
|
|
||||||
setScrollListener,
|
|
||||||
setSize,
|
|
||||||
getTopLeftOfElementBegin,
|
|
||||||
getScrollSize,
|
|
||||||
getScrollValue,
|
|
||||||
setScrollValue,
|
|
||||||
invalidate,
|
|
||||||
invalidateRects,
|
|
||||||
getPosition,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,479 +0,0 @@
|
|||||||
import './polyfills';
|
|
||||||
import * as Utils from './utils';
|
|
||||||
import * as constants from './constants';
|
|
||||||
import { addStyleToHead, addCursorStyleToBody, removeStyle } from './styles';
|
|
||||||
import dragScroller from './dragscroller';
|
|
||||||
|
|
||||||
const grabEvents = ['mousedown', 'touchstart'];
|
|
||||||
const moveEvents = ['mousemove', 'touchmove'];
|
|
||||||
const releaseEvents = ['mouseup', 'touchend'];
|
|
||||||
|
|
||||||
let dragListeningContainers = null;
|
|
||||||
let grabbedElement = null;
|
|
||||||
let ghostInfo = null;
|
|
||||||
let draggableInfo = null;
|
|
||||||
let containers = [];
|
|
||||||
let isDragging = false;
|
|
||||||
let removedElement = null;
|
|
||||||
|
|
||||||
let handleDrag = null;
|
|
||||||
let handleScroll = null;
|
|
||||||
let sourceContainer = null;
|
|
||||||
let sourceContainerLockAxis = null;
|
|
||||||
let cursorStyleElement = null;
|
|
||||||
|
|
||||||
// Utils.addClass(document.body, 'clearfix');
|
|
||||||
|
|
||||||
const isMobile = Utils.isMobile();
|
|
||||||
|
|
||||||
function listenEvents() {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
addGrabListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addGrabListeners() {
|
|
||||||
grabEvents.forEach(e => {
|
|
||||||
global.document.addEventListener(e, onMouseDown, { passive: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addMoveListeners() {
|
|
||||||
moveEvents.forEach(e => {
|
|
||||||
global.document.addEventListener(e, onMouseMove, { passive: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeMoveListeners() {
|
|
||||||
moveEvents.forEach(e => {
|
|
||||||
global.document.removeEventListener(e, onMouseMove, { passive: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addReleaseListeners() {
|
|
||||||
releaseEvents.forEach(e => {
|
|
||||||
global.document.addEventListener(e, onMouseUp, { passive: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeReleaseListeners() {
|
|
||||||
releaseEvents.forEach(e => {
|
|
||||||
global.document.removeEventListener(e, onMouseUp, { passive: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGhostParent() {
|
|
||||||
if (draggableInfo.ghostParent) {
|
|
||||||
return draggableInfo.ghostParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grabbedElement) {
|
|
||||||
return grabbedElement.parentElement || global.document.body;
|
|
||||||
} else {
|
|
||||||
return global.document.body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGhostElement(wrapperElement, { x, y }, container, cursor) {
|
|
||||||
const { scaleX = 1, scaleY = 1 } = container.getScale();
|
|
||||||
const { left, top, right, bottom } = wrapperElement.getBoundingClientRect();
|
|
||||||
const midX = left + (right - left) / 2;
|
|
||||||
const midY = top + (bottom - top) / 2;
|
|
||||||
const ghost = wrapperElement.cloneNode(true);
|
|
||||||
ghost.style.zIndex = 1000;
|
|
||||||
ghost.style.boxSizing = 'border-box';
|
|
||||||
ghost.style.position = 'fixed';
|
|
||||||
ghost.style.left = left + 'px';
|
|
||||||
ghost.style.top = top + 'px';
|
|
||||||
ghost.style.width = right - left + 'px';
|
|
||||||
ghost.style.height = bottom - top + 'px';
|
|
||||||
ghost.style.overflow = 'visible';
|
|
||||||
ghost.style.transition = null;
|
|
||||||
ghost.style.removeProperty('transition');
|
|
||||||
ghost.style.pointerEvents = 'none';
|
|
||||||
|
|
||||||
if (container.getOptions().dragClass) {
|
|
||||||
setTimeout(() => {
|
|
||||||
Utils.addClass(ghost.firstElementChild, container.getOptions().dragClass);
|
|
||||||
const dragCursor = global.getComputedStyle(ghost.firstElementChild).cursor;
|
|
||||||
cursorStyleElement = addCursorStyleToBody(dragCursor);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cursorStyleElement = addCursorStyleToBody(cursor);
|
|
||||||
}
|
|
||||||
Utils.addClass(ghost, container.getOptions().orientation);
|
|
||||||
Utils.addClass(ghost, constants.ghostClass);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ghost: ghost,
|
|
||||||
centerDelta: { x: midX - x, y: midY - y },
|
|
||||||
positionDelta: { left: left - x, top: top - y }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDraggableInfo(draggableElement) {
|
|
||||||
const container = containers.filter(p => draggableElement.parentElement === p.element)[0];
|
|
||||||
const draggableIndex = container.draggables.indexOf(draggableElement);
|
|
||||||
const getGhostParent = container.getOptions().getGhostParent;
|
|
||||||
return {
|
|
||||||
container,
|
|
||||||
element: draggableElement,
|
|
||||||
elementIndex: draggableIndex,
|
|
||||||
payload: container.getOptions().getChildPayload
|
|
||||||
? container.getOptions().getChildPayload(draggableIndex)
|
|
||||||
: undefined,
|
|
||||||
targetElement: null,
|
|
||||||
position: { x: 0, y: 0 },
|
|
||||||
groupName: container.getOptions().groupName,
|
|
||||||
ghostParent: getGhostParent ? getGhostParent() : null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDropAnimation(callback) {
|
|
||||||
function endDrop() {
|
|
||||||
Utils.removeClass(ghostInfo.ghost, 'animated');
|
|
||||||
ghostInfo.ghost.style.transitionDuration = null;
|
|
||||||
getGhostParent().removeChild(ghostInfo.ghost);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateGhostToPosition({ top, left }, duration, dropClass) {
|
|
||||||
Utils.addClass(ghostInfo.ghost, 'animated');
|
|
||||||
if (dropClass) {
|
|
||||||
Utils.addClass(ghostInfo.ghost.firstElementChild, dropClass);
|
|
||||||
}
|
|
||||||
ghostInfo.ghost.style.transitionDuration = duration + 'ms';
|
|
||||||
ghostInfo.ghost.style.left = left + 'px';
|
|
||||||
ghostInfo.ghost.style.top = top + 'px';
|
|
||||||
setTimeout(function() {
|
|
||||||
endDrop();
|
|
||||||
}, duration + 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldAnimateDrop(options) {
|
|
||||||
return options.shouldAnimateDrop
|
|
||||||
? options.shouldAnimateDrop(draggableInfo.container.getOptions(), draggableInfo.payload)
|
|
||||||
: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (draggableInfo.targetElement) {
|
|
||||||
const container = containers.filter(p => p.element === draggableInfo.targetElement)[0];
|
|
||||||
if (shouldAnimateDrop(container.getOptions())) {
|
|
||||||
const dragResult = container.getDragResult();
|
|
||||||
animateGhostToPosition(
|
|
||||||
dragResult.shadowBeginEnd.rect,
|
|
||||||
Math.max(150, container.getOptions().animationDuration / 2),
|
|
||||||
container.getOptions().dropClass
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
endDrop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const container = containers.filter(p => p === draggableInfo.container)[0];
|
|
||||||
const { behaviour, removeOnDropOut } = container.getOptions();
|
|
||||||
if (behaviour === 'move' && !removeOnDropOut && container.getDragResult()) {
|
|
||||||
const { removedIndex, elementSize } = container.getDragResult();
|
|
||||||
const layout = container.layout;
|
|
||||||
// drag ghost to back
|
|
||||||
container.getTranslateCalculator({
|
|
||||||
dragResult: {
|
|
||||||
removedIndex,
|
|
||||||
addedIndex: removedIndex,
|
|
||||||
elementSize
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const prevDraggableEnd =
|
|
||||||
removedIndex > 0
|
|
||||||
? layout.getBeginEnd(container.draggables[removedIndex - 1]).end
|
|
||||||
: layout.getBeginEndOfContainer().begin;
|
|
||||||
animateGhostToPosition(
|
|
||||||
layout.getTopLeftOfElementBegin(prevDraggableEnd),
|
|
||||||
container.getOptions().animationDuration,
|
|
||||||
container.getOptions().dropClass
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Utils.addClass(ghostInfo.ghost, 'animated');
|
|
||||||
ghostInfo.ghost.style.transitionDuration = container.getOptions().animationDuration + 'ms';
|
|
||||||
ghostInfo.ghost.style.opacity = '0';
|
|
||||||
ghostInfo.ghost.style.transform = 'scale(0.90)';
|
|
||||||
setTimeout(function() {
|
|
||||||
endDrop();
|
|
||||||
}, container.getOptions().animationDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragStartConditions = (function handleDragStartConditions() {
|
|
||||||
let startEvent;
|
|
||||||
let delay;
|
|
||||||
let clb;
|
|
||||||
let timer = null;
|
|
||||||
const moveThreshold = 1;
|
|
||||||
const maxMoveInDelay = 5;
|
|
||||||
|
|
||||||
function onMove(event) {
|
|
||||||
const { clientX: currentX, clientY: currentY } = getPointerEvent(event);
|
|
||||||
if (!delay) {
|
|
||||||
if (
|
|
||||||
Math.abs(startEvent.clientX - currentX) > moveThreshold ||
|
|
||||||
Math.abs(startEvent.clientY - currentY) > moveThreshold
|
|
||||||
) {
|
|
||||||
return callCallback();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
Math.abs(startEvent.clientX - currentX) > maxMoveInDelay ||
|
|
||||||
Math.abs(startEvent.clientY - currentY) > maxMoveInDelay
|
|
||||||
) {
|
|
||||||
deregisterEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUp() {
|
|
||||||
deregisterEvent();
|
|
||||||
}
|
|
||||||
function onHTMLDrag() {
|
|
||||||
deregisterEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerEvents() {
|
|
||||||
if (delay) {
|
|
||||||
timer = setTimeout(callCallback, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
moveEvents.forEach(e => global.document.addEventListener(e, onMove), {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
releaseEvents.forEach(e => global.document.addEventListener(e, onUp), {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
global.document.addEventListener('drag', onHTMLDrag, {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function deregisterEvent() {
|
|
||||||
clearTimeout(timer);
|
|
||||||
moveEvents.forEach(e => global.document.removeEventListener(e, onMove), {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
releaseEvents.forEach(e => global.document.removeEventListener(e, onUp), {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
global.document.removeEventListener('drag', onHTMLDrag, {
|
|
||||||
passive: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function callCallback() {
|
|
||||||
clearTimeout(timer);
|
|
||||||
deregisterEvent();
|
|
||||||
clb();
|
|
||||||
}
|
|
||||||
|
|
||||||
return function(_startEvent, _delay, _clb) {
|
|
||||||
startEvent = getPointerEvent(_startEvent);
|
|
||||||
delay = (typeof _delay === 'number') ? _delay : (isMobile ? 200 : 0);
|
|
||||||
clb = _clb;
|
|
||||||
|
|
||||||
registerEvents();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
function onMouseDown(event) {
|
|
||||||
const e = getPointerEvent(event);
|
|
||||||
if (!isDragging && (e.button === undefined || e.button === 0)) {
|
|
||||||
grabbedElement = Utils.getParent(e.target, '.' + constants.wrapperClass);
|
|
||||||
if (grabbedElement) {
|
|
||||||
const containerElement = Utils.getParent(grabbedElement, '.' + constants.containerClass);
|
|
||||||
const container = containers.filter(p => p.element === containerElement)[0];
|
|
||||||
const dragHandleSelector = container.getOptions().dragHandleSelector;
|
|
||||||
const nonDragAreaSelector = container.getOptions().nonDragAreaSelector;
|
|
||||||
|
|
||||||
let startDrag = true;
|
|
||||||
if (dragHandleSelector && !Utils.getParent(e.target, dragHandleSelector)) {
|
|
||||||
startDrag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonDragAreaSelector && Utils.getParent(e.target, nonDragAreaSelector)) {
|
|
||||||
startDrag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startDrag) {
|
|
||||||
handleDragStartConditions(e, container.getOptions().dragBeginDelay, () => {
|
|
||||||
Utils.clearSelection();
|
|
||||||
initiateDrag(e, Utils.getElementCursor(event.target));
|
|
||||||
addMoveListeners();
|
|
||||||
addReleaseListeners();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseUp() {
|
|
||||||
removeMoveListeners();
|
|
||||||
removeReleaseListeners();
|
|
||||||
handleScroll({ reset: true });
|
|
||||||
if (cursorStyleElement) {
|
|
||||||
removeStyle(cursorStyleElement);
|
|
||||||
cursorStyleElement = null;
|
|
||||||
}
|
|
||||||
if (draggableInfo) {
|
|
||||||
handleDropAnimation(() => {
|
|
||||||
Utils.removeClass(global.document.body, constants.disbaleTouchActions);
|
|
||||||
Utils.removeClass(global.document.body, constants.noUserSelectClass);
|
|
||||||
fireOnDragStartEnd(false);
|
|
||||||
(dragListeningContainers || []).forEach(p => {
|
|
||||||
p.handleDrop(draggableInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
dragListeningContainers = null;
|
|
||||||
grabbedElement = null;
|
|
||||||
ghostInfo = null;
|
|
||||||
draggableInfo = null;
|
|
||||||
isDragging = false;
|
|
||||||
sourceContainer = null;
|
|
||||||
sourceContainerLockAxis = null;
|
|
||||||
handleDrag = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPointerEvent(e) {
|
|
||||||
return e.touches ? e.touches[0] : e;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragHandler(dragListeningContainers) {
|
|
||||||
let targetContainers = dragListeningContainers;
|
|
||||||
return function(draggableInfo) {
|
|
||||||
let containerBoxChanged = false;
|
|
||||||
targetContainers.forEach(p => {
|
|
||||||
const dragResult = p.handleDrag(draggableInfo);
|
|
||||||
containerBoxChanged |= dragResult.containerBoxChanged || false;
|
|
||||||
dragResult.containerBoxChanged = false;
|
|
||||||
});
|
|
||||||
handleScroll({ draggableInfo });
|
|
||||||
|
|
||||||
if (containerBoxChanged) {
|
|
||||||
containerBoxChanged = false;
|
|
||||||
setTimeout(() => {
|
|
||||||
containers.forEach(p => {
|
|
||||||
p.layout.invalidateRects();
|
|
||||||
p.onTranslated();
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScrollHandler(container, dragListeningContainers) {
|
|
||||||
if (container.getOptions().autoScrollEnabled) {
|
|
||||||
return dragScroller(dragListeningContainers);
|
|
||||||
} else {
|
|
||||||
return () => null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireOnDragStartEnd(isStart) {
|
|
||||||
containers.forEach(p => {
|
|
||||||
const fn = isStart ? p.getOptions().onDragStart : p.getOptions().onDragEnd;
|
|
||||||
if (fn) {
|
|
||||||
const options = {
|
|
||||||
isSource: p === draggableInfo.container,
|
|
||||||
payload: draggableInfo.payload
|
|
||||||
};
|
|
||||||
if (p.isDragRelevant(draggableInfo.container, draggableInfo.payload)) {
|
|
||||||
options.willAcceptDrop = true;
|
|
||||||
} else {
|
|
||||||
options.willAcceptDrop = false;
|
|
||||||
}
|
|
||||||
fn(options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initiateDrag(position, cursor) {
|
|
||||||
isDragging = true;
|
|
||||||
const container = containers.filter(p => grabbedElement.parentElement === p.element)[0];
|
|
||||||
container.setDraggables();
|
|
||||||
sourceContainer = container;
|
|
||||||
sourceContainerLockAxis = container.getOptions().lockAxis ? container.getOptions().lockAxis.toLowerCase() : null;
|
|
||||||
|
|
||||||
draggableInfo = getDraggableInfo(grabbedElement);
|
|
||||||
ghostInfo = getGhostElement(
|
|
||||||
grabbedElement,
|
|
||||||
{ x: position.clientX, y: position.clientY },
|
|
||||||
draggableInfo.container,
|
|
||||||
cursor
|
|
||||||
);
|
|
||||||
draggableInfo.position = {
|
|
||||||
x: position.clientX + ghostInfo.centerDelta.x,
|
|
||||||
y: position.clientY + ghostInfo.centerDelta.y
|
|
||||||
};
|
|
||||||
draggableInfo.mousePosition = {
|
|
||||||
x: position.clientX,
|
|
||||||
y: position.clientY
|
|
||||||
};
|
|
||||||
|
|
||||||
Utils.addClass(global.document.body, constants.disbaleTouchActions);
|
|
||||||
Utils.addClass(global.document.body, constants.noUserSelectClass);
|
|
||||||
|
|
||||||
dragListeningContainers = containers.filter(p => p.isDragRelevant(container, draggableInfo.payload));
|
|
||||||
handleDrag = dragHandler(dragListeningContainers);
|
|
||||||
if (handleScroll) {
|
|
||||||
handleScroll({ reset: true });
|
|
||||||
}
|
|
||||||
handleScroll = getScrollHandler(container, dragListeningContainers);
|
|
||||||
dragListeningContainers.forEach(p => p.prepareDrag(p, dragListeningContainers));
|
|
||||||
fireOnDragStartEnd(true);
|
|
||||||
handleDrag(draggableInfo);
|
|
||||||
getGhostParent().appendChild(ghostInfo.ghost);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseMove(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const e = getPointerEvent(event);
|
|
||||||
if (!draggableInfo) {
|
|
||||||
initiateDrag(e, Utils.getElementCursor(event.target));
|
|
||||||
} else {
|
|
||||||
// just update ghost position && draggableInfo position
|
|
||||||
if (sourceContainerLockAxis) {
|
|
||||||
if (sourceContainerLockAxis === 'y') {
|
|
||||||
ghostInfo.ghost.style.top = `${e.clientY + ghostInfo.positionDelta.top}px`;
|
|
||||||
draggableInfo.position.y = e.clientY + ghostInfo.centerDelta.y;
|
|
||||||
draggableInfo.mousePosition.y = e.clientY;
|
|
||||||
} else if (sourceContainerLockAxis === 'x') {
|
|
||||||
ghostInfo.ghost.style.left = `${e.clientX + ghostInfo.positionDelta.left}px`;
|
|
||||||
draggableInfo.position.x = e.clientX + ghostInfo.centerDelta.x;
|
|
||||||
draggableInfo.mousePosition.x = e.clientX;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ghostInfo.ghost.style.left = `${e.clientX + ghostInfo.positionDelta.left}px`;
|
|
||||||
ghostInfo.ghost.style.top = `${e.clientY + ghostInfo.positionDelta.top}px`;
|
|
||||||
draggableInfo.position.x = e.clientX + ghostInfo.centerDelta.x;
|
|
||||||
draggableInfo.position.y = e.clientY + ghostInfo.centerDelta.y;
|
|
||||||
draggableInfo.mousePosition.x = e.clientX;
|
|
||||||
draggableInfo.mousePosition.y = e.clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDrag(draggableInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Mediator() {
|
|
||||||
listenEvents();
|
|
||||||
return {
|
|
||||||
register: function(container) {
|
|
||||||
containers.push(container);
|
|
||||||
},
|
|
||||||
unregister: function(container) {
|
|
||||||
containers.splice(containers.indexOf(container), 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
addStyleToHead();
|
|
||||||
|
|
||||||
export default Mediator();
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
(function(constructor) {
|
|
||||||
if (constructor && constructor.prototype && !constructor.prototype.matches) {
|
|
||||||
constructor.prototype.matches =
|
|
||||||
constructor.prototype.matchesSelector ||
|
|
||||||
constructor.prototype.mozMatchesSelector ||
|
|
||||||
constructor.prototype.msMatchesSelector ||
|
|
||||||
constructor.prototype.oMatchesSelector ||
|
|
||||||
constructor.prototype.webkitMatchesSelector ||
|
|
||||||
function(s) {
|
|
||||||
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
|
|
||||||
i = matches.length;
|
|
||||||
while (--i >= 0 && matches.item(i) !== this) {}
|
|
||||||
return i > -1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})(global.Node || global.Element);
|
|
||||||
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
import * as constants from "./constants";
|
|
||||||
|
|
||||||
const verticalWrapperClass = {
|
|
||||||
overflow: "hidden",
|
|
||||||
display: "block"
|
|
||||||
};
|
|
||||||
|
|
||||||
const horizontalWrapperClass = {
|
|
||||||
height: "100%",
|
|
||||||
display: "inline-block",
|
|
||||||
"vertical-align": "top",
|
|
||||||
"white-space": "normal"
|
|
||||||
};
|
|
||||||
|
|
||||||
const stretcherElementHorizontalClass = {
|
|
||||||
display: "inline-block"
|
|
||||||
};
|
|
||||||
|
|
||||||
const css = {
|
|
||||||
[`.${constants.containerClass}`]: {
|
|
||||||
position: "relative"
|
|
||||||
},
|
|
||||||
[`.${constants.containerClass} *`]: {
|
|
||||||
"box-sizing": "border-box"
|
|
||||||
},
|
|
||||||
[`.${constants.containerClass}.horizontal`]: {
|
|
||||||
"white-space": "nowrap"
|
|
||||||
},
|
|
||||||
[`.${constants.containerClass}.horizontal > .${constants.stretcherElementClass}`]: stretcherElementHorizontalClass,
|
|
||||||
[`.${constants.containerClass}.horizontal > .${constants.wrapperClass}`]: horizontalWrapperClass,
|
|
||||||
[`.${constants.containerClass}.vertical > .${constants.wrapperClass}`]: verticalWrapperClass,
|
|
||||||
[`.${constants.wrapperClass}`]: {
|
|
||||||
// 'overflow': 'hidden'
|
|
||||||
},
|
|
||||||
[`.${constants.wrapperClass}.horizontal`]: horizontalWrapperClass,
|
|
||||||
[`.${constants.wrapperClass}.vertical`]: verticalWrapperClass,
|
|
||||||
[`.${constants.wrapperClass}.animated`]: {
|
|
||||||
transition: "transform ease"
|
|
||||||
},
|
|
||||||
[`.${constants.ghostClass} *`]: {
|
|
||||||
//'perspective': '800px',
|
|
||||||
"box-sizing": "border-box"
|
|
||||||
},
|
|
||||||
[`.${constants.ghostClass}.animated`]: {
|
|
||||||
transition: "all ease-in-out"
|
|
||||||
},
|
|
||||||
[`.${constants.disbaleTouchActions} *`]: {
|
|
||||||
"touch-actions": "none",
|
|
||||||
"-ms-touch-actions": "none"
|
|
||||||
},
|
|
||||||
[`.${constants.noUserSelectClass} *`]: {
|
|
||||||
"-webkit-touch-callout": "none",
|
|
||||||
"-webkit-user-select": "none",
|
|
||||||
"-khtml-user-select": "none",
|
|
||||||
"-moz-user-select": "none",
|
|
||||||
"-ms-user-select": "none",
|
|
||||||
"user-select": "none"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function convertToCssString(css) {
|
|
||||||
return Object.keys(css).reduce((styleString, propName) => {
|
|
||||||
const propValue = css[propName];
|
|
||||||
if (typeof propValue === "object") {
|
|
||||||
return `${styleString}${propName}{${convertToCssString(propValue)}}`;
|
|
||||||
}
|
|
||||||
return `${styleString}${propName}:${propValue};`;
|
|
||||||
}, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function addStyleToHead() {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
const head = global.document.head || global.document.getElementsByTagName("head")[0];
|
|
||||||
const style = global.document.createElement("style");
|
|
||||||
const cssString = convertToCssString(css);
|
|
||||||
style.type = "text/css";
|
|
||||||
if (style.styleSheet) {
|
|
||||||
style.styleSheet.cssText = cssString;
|
|
||||||
} else {
|
|
||||||
style.appendChild(global.document.createTextNode(cssString));
|
|
||||||
}
|
|
||||||
|
|
||||||
head.appendChild(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addCursorStyleToBody(cursor) {
|
|
||||||
if (cursor && typeof window !== "undefined") {
|
|
||||||
const head = global.document.head || global.document.getElementsByTagName("head")[0];
|
|
||||||
const style = global.document.createElement("style");
|
|
||||||
const cssString = convertToCssString({
|
|
||||||
"body *": {
|
|
||||||
cursor: `${cursor} !important`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
style.type = "text/css";
|
|
||||||
if (style.styleSheet) {
|
|
||||||
style.styleSheet.cssText = cssString;
|
|
||||||
} else {
|
|
||||||
style.appendChild(global.document.createTextNode(cssString));
|
|
||||||
}
|
|
||||||
|
|
||||||
head.appendChild(style);
|
|
||||||
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeStyle(styleElement) {
|
|
||||||
if (styleElement && typeof window !== "undefined") {
|
|
||||||
const head = global.document.head || global.document.getElementsByTagName("head")[0];
|
|
||||||
head.removeChild(styleElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { addStyleToHead, addCursorStyleToBody, removeStyle };
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
export const getIntersection = (rect1, rect2) => {
|
|
||||||
return {
|
|
||||||
left: Math.max(rect1.left, rect2.left),
|
|
||||||
top: Math.max(rect1.top, rect2.top),
|
|
||||||
right: Math.min(rect1.right, rect2.right),
|
|
||||||
bottom: Math.min(rect1.bottom, rect2.bottom)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getIntersectionOnAxis = (rect1, rect2, axis) => {
|
|
||||||
if (axis === "x") {
|
|
||||||
return {
|
|
||||||
left: Math.max(rect1.left, rect2.left),
|
|
||||||
top: rect1.top,
|
|
||||||
right: Math.min(rect1.right, rect2.right),
|
|
||||||
bottom: rect1.bottom
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
left: rect1.left,
|
|
||||||
top: Math.max(rect1.top, rect2.top),
|
|
||||||
right: rect1.right,
|
|
||||||
bottom: Math.min(rect1.bottom, rect2.bottom)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getContainerRect = element => {
|
|
||||||
const _rect = element.getBoundingClientRect();
|
|
||||||
const rect = {
|
|
||||||
left: _rect.left,
|
|
||||||
right: _rect.right + 10,
|
|
||||||
top: _rect.top,
|
|
||||||
bottom: _rect.bottom
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hasBiggerChild(element, "x") && !isScrollingOrHidden(element, "x")) {
|
|
||||||
const width = rect.right - rect.left;
|
|
||||||
rect.right = rect.right + element.scrollWidth - width;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasBiggerChild(element, "y") && !isScrollingOrHidden(element, "y")) {
|
|
||||||
const height = rect.bottom - rect.top;
|
|
||||||
rect.bottom = rect.bottom + element.scrollHeight - height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getScrollingAxis = element => {
|
|
||||||
const style = global.getComputedStyle(element);
|
|
||||||
const overflow = style["overflow"];
|
|
||||||
const general = overflow === "auto" || overflow === "scroll";
|
|
||||||
if (general) return "xy";
|
|
||||||
const overFlowX = style[`overflow-x`];
|
|
||||||
const xScroll = overFlowX === "auto" || overFlowX === "scroll";
|
|
||||||
const overFlowY = style[`overflow-y`];
|
|
||||||
const yScroll = overFlowY === "auto" || overFlowY === "scroll";
|
|
||||||
|
|
||||||
return `${xScroll ? "x" : ""}${yScroll ? "y" : ""}` || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isScrolling = (element, axis) => {
|
|
||||||
const style = global.getComputedStyle(element);
|
|
||||||
const overflow = style["overflow"];
|
|
||||||
const overFlowAxis = style[`overflow-${axis}`];
|
|
||||||
const general = overflow === "auto" || overflow === "scroll";
|
|
||||||
const dimensionScroll = overFlowAxis === "auto" || overFlowAxis === "scroll";
|
|
||||||
return general || dimensionScroll;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isScrollingOrHidden = (element, axis) => {
|
|
||||||
const style = global.getComputedStyle(element);
|
|
||||||
const overflow = style["overflow"];
|
|
||||||
const overFlowAxis = style[`overflow-${axis}`];
|
|
||||||
const general =
|
|
||||||
overflow === "auto" || overflow === "scroll" || overflow === "hidden";
|
|
||||||
const dimensionScroll =
|
|
||||||
overFlowAxis === "auto" ||
|
|
||||||
overFlowAxis === "scroll" ||
|
|
||||||
overFlowAxis === "hidden";
|
|
||||||
return general || dimensionScroll;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hasBiggerChild = (element, axis) => {
|
|
||||||
if (axis === "x") {
|
|
||||||
return element.scrollWidth > element.clientWidth;
|
|
||||||
} else {
|
|
||||||
return element.scrollHeight > element.clientHeight;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hasScrollBar = (element, axis) => {
|
|
||||||
return hasBiggerChild(element, axis) && isScrolling(element, axis);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getVisibleRect = (element, elementRect) => {
|
|
||||||
let currentElement = element;
|
|
||||||
let rect = elementRect || getContainerRect(element);
|
|
||||||
currentElement = element.parentElement;
|
|
||||||
while (currentElement) {
|
|
||||||
if (
|
|
||||||
hasBiggerChild(currentElement, "x") &&
|
|
||||||
isScrollingOrHidden(currentElement, "x")
|
|
||||||
) {
|
|
||||||
rect = getIntersectionOnAxis(
|
|
||||||
rect,
|
|
||||||
currentElement.getBoundingClientRect(),
|
|
||||||
"x"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
hasBiggerChild(currentElement, "y") &&
|
|
||||||
isScrollingOrHidden(currentElement, "y")
|
|
||||||
) {
|
|
||||||
rect = getIntersectionOnAxis(
|
|
||||||
rect,
|
|
||||||
currentElement.getBoundingClientRect(),
|
|
||||||
"y"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentElement = currentElement.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const listenScrollParent = (element, clb) => {
|
|
||||||
let scrollers = [];
|
|
||||||
const dispose = () => {
|
|
||||||
scrollers.forEach(p => {
|
|
||||||
p.removeEventListener("scroll", clb);
|
|
||||||
});
|
|
||||||
global.removeEventListener("scroll", clb);
|
|
||||||
};
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
let currentElement = element;
|
|
||||||
while (currentElement) {
|
|
||||||
if (
|
|
||||||
isScrolling(currentElement, "x") ||
|
|
||||||
isScrolling(currentElement, "y")
|
|
||||||
) {
|
|
||||||
currentElement.addEventListener("scroll", clb);
|
|
||||||
scrollers.push(currentElement);
|
|
||||||
}
|
|
||||||
currentElement = currentElement.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
global.addEventListener("scroll", clb);
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dispose
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hasParent = (element, parent) => {
|
|
||||||
let current = element;
|
|
||||||
while (current) {
|
|
||||||
if (current === parent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
current = current.parentElement;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getParent = (element, selector) => {
|
|
||||||
let current = element;
|
|
||||||
while (current) {
|
|
||||||
if (current.matches(selector)) {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
current = current.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hasClass = (element, cls) => {
|
|
||||||
return (
|
|
||||||
element.className
|
|
||||||
.split(" ")
|
|
||||||
.map(p => p)
|
|
||||||
.indexOf(cls) > -1
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addClass = (element, cls) => {
|
|
||||||
if (element) {
|
|
||||||
element.className = element.className || ''
|
|
||||||
const classes = element.className.split(" ").filter(p => p);
|
|
||||||
if (classes.indexOf(cls) === -1) {
|
|
||||||
classes.unshift(cls);
|
|
||||||
element.className = classes.join(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeClass = (element, cls) => {
|
|
||||||
if (element) {
|
|
||||||
const classes = element.className.split(" ").filter(p => p && p !== cls);
|
|
||||||
element.className = classes.join(" ");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const debounce = (fn, delay, immediate) => {
|
|
||||||
let timer = null;
|
|
||||||
return (...params) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
if (immediate && !timer) {
|
|
||||||
fn.call(this, ...params);
|
|
||||||
} else {
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
timer = null;
|
|
||||||
fn.call(this, ...params);
|
|
||||||
}, delay);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeChildAt = (parent, index) => {
|
|
||||||
return parent.removeChild(parent.children[index]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addChildAt = (parent, child, index) => {
|
|
||||||
if (index >= parent.children.lenght) {
|
|
||||||
parent.appendChild(child);
|
|
||||||
} else {
|
|
||||||
parent.insertBefore(child, parent.children[index]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isMobile = () => {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
if (
|
|
||||||
global.navigator.userAgent.match(/Android/i) ||
|
|
||||||
global.navigator.userAgent.match(/webOS/i) ||
|
|
||||||
global.navigator.userAgent.match(/iPhone/i) ||
|
|
||||||
global.navigator.userAgent.match(/iPad/i) ||
|
|
||||||
global.navigator.userAgent.match(/iPod/i) ||
|
|
||||||
global.navigator.userAgent.match(/BlackBerry/i) ||
|
|
||||||
global.navigator.userAgent.match(/Windows Phone/i)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const clearSelection = () => {
|
|
||||||
if (global.getSelection) {
|
|
||||||
if (global.getSelection().empty) {
|
|
||||||
// Chrome
|
|
||||||
global.getSelection().empty();
|
|
||||||
} else if (global.getSelection().removeAllRanges) {
|
|
||||||
// Firefox
|
|
||||||
global.getSelection().removeAllRanges();
|
|
||||||
}
|
|
||||||
} else if (global.document.selection) {
|
|
||||||
// IE?
|
|
||||||
global.document.selection.empty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getElementCursor = (element) => {
|
|
||||||
if (element) {
|
|
||||||
const style = global.getComputedStyle(element);
|
|
||||||
if (style) {
|
|
||||||
return style.cursor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -82,30 +82,12 @@ export const StyleVertical = styled.div`
|
|||||||
// TODO ? This is the question. We need the same drag-zone we get in horizontal mode
|
// TODO ? This is the question. We need the same drag-zone we get in horizontal mode
|
||||||
min-height: 50px; // Not needed, just for extra landing space
|
min-height: 50px; // Not needed, just for extra landing space
|
||||||
}
|
}
|
||||||
.smooth-dnd-container.horizontal {
|
|
||||||
// TODO: This is what is currently providing us multi row cols, and may need to be adjusted with new DND Library
|
|
||||||
display: flex; /* Allows wrapping */
|
|
||||||
flex-wrap: wrap; /* Allows wrapping */
|
|
||||||
//background-color: yellow !important;
|
|
||||||
}
|
|
||||||
.smooth-dnd-ghost {
|
|
||||||
//background-color: red !important;
|
|
||||||
}
|
|
||||||
.react-trello-card {
|
.react-trello-card {
|
||||||
//background-color: orange !important;
|
//background-color: orange !important;
|
||||||
margin: 5px;
|
|
||||||
// TODO: This is what is currently providing us multi row cols, and may need to be adjusted with new DND Library
|
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
}
|
}
|
||||||
.smooth-dnd-stretcher-element {
|
|
||||||
//background-color: purple !important;
|
|
||||||
}
|
|
||||||
.smooth-dnd-draggable-wrapper {
|
|
||||||
//background-color: blue !important;
|
|
||||||
flex: 0 1 auto; /* Allows items to grow and shrink */
|
|
||||||
}
|
|
||||||
.react-trello-board {
|
.react-trello-board {
|
||||||
overflow-y: hidden !important;
|
display: flex;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user