- 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-sticky": "^6.0.3",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"react-virtuoso": "^4.7.11",
|
||||
"recharts": "^2.12.7",
|
||||
"redux": "^5.0.1",
|
||||
"redux-actions": "^3.0.0",
|
||||
@@ -15888,6 +15889,18 @@
|
||||
"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": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"react-router-dom": "^6.23.1",
|
||||
"react-sticky": "^6.0.3",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"react-virtuoso": "^4.7.11",
|
||||
"recharts": "^2.12.7",
|
||||
"redux": "^5.0.1",
|
||||
"redux-actions": "^3.0.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { groupBy } from "lodash";
|
||||
import fakeData from "./testData/board300.json";
|
||||
import fakeData from "./testData/board1200.json";
|
||||
|
||||
const sortByParentId = (arr) => {
|
||||
// return arr.reduce((accumulator, currentValue) => {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
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 pick from "lodash/pick";
|
||||
import isEqual from "lodash/isEqual";
|
||||
@@ -151,6 +155,10 @@ const BoardContainer = ({
|
||||
[dispatch, handleLaneDragEnd]
|
||||
);
|
||||
|
||||
const onDragEnd = (...params) => {
|
||||
console.dir(params);
|
||||
};
|
||||
|
||||
const getCardDetails = useCallback(
|
||||
(laneId, cardIndex) => {
|
||||
return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
||||
@@ -239,22 +247,25 @@ const BoardContainer = ({
|
||||
]
|
||||
);
|
||||
|
||||
let cardIndex = 0;
|
||||
|
||||
return (
|
||||
<components.BoardWrapper style={style} orientation={orientation} draggable={false}>
|
||||
<PopoverWrapper>
|
||||
<Container
|
||||
orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
||||
onDragStart={onDragStart}
|
||||
dragClass={laneDragClass}
|
||||
dropClass={laneDropClass}
|
||||
onDrop={onLaneDrop}
|
||||
lockAxis={orientation === "vertical" ? "y" : "x"}
|
||||
getChildPayload={(index) => getLaneDetails(index)}
|
||||
groupName={groupName}
|
||||
<DragDropContext
|
||||
onDragEnd={onDragEnd}
|
||||
// orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
||||
// onDragStart={onDragStart}
|
||||
// dragClass={laneDragClass}
|
||||
// dropClass={laneDropClass}
|
||||
// onDrop={onLaneDrop}
|
||||
// lockAxis={orientation === "vertical" ? "y" : "x"}
|
||||
// getChildPayload={(index) => getLaneDetails(index)}
|
||||
// groupName={groupName}
|
||||
>
|
||||
{currentReducerData.lanes.map((lane, index) => {
|
||||
const { id, droppable, ...laneOtherProps } = lane;
|
||||
const laneToRender = (
|
||||
return (
|
||||
<Lane
|
||||
key={id}
|
||||
boardId={groupName}
|
||||
@@ -269,21 +280,21 @@ const BoardContainer = ({
|
||||
editable={editable && !lane.disallowAddingCard}
|
||||
{...laneOtherProps}
|
||||
{...passThroughProps}
|
||||
cards={lane.cards.map((card) => ({ ...card, idx: cardIndex++ }))}
|
||||
/>
|
||||
);
|
||||
return draggable || laneDraggable ? <Draggable key={lane.id}>{laneToRender}</Draggable> : laneToRender;
|
||||
})}
|
||||
</Container>
|
||||
</DragDropContext>
|
||||
</PopoverWrapper>
|
||||
{canAddLanes && (
|
||||
<Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>
|
||||
{editable && !addLaneMode ? (
|
||||
<components.NewLaneSection onClick={showEditableLane} />
|
||||
) : (
|
||||
addLaneMode && <components.NewLaneForm onCancel={hideEditableLane} onAdd={addNewLane} />
|
||||
)}
|
||||
</Container>
|
||||
)}
|
||||
{/*{canAddLanes && (*/}
|
||||
{/* <Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>*/}
|
||||
{/* {editable && !addLaneMode ? (*/}
|
||||
{/* <components.NewLaneSection onClick={showEditableLane} />*/}
|
||||
{/* ) : (*/}
|
||||
{/* addLaneMode && <components.NewLaneForm onCancel={hideEditableLane} onAdd={addNewLane} />*/}
|
||||
{/* )}*/}
|
||||
{/* </Container>*/}
|
||||
{/*)}*/}
|
||||
</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 PropTypes from "prop-types";
|
||||
import { bindActionCreators } from "redux";
|
||||
@@ -7,10 +7,8 @@ import isEqual from "lodash/isEqual";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
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 { Droppable, Draggable } from "../dnd/lib/index.js";
|
||||
|
||||
/**
|
||||
* 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 flexStyle = useMemo(() => {
|
||||
return orientation === "vertical"
|
||||
? {
|
||||
display: "flex",
|
||||
flexWrap: "wrap"
|
||||
}
|
||||
: {};
|
||||
}, [orientation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEqual(cards, currentPageFinal)) {
|
||||
setCurrentPageFinal(currentPage);
|
||||
@@ -171,6 +178,7 @@ function Lane({
|
||||
};
|
||||
|
||||
const handleCardClick = (e, card) => {
|
||||
console.log("hit");
|
||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||
e.stopPropagation();
|
||||
};
|
||||
@@ -265,32 +273,58 @@ function Lane({
|
||||
{...card}
|
||||
/>
|
||||
);
|
||||
|
||||
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>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<components.ScrollableLane ref={laneRef} isDraggingOver={isDraggingOver}>
|
||||
<Container
|
||||
orientation={orientation === "horizontal" ? "vertical" : "horizontal"}
|
||||
groupName={groupName}
|
||||
dragClass={cardDragClass}
|
||||
dropClass={cardDropClass}
|
||||
onDragStart={onDragStart}
|
||||
onDrop={(e) => onDragEnd(id, e)}
|
||||
onDragEnter={() => setIsDraggingOver(true)}
|
||||
onDragLeave={() => setIsDraggingOver(false)}
|
||||
shouldAcceptDrop={shouldAcceptDrop}
|
||||
getChildPayload={(index) => getCardDetails(id, index)}
|
||||
>
|
||||
{cardList}
|
||||
</Container>
|
||||
{editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />}
|
||||
{addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />}
|
||||
</components.ScrollableLane>
|
||||
<Droppable
|
||||
direction={orientation === "horizontal" ? "vertical" : "grid"}
|
||||
droppableId={`lane-${index}`}
|
||||
// dragClass={cardDragClass}
|
||||
// dropClass={cardDropClass}
|
||||
// onDragStart={onDragStart}
|
||||
// onDrop={(e) => onDragEnd(id, e)}
|
||||
// onDragEnter={() => setIsDraggingOver(true)}
|
||||
// onDragLeave={() => setIsDraggingOver(false)}
|
||||
// shouldAcceptDrop={shouldAcceptDrop}
|
||||
// getChildPayload={(index) => getCardDetails(id, index)}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
style={{
|
||||
...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 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 = (() => {
|
||||
if (!closest) {
|
||||
return null;
|
||||
@@ -54,3 +54,5 @@ export default ({ descriptor, isEnabled, isCombineEnabled, isFixedOnPage, direct
|
||||
};
|
||||
return dimension;
|
||||
};
|
||||
|
||||
export default getDroppable;
|
||||
|
||||
@@ -306,7 +306,6 @@ const ConnectedDraggable = connect(
|
||||
// Using our own context for the store to avoid clashing with consumers
|
||||
context: StoreContext,
|
||||
// Default value, but being really clear
|
||||
pure: true,
|
||||
// When pure, compares the result of mapStateToProps to its previous value.
|
||||
// Default value: shallowEqual
|
||||
// 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
|
||||
context: StoreContext,
|
||||
// pure: true is default value, but being really clear
|
||||
pure: true,
|
||||
|
||||
// When pure, compares the result of mapStateToProps to its previous value.
|
||||
// Default value: shallowEqual
|
||||
// Switching to a strictEqual as we return a memoized object on changes
|
||||
areStatePropsEqual: isStrictEqual
|
||||
}
|
||||
)(Droppable);
|
||||
|
||||
ConnectedDroppable.defaultProps = defaultProps;
|
||||
export default ConnectedDroppable;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
import Draggable from "./dnd/Draggable.jsx";
|
||||
import Container from "./dnd/Container.jsx";
|
||||
import BoardContainer from "./controllers/BoardContainer.jsx";
|
||||
import Board from "./controllers/Board.jsx";
|
||||
import Lane from "./controllers/Lane.jsx";
|
||||
@@ -12,7 +10,7 @@ import widgets from "./widgets/index";
|
||||
import { StyleSheetManager } from "styled-components";
|
||||
import isPropValid from "@emotion/is-prop-valid";
|
||||
|
||||
export { Draggable, Container, BoardContainer, Lane, widgets };
|
||||
export { BoardContainer, Lane, widgets };
|
||||
|
||||
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
|
||||
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 {
|
||||
//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;
|
||||
}
|
||||
.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 {
|
||||
overflow-y: hidden !important;
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user