@@ -55,7 +55,6 @@ export function ProductionBoardKanbanComponent({
|
|||||||
const [isMoving, setIsMoving] = useState(false);
|
const [isMoving, setIsMoving] = useState(false);
|
||||||
|
|
||||||
const [orientation, setOrientation] = useState("horizontal");
|
const [orientation, setOrientation] = useState("horizontal");
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -223,7 +222,7 @@ export function ProductionBoardKanbanComponent({
|
|||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
Card: (cardProps) => ProductionBoardCard({ card: cardProps, technician, bodyshop, cardSettings }),
|
Card: (cardProps) => ProductionBoardCard({ card: cardProps, technician, bodyshop, cardSettings }),
|
||||||
LaneHeader: cardSettings.stickyheader ? StickyHeader : NormalHeader
|
LaneHeader: cardSettings.stickyheader && orientation === "horizontal" ? StickyHeader : NormalHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -255,9 +254,8 @@ export function ProductionBoardKanbanComponent({
|
|||||||
<StickyContainer>
|
<StickyContainer>
|
||||||
<Board
|
<Board
|
||||||
data={boardLanes}
|
data={boardLanes}
|
||||||
draggable
|
|
||||||
handleDragEnd={handleDragEnd}
|
handleDragEnd={handleDragEnd}
|
||||||
style={{ height: "100%", backgroundColor: "transparent" }}
|
style={{ height: "100%", backgroundColor: "transparent", overflowY: "auto" }}
|
||||||
components={components}
|
components={components}
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
collapsibleLanes
|
collapsibleLanes
|
||||||
@@ -267,9 +265,8 @@ export function ProductionBoardKanbanComponent({
|
|||||||
<div>
|
<div>
|
||||||
<Board
|
<Board
|
||||||
data={boardLanes}
|
data={boardLanes}
|
||||||
draggable
|
|
||||||
handleDragEnd={handleDragEnd}
|
handleDragEnd={handleDragEnd}
|
||||||
style={{ backgroundColor: "transparent" }}
|
style={{ backgroundColor: "transparent", overflowY: "auto" }}
|
||||||
components={components}
|
components={components}
|
||||||
collapsibleLanes
|
collapsibleLanes
|
||||||
orientation={orientation}
|
orientation={orientation}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ const Board = ({ id, className, components, orientation, ...additionalProps }) =
|
|||||||
const [storeId] = useState(id || v1());
|
const [storeId] = useState(id || v1());
|
||||||
|
|
||||||
const allClassNames = classNames("react-trello-board", className || "");
|
const allClassNames = classNames("react-trello-board", className || "");
|
||||||
|
|
||||||
const Styles = orientation === "horizontal" ? components.GlobalStyleHorizontal : components.GlobalStyleVertical;
|
const Styles = orientation === "horizontal" ? components.GlobalStyleHorizontal : components.GlobalStyleVertical;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Styles />
|
<Styles />
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { bindActionCreators } from "redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import Container from "../dnd/Container";
|
import Container from "../dnd/Container";
|
||||||
import Draggable from "../dnd/Draggable";
|
import Draggable from "../dnd/Draggable";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
@@ -8,220 +7,214 @@ import pick from "lodash/pick";
|
|||||||
import isEqual from "lodash/isEqual";
|
import isEqual from "lodash/isEqual";
|
||||||
import Lane from "./Lane";
|
import Lane from "./Lane";
|
||||||
import { PopoverWrapper } from "react-popopo";
|
import { PopoverWrapper } from "react-popopo";
|
||||||
|
|
||||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||||
|
|
||||||
class BoardContainer extends Component {
|
const BoardContainer = (props) => {
|
||||||
state = {
|
const [addLaneMode, setAddLaneMode] = useState(false);
|
||||||
addLaneMode: false
|
|
||||||
};
|
|
||||||
|
|
||||||
get groupName() {
|
const {
|
||||||
const { id } = this.props;
|
id,
|
||||||
return `TrelloBoard${id}`;
|
components,
|
||||||
}
|
data,
|
||||||
|
draggable,
|
||||||
|
laneDraggable,
|
||||||
|
laneDragClass,
|
||||||
|
laneDropClass,
|
||||||
|
style,
|
||||||
|
onDataChange,
|
||||||
|
onCardAdd,
|
||||||
|
onCardUpdate,
|
||||||
|
onCardClick,
|
||||||
|
onBeforeCardDelete,
|
||||||
|
onCardDelete,
|
||||||
|
onLaneScroll,
|
||||||
|
onLaneClick,
|
||||||
|
onLaneAdd,
|
||||||
|
onLaneDelete,
|
||||||
|
onLaneUpdate,
|
||||||
|
editable,
|
||||||
|
canAddLanes,
|
||||||
|
laneStyle,
|
||||||
|
onCardMoveAcrossLanes,
|
||||||
|
orientation,
|
||||||
|
eventBusHandle,
|
||||||
|
handleLaneDragStart,
|
||||||
|
handleLaneDragEnd,
|
||||||
|
...otherProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
componentDidMount() {
|
const dispatch = useDispatch();
|
||||||
const { actions, eventBusHandle } = this.props;
|
const reducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
|
||||||
actions.loadBoard(this.props.data);
|
|
||||||
if (eventBusHandle) {
|
|
||||||
this.wireEventBus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
const groupName = `TrelloBoard${id}`;
|
||||||
const { data, reducerData, onDataChange, actions } = this.props;
|
|
||||||
|
|
||||||
if (this.props.reducerData && !isEqual(reducerData, prevProps.reducerData)) {
|
const wireEventBus = useCallback(() => {
|
||||||
onDataChange(this.props.reducerData);
|
const eventBus = {
|
||||||
}
|
|
||||||
|
|
||||||
if (data && !isEqual(data, prevProps.data)) {
|
|
||||||
actions.loadBoard(data);
|
|
||||||
onDataChange(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDragStart = ({ payload }) => {
|
|
||||||
const { handleLaneDragStart } = this.props;
|
|
||||||
handleLaneDragStart(payload.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
onLaneDrop = ({ removedIndex, addedIndex, payload }) => {
|
|
||||||
const { actions, handleLaneDragEnd } = this.props;
|
|
||||||
if (removedIndex !== addedIndex) {
|
|
||||||
actions.moveLane({ oldIndex: removedIndex, newIndex: addedIndex });
|
|
||||||
handleLaneDragEnd(removedIndex, addedIndex, payload);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
getCardDetails = (laneId, cardIndex) => {
|
|
||||||
return this.props.reducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
|
||||||
};
|
|
||||||
|
|
||||||
getLaneDetails = (index) => {
|
|
||||||
return this.props.reducerData.lanes[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
wireEventBus = () => {
|
|
||||||
const { actions, eventBusHandle } = this.props;
|
|
||||||
let eventBus = {
|
|
||||||
publish: (event) => {
|
publish: (event) => {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "ADD_CARD":
|
case "ADD_CARD":
|
||||||
return actions.addCard({ laneId: event.laneId, card: event.card });
|
return dispatch(actions.addCard({ laneId: event.laneId, card: event.card }));
|
||||||
// Note: Removed because there was a duplicate entry
|
|
||||||
// case "UPDATE_CARD":
|
|
||||||
// return actions.updateCard({ laneId: event.laneId, card: event.card });
|
|
||||||
case "REMOVE_CARD":
|
case "REMOVE_CARD":
|
||||||
return actions.removeCard({ laneId: event.laneId, cardId: event.cardId });
|
return dispatch(actions.removeCard({ laneId: event.laneId, cardId: event.cardId }));
|
||||||
case "REFRESH_BOARD":
|
case "REFRESH_BOARD":
|
||||||
return actions.loadBoard(event.data);
|
return dispatch(actions.loadBoard(event.data));
|
||||||
case "MOVE_CARD":
|
case "MOVE_CARD":
|
||||||
return actions.moveCardAcrossLanes({
|
return dispatch(
|
||||||
fromLaneId: event.fromLaneId,
|
actions.moveCardAcrossLanes({
|
||||||
toLaneId: event.toLaneId,
|
fromLaneId: event.fromLaneId,
|
||||||
cardId: event.cardId,
|
toLaneId: event.toLaneId,
|
||||||
index: event.index
|
cardId: event.cardId,
|
||||||
});
|
index: event.index
|
||||||
|
})
|
||||||
|
);
|
||||||
case "UPDATE_CARDS":
|
case "UPDATE_CARDS":
|
||||||
return actions.updateCards({ laneId: event.laneId, cards: event.cards });
|
return dispatch(actions.updateCards({ laneId: event.laneId, cards: event.cards }));
|
||||||
case "UPDATE_CARD":
|
case "UPDATE_CARD":
|
||||||
return actions.updateCard({ laneId: event.laneId, updatedCard: event.card });
|
return dispatch(actions.updateCard({ laneId: event.laneId, updatedCard: event.card }));
|
||||||
case "UPDATE_LANES":
|
case "UPDATE_LANES":
|
||||||
return actions.updateLanes(event.lanes);
|
return dispatch(actions.updateLanes(event.lanes));
|
||||||
case "UPDATE_LANE":
|
case "UPDATE_LANE":
|
||||||
return actions.updateLane(event.lane);
|
return dispatch(actions.updateLane(event.lane));
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
eventBusHandle(eventBus);
|
eventBusHandle(eventBus);
|
||||||
|
}, [dispatch, eventBusHandle]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(actions.loadBoard(data));
|
||||||
|
if (eventBusHandle) {
|
||||||
|
wireEventBus();
|
||||||
|
}
|
||||||
|
}, [data, eventBusHandle, dispatch, wireEventBus]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEqual(reducerData, props.reducerData)) {
|
||||||
|
onDataChange(reducerData);
|
||||||
|
}
|
||||||
|
}, [reducerData, props.reducerData, onDataChange]);
|
||||||
|
|
||||||
|
const onDragStart = useCallback(
|
||||||
|
({ payload }) => {
|
||||||
|
handleLaneDragStart(payload.id);
|
||||||
|
},
|
||||||
|
[handleLaneDragStart]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLaneDrop = useCallback(
|
||||||
|
({ removedIndex, addedIndex, payload }) => {
|
||||||
|
if (removedIndex !== addedIndex) {
|
||||||
|
dispatch(actions.moveLane({ oldIndex: removedIndex, newIndex: addedIndex }));
|
||||||
|
handleLaneDragEnd(removedIndex, addedIndex, payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[dispatch, handleLaneDragEnd]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getCardDetails = useCallback(
|
||||||
|
(laneId, cardIndex) => {
|
||||||
|
return reducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
||||||
|
},
|
||||||
|
[reducerData]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getLaneDetails = useCallback(
|
||||||
|
(index) => {
|
||||||
|
return reducerData.lanes[index];
|
||||||
|
},
|
||||||
|
[reducerData]
|
||||||
|
);
|
||||||
|
|
||||||
|
const hideEditableLane = () => {
|
||||||
|
setAddLaneMode(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// + add
|
const showEditableLane = () => {
|
||||||
hideEditableLane = () => {
|
setAddLaneMode(true);
|
||||||
this.setState({ addLaneMode: false });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showEditableLane = () => {
|
const addNewLane = (params) => {
|
||||||
this.setState({ addLaneMode: true });
|
hideEditableLane();
|
||||||
|
dispatch(actions.addLane(params));
|
||||||
|
onLaneAdd(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
addNewLane = (params) => {
|
const passThroughProps = pick(props, [
|
||||||
this.hideEditableLane();
|
"onCardMoveAcrossLanes",
|
||||||
this.props.actions.addLane(params);
|
"onLaneScroll",
|
||||||
this.props.onLaneAdd(params);
|
"onLaneDelete",
|
||||||
};
|
"onLaneUpdate",
|
||||||
|
"onCardClick",
|
||||||
|
"onBeforeCardDelete",
|
||||||
|
"onCardDelete",
|
||||||
|
"onCardAdd",
|
||||||
|
"onCardUpdate",
|
||||||
|
"onLaneClick",
|
||||||
|
"laneSortFunction",
|
||||||
|
"draggable",
|
||||||
|
"laneDraggable",
|
||||||
|
"cardDraggable",
|
||||||
|
"collapsibleLanes",
|
||||||
|
"canAddLanes",
|
||||||
|
"hideCardDeleteIcon",
|
||||||
|
"tagStyle",
|
||||||
|
"handleDragStart",
|
||||||
|
"handleDragEnd",
|
||||||
|
"cardDragClass",
|
||||||
|
"editLaneTitle",
|
||||||
|
"orientation"
|
||||||
|
]);
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const {
|
<components.BoardWrapper style={style} orientation={orientation} draggable={false}>
|
||||||
id,
|
<PopoverWrapper>
|
||||||
components,
|
<Container
|
||||||
reducerData,
|
orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
||||||
draggable,
|
onDragStart={onDragStart}
|
||||||
laneDraggable,
|
dragClass={laneDragClass}
|
||||||
laneDragClass,
|
dropClass={laneDropClass}
|
||||||
laneDropClass,
|
onDrop={onLaneDrop}
|
||||||
style,
|
lockAxis={orientation === "vertical" ? "y" : "x"}
|
||||||
onDataChange,
|
getChildPayload={(index) => getLaneDetails(index)}
|
||||||
onCardAdd,
|
groupName={groupName}
|
||||||
onCardUpdate,
|
>
|
||||||
onCardClick,
|
{reducerData.lanes.map((lane, index) => {
|
||||||
onBeforeCardDelete,
|
const { id, droppable, ...otherProps } = lane;
|
||||||
onCardDelete,
|
const laneToRender = (
|
||||||
onLaneScroll,
|
<Lane
|
||||||
onLaneClick,
|
key={id}
|
||||||
onLaneAdd,
|
boardId={groupName}
|
||||||
onLaneDelete,
|
components={components}
|
||||||
onLaneUpdate,
|
id={id}
|
||||||
editable,
|
getCardDetails={getCardDetails}
|
||||||
canAddLanes,
|
index={index}
|
||||||
laneStyle,
|
droppable={droppable === undefined ? true : droppable}
|
||||||
onCardMoveAcrossLanes,
|
style={laneStyle || lane.style || {}}
|
||||||
t,
|
labelStyle={lane.labelStyle || {}}
|
||||||
orientation,
|
cardStyle={props.cardStyle || lane.cardStyle}
|
||||||
...otherProps
|
editable={editable && !lane.disallowAddingCard}
|
||||||
} = this.props;
|
{...otherProps}
|
||||||
|
{...passThroughProps}
|
||||||
const { addLaneMode } = this.state;
|
/>
|
||||||
// Stick to whitelisting attributes to segregate board and lane props
|
);
|
||||||
const passThroughProps = pick(this.props, [
|
return draggable && laneDraggable ? <Draggable key={lane.id}>{laneToRender}</Draggable> : laneToRender;
|
||||||
"onCardMoveAcrossLanes",
|
})}
|
||||||
"onLaneScroll",
|
</Container>
|
||||||
"onLaneDelete",
|
</PopoverWrapper>
|
||||||
"onLaneUpdate",
|
{canAddLanes && (
|
||||||
"onCardClick",
|
<Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>
|
||||||
"onBeforeCardDelete",
|
{editable && !addLaneMode ? (
|
||||||
"onCardDelete",
|
<components.NewLaneSection onClick={showEditableLane} />
|
||||||
"onCardAdd",
|
) : (
|
||||||
"onCardUpdate",
|
addLaneMode && <components.NewLaneForm onCancel={hideEditableLane} onAdd={addNewLane} />
|
||||||
"onLaneClick",
|
)}
|
||||||
"laneSortFunction",
|
</Container>
|
||||||
"draggable",
|
)}
|
||||||
"laneDraggable",
|
</components.BoardWrapper>
|
||||||
"cardDraggable",
|
);
|
||||||
"collapsibleLanes",
|
};
|
||||||
"canAddLanes",
|
|
||||||
"hideCardDeleteIcon",
|
|
||||||
"tagStyle",
|
|
||||||
"handleDragStart",
|
|
||||||
"handleDragEnd",
|
|
||||||
"cardDragClass",
|
|
||||||
"editLaneTitle",
|
|
||||||
"orientation"
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<components.BoardWrapper style={style} orientation={orientation} {...otherProps} draggable={false}>
|
|
||||||
<PopoverWrapper>
|
|
||||||
<Container
|
|
||||||
orientation={orientation === "vertical" ? "vertical" : "horizontal"}
|
|
||||||
onDragStart={this.onDragStart}
|
|
||||||
dragClass={laneDragClass}
|
|
||||||
dropClass={laneDropClass}
|
|
||||||
onDrop={this.onLaneDrop}
|
|
||||||
lockAxis={orientation === "vertical" ? "y" : "x"}
|
|
||||||
getChildPayload={(index) => this.getLaneDetails(index)}
|
|
||||||
groupName={this.groupName}
|
|
||||||
>
|
|
||||||
{reducerData.lanes.map((lane, index) => {
|
|
||||||
const { id, droppable, ...otherProps } = lane;
|
|
||||||
const laneToRender = (
|
|
||||||
<Lane
|
|
||||||
key={id}
|
|
||||||
boardId={this.groupName}
|
|
||||||
components={components}
|
|
||||||
id={id}
|
|
||||||
getCardDetails={this.getCardDetails}
|
|
||||||
index={index}
|
|
||||||
droppable={droppable === undefined ? true : droppable}
|
|
||||||
style={laneStyle || lane.style || {}}
|
|
||||||
labelStyle={lane.labelStyle || {}}
|
|
||||||
cardStyle={this.props.cardStyle || lane.cardStyle}
|
|
||||||
editable={editable && !lane.disallowAddingCard}
|
|
||||||
{...otherProps}
|
|
||||||
{...passThroughProps}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return draggable && laneDraggable ? <Draggable key={lane.id}>{laneToRender}</Draggable> : laneToRender;
|
|
||||||
})}
|
|
||||||
</Container>
|
|
||||||
</PopoverWrapper>
|
|
||||||
{canAddLanes && (
|
|
||||||
<Container orientation={orientation === "vertical" ? "vertical" : "horizontal"}>
|
|
||||||
{editable && !addLaneMode ? (
|
|
||||||
<components.NewLaneSection onClick={this.showEditableLane} />
|
|
||||||
) : (
|
|
||||||
addLaneMode && <components.NewLaneForm onCancel={this.hideEditableLane} onAdd={this.addNewLane} t={t} />
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
)}
|
|
||||||
</components.BoardWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BoardContainer.propTypes = {
|
BoardContainer.propTypes = {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
@@ -259,12 +252,10 @@ BoardContainer.propTypes = {
|
|||||||
laneDragClass: PropTypes.string,
|
laneDragClass: PropTypes.string,
|
||||||
laneDropClass: PropTypes.string,
|
laneDropClass: PropTypes.string,
|
||||||
onCardMoveAcrossLanes: PropTypes.func.isRequired,
|
onCardMoveAcrossLanes: PropTypes.func.isRequired,
|
||||||
t: PropTypes.func,
|
|
||||||
orientation: PropTypes.string
|
orientation: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
BoardContainer.defaultProps = {
|
BoardContainer.defaultProps = {
|
||||||
t: (v) => v,
|
|
||||||
onDataChange: () => {},
|
onDataChange: () => {},
|
||||||
handleDragStart: () => {},
|
handleDragStart: () => {},
|
||||||
handleDragEnd: () => {},
|
handleDragEnd: () => {},
|
||||||
@@ -284,16 +275,8 @@ BoardContainer.defaultProps = {
|
|||||||
cardDraggable: true,
|
cardDraggable: true,
|
||||||
cardDragClass: "react_trello_dragClass",
|
cardDragClass: "react_trello_dragClass",
|
||||||
laneDragClass: "react_trello_dragLaneClass",
|
laneDragClass: "react_trello_dragLaneClass",
|
||||||
laneDropClass: "",
|
laneDropClass: "react_trello_dragLaneDropClass",
|
||||||
orientation: "horizontal"
|
orientation: "horizontal"
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
export default BoardContainer;
|
||||||
return state.trello.lanes ? { reducerData: state.trello } : {};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
actions: bindActionCreators({ ...actions }, dispatch)
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(BoardContainer);
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { bindActionCreators } from "redux";
|
import { bindActionCreators } from "redux";
|
||||||
@@ -12,140 +12,183 @@ import Draggable from "../dnd/Draggable.jsx";
|
|||||||
|
|
||||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||||
|
|
||||||
class Lane extends Component {
|
const defaultProps = {
|
||||||
state = {
|
style: {},
|
||||||
loading: false,
|
titleStyle: {},
|
||||||
currentPage: this.props.currentPage,
|
labelStyle: {},
|
||||||
addCardMode: false,
|
label: undefined,
|
||||||
collapsed: false,
|
editable: false,
|
||||||
isDraggingOver: false
|
onLaneUpdate: () => {},
|
||||||
};
|
onCardAdd: () => {},
|
||||||
|
onCardUpdate: () => {},
|
||||||
|
onCardDelete: () => {},
|
||||||
|
onBeforeCardDelete: () => {},
|
||||||
|
onLaneDelete: () => {},
|
||||||
|
onLaneClick: () => {},
|
||||||
|
onLaneScroll: () => {},
|
||||||
|
onCardClick: () => {},
|
||||||
|
onCardMoveAcrossLanes: () => {},
|
||||||
|
draggable: false,
|
||||||
|
laneDraggable: false,
|
||||||
|
cardDraggable: true,
|
||||||
|
collapsibleLanes: false,
|
||||||
|
droppable: true,
|
||||||
|
canAddLanes: false,
|
||||||
|
hideCardDeleteIcon: false,
|
||||||
|
components: {},
|
||||||
|
handleDragStart: () => {},
|
||||||
|
handleDragEnd: () => {},
|
||||||
|
orientation: "vertical"
|
||||||
|
};
|
||||||
|
|
||||||
get groupName() {
|
const Lane = (internalProps) => {
|
||||||
const { boardId } = this.props;
|
const props = useMemo(() => ({ ...defaultProps, ...internalProps }), [internalProps]);
|
||||||
return `TrelloBoard${boardId}Lane`;
|
const [loading, setLoading] = useState(false);
|
||||||
}
|
const [currentPage, setCurrentPage] = useState(props.currentPage);
|
||||||
|
const [addCardMode, setAddCardMode] = useState(false);
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const [isDraggingOver, setIsDraggingOver] = useState(false);
|
||||||
|
|
||||||
handleScroll = (evt) => {
|
const laneRef = useRef(null);
|
||||||
const node = evt.target;
|
|
||||||
const elemScrollPosition = node.scrollHeight - node.scrollTop - node.clientHeight;
|
useEffect(() => {
|
||||||
const { onLaneScroll } = this.props;
|
if (!isEqual(props.cards, currentPage)) {
|
||||||
// In some browsers and/or screen sizes a decimal rest value between 0 and 1 exists, so it should be checked on < 1 instead of < 0
|
setCurrentPage(props.currentPage);
|
||||||
if (elemScrollPosition < 1 && onLaneScroll && !this.state.loading) {
|
|
||||||
const { currentPage } = this.state;
|
|
||||||
this.setState({ loading: true });
|
|
||||||
const nextPage = currentPage + 1;
|
|
||||||
onLaneScroll(nextPage, this.props.id).then((moreCards) => {
|
|
||||||
if ((moreCards || []).length > 0) {
|
|
||||||
this.props.actions.paginateLane({
|
|
||||||
laneId: this.props.id,
|
|
||||||
newCards: moreCards,
|
|
||||||
nextPage: nextPage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.setState({ loading: false });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
}, [props.cards, props.currentPage, currentPage]);
|
||||||
|
|
||||||
sortCards(cards, sortFunction) {
|
const handleScroll = useCallback(
|
||||||
|
(evt) => {
|
||||||
|
const node = evt.target;
|
||||||
|
const elemScrollPosition = node.scrollHeight - node.scrollTop - node.clientHeight;
|
||||||
|
const { onLaneScroll } = props;
|
||||||
|
if (elemScrollPosition < 1 && onLaneScroll && !loading) {
|
||||||
|
const nextPage = currentPage + 1;
|
||||||
|
setLoading(true);
|
||||||
|
onLaneScroll(nextPage, props.id).then((moreCards) => {
|
||||||
|
if ((moreCards || []).length > 0) {
|
||||||
|
props.actions.paginateLane({
|
||||||
|
laneId: props.id,
|
||||||
|
newCards: moreCards,
|
||||||
|
nextPage: nextPage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currentPage, loading, props]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const node = laneRef.current;
|
||||||
|
if (node) {
|
||||||
|
node.addEventListener("scroll", handleScroll);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (node) {
|
||||||
|
node.removeEventListener("scroll", handleScroll);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [handleScroll]);
|
||||||
|
|
||||||
|
const sortCards = (cards, sortFunction) => {
|
||||||
if (!cards) return [];
|
if (!cards) return [];
|
||||||
if (!sortFunction) return cards;
|
if (!sortFunction) return cards;
|
||||||
return cards.concat().sort(function (card1, card2) {
|
return cards.concat().sort((card1, card2) => sortFunction(card1, card2));
|
||||||
return sortFunction(card1, card2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
laneDidMount = (node) => {
|
|
||||||
if (node) {
|
|
||||||
node.addEventListener("scroll", this.handleScroll);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static getDerivedStateFromProps(nextProps, prevState) {
|
const removeCard = (cardId) => {
|
||||||
if (!isEqual(prevState.cards, nextProps.cards)) {
|
if (props.onBeforeCardDelete && typeof props.onBeforeCardDelete === "function") {
|
||||||
return {
|
props.onBeforeCardDelete(() => {
|
||||||
currentPage: nextProps.currentPage
|
props.onCardDelete && props.onCardDelete(cardId, props.id);
|
||||||
};
|
props.actions.removeCard({ laneId: props.id, cardId: cardId });
|
||||||
}
|
|
||||||
// Return null if the state hasn't changed
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCard = (cardId) => {
|
|
||||||
if (this.props.onBeforeCardDelete && typeof this.props.onBeforeCardDelete === "function") {
|
|
||||||
this.props.onBeforeCardDelete(() => {
|
|
||||||
this.props.onCardDelete && this.props.onCardDelete(cardId, this.props.id);
|
|
||||||
this.props.actions.removeCard({ laneId: this.props.id, cardId: cardId });
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.props.onCardDelete && this.props.onCardDelete(cardId, this.props.id);
|
props.onCardDelete && props.onCardDelete(cardId, props.id);
|
||||||
this.props.actions.removeCard({ laneId: this.props.id, cardId: cardId });
|
props.actions.removeCard({ laneId: props.id, cardId: cardId });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCardClick = (e, card) => {
|
const handleCardClick = (e, card) => {
|
||||||
const { onCardClick } = this.props;
|
const { onCardClick } = props;
|
||||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
showEditableCard = () => {
|
const showEditableCard = () => {
|
||||||
this.setState({ addCardMode: true });
|
setAddCardMode(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
hideEditableCard = () => {
|
const hideEditableCard = () => {
|
||||||
this.setState({ addCardMode: false });
|
setAddCardMode(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
addNewCard = (params) => {
|
const addNewCard = (params) => {
|
||||||
const laneId = this.props.id;
|
const laneId = props.id;
|
||||||
const id = v1();
|
const id = v1();
|
||||||
this.hideEditableCard();
|
hideEditableCard();
|
||||||
let card = { id, ...params };
|
let card = { id, ...params };
|
||||||
this.props.actions.addCard({ laneId, card });
|
props.actions.addCard({ laneId, card });
|
||||||
this.props.onCardAdd(card, laneId);
|
props.onCardAdd(card, laneId);
|
||||||
};
|
};
|
||||||
|
|
||||||
onDragStart = ({ payload }) => {
|
const onDragStart = ({ payload }) => {
|
||||||
const { handleDragStart } = this.props;
|
const { handleDragStart } = props;
|
||||||
handleDragStart && handleDragStart(payload.id, payload.laneId);
|
handleDragStart && handleDragStart(payload.id, payload.laneId);
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldAcceptDrop = (sourceContainerOptions) => {
|
const shouldAcceptDrop = (sourceContainerOptions) => {
|
||||||
return this.props.droppable && sourceContainerOptions.groupName === this.groupName;
|
return props.droppable && sourceContainerOptions.groupName === groupName;
|
||||||
};
|
};
|
||||||
|
|
||||||
onDragEnd = (laneId, result) => {
|
const onDragEnd = (laneId, result) => {
|
||||||
const { handleDragEnd } = this.props;
|
const { handleDragEnd } = props;
|
||||||
const { addedIndex, payload } = result;
|
const { addedIndex, payload } = result;
|
||||||
|
|
||||||
if (this.state.isDraggingOver) {
|
if (isDraggingOver) {
|
||||||
this.setState({ isDraggingOver: false });
|
setIsDraggingOver(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addedIndex != null) {
|
if (addedIndex != null) {
|
||||||
const newCard = { ...cloneDeep(payload), laneId };
|
const newCard = { ...cloneDeep(payload), laneId };
|
||||||
const response = handleDragEnd ? handleDragEnd(payload.id, payload.laneId, laneId, addedIndex, newCard) : true;
|
const response = handleDragEnd ? handleDragEnd(payload.id, payload.laneId, laneId, addedIndex, newCard) : true;
|
||||||
if (response === undefined || !!response) {
|
if (response === undefined || !!response) {
|
||||||
this.props.actions.moveCardAcrossLanes({
|
props.actions.moveCardAcrossLanes({
|
||||||
fromLaneId: payload.laneId,
|
fromLaneId: payload.laneId,
|
||||||
toLaneId: laneId,
|
toLaneId: laneId,
|
||||||
cardId: payload.id,
|
cardId: payload.id,
|
||||||
index: addedIndex
|
index: addedIndex
|
||||||
});
|
});
|
||||||
this.props.onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex);
|
props.onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex);
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateCard = (updatedCard) => {
|
const updateCard = (updatedCard) => {
|
||||||
this.props.actions.updateCard({ laneId: this.props.id, card: updatedCard });
|
props.actions.updateCard({ laneId: props.id, card: updatedCard });
|
||||||
this.props.onCardUpdate(this.props.id, updatedCard);
|
props.onCardUpdate(props.id, updatedCard);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDragContainer = (isDraggingOver) => {
|
const removeLane = () => {
|
||||||
|
const { id } = props;
|
||||||
|
props.actions.removeLane({ laneId: id });
|
||||||
|
props.onLaneDelete(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTitle = (value) => {
|
||||||
|
props.actions.updateLane({ id: props.id, title: value });
|
||||||
|
props.onLaneUpdate(props.id, { title: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleLaneCollapsed = () => {
|
||||||
|
props.collapsibleLanes && setCollapsed(!collapsed);
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupName = `TrelloBoard${props.boardId}Lane`;
|
||||||
|
|
||||||
|
const renderDragContainer = (isDraggingOver) => {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
cards,
|
cards,
|
||||||
@@ -159,13 +202,12 @@ class Lane extends Component {
|
|||||||
cardStyle,
|
cardStyle,
|
||||||
components,
|
components,
|
||||||
orientation
|
orientation
|
||||||
} = this.props;
|
} = props;
|
||||||
const { addCardMode, collapsed } = this.state;
|
|
||||||
|
|
||||||
const stableCards = collapsed ? [] : cards;
|
const stableCards = collapsed ? [] : cards;
|
||||||
|
|
||||||
const cardList = this.sortCards(stableCards, laneSortFunction).map((card, idx) => {
|
const cardList = sortCards(stableCards, laneSortFunction).map((card, idx) => {
|
||||||
const onDeleteCard = () => this.removeCard(card.id);
|
const onDeleteCard = () => removeCard(card.id);
|
||||||
const cardToRender = (
|
const cardToRender = (
|
||||||
<components.Card
|
<components.Card
|
||||||
key={card.id}
|
key={card.id}
|
||||||
@@ -173,8 +215,8 @@ class Lane extends Component {
|
|||||||
style={card.style || cardStyle}
|
style={card.style || cardStyle}
|
||||||
className="react-trello-card"
|
className="react-trello-card"
|
||||||
onDelete={onDeleteCard}
|
onDelete={onDeleteCard}
|
||||||
onClick={(e) => this.handleCardClick(e, card)}
|
onClick={(e) => handleCardClick(e, card)}
|
||||||
onChange={(updatedCard) => this.updateCard(updatedCard)}
|
onChange={(updatedCard) => updateCard(updatedCard)}
|
||||||
showDeleteButton={!hideCardDeleteIcon}
|
showDeleteButton={!hideCardDeleteIcon}
|
||||||
tagStyle={tagStyle}
|
tagStyle={tagStyle}
|
||||||
cardDraggable={cardDraggable}
|
cardDraggable={cardDraggable}
|
||||||
@@ -190,93 +232,59 @@ class Lane extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.ScrollableLane ref={this.laneDidMount} isDraggingOver={isDraggingOver}>
|
<components.ScrollableLane ref={laneRef} isDraggingOver={isDraggingOver}>
|
||||||
<Container
|
<Container
|
||||||
orientation={orientation === "horizontal" ? "vertical" : "horizontal"}
|
orientation={orientation === "horizontal" ? "vertical" : "horizontal"}
|
||||||
groupName={this.groupName}
|
groupName={groupName}
|
||||||
dragClass={cardDragClass}
|
dragClass={cardDragClass}
|
||||||
dropClass={cardDropClass}
|
dropClass={cardDropClass}
|
||||||
onDragStart={this.onDragStart}
|
onDragStart={onDragStart}
|
||||||
onDrop={(e) => this.onDragEnd(id, e)}
|
onDrop={(e) => onDragEnd(id, e)}
|
||||||
onDragEnter={() => this.setState({ isDraggingOver: true })}
|
onDragEnter={() => setIsDraggingOver(true)}
|
||||||
onDragLeave={() => this.setState({ isDraggingOver: false })}
|
onDragLeave={() => setIsDraggingOver(false)}
|
||||||
shouldAcceptDrop={this.shouldAcceptDrop}
|
shouldAcceptDrop={shouldAcceptDrop}
|
||||||
getChildPayload={(index) => this.props.getCardDetails(id, index)}
|
getChildPayload={(index) => props.getCardDetails(id, index)}
|
||||||
>
|
>
|
||||||
{cardList}
|
{cardList}
|
||||||
</Container>
|
</Container>
|
||||||
{editable && !addCardMode && <components.AddCardLink onClick={this.showEditableCard} laneId={id} />}
|
{editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />}
|
||||||
{addCardMode && <components.NewCardForm onCancel={this.hideEditableCard} laneId={id} onAdd={this.addNewCard} />}
|
{addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />}
|
||||||
</components.ScrollableLane>
|
</components.ScrollableLane>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
removeLane = () => {
|
const renderHeader = (pickedProps) => {
|
||||||
const { id } = this.props;
|
const { components } = props;
|
||||||
this.props.actions.removeLane({ laneId: id });
|
|
||||||
this.props.onLaneDelete(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTitle = (value) => {
|
|
||||||
this.props.actions.updateLane({ id: this.props.id, title: value });
|
|
||||||
this.props.onLaneUpdate(this.props.id, { title: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
renderHeader = (pickedProps) => {
|
|
||||||
const { components } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<components.LaneHeader
|
<components.LaneHeader
|
||||||
{...pickedProps}
|
{...pickedProps}
|
||||||
onDelete={this.removeLane}
|
onDelete={removeLane}
|
||||||
onDoubleClick={this.toggleLaneCollapsed}
|
onDoubleClick={toggleLaneCollapsed}
|
||||||
updateTitle={this.updateTitle}
|
updateTitle={updateTitle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleLaneCollapsed = () => {
|
const { id, cards, collapsibleLanes, components, onLaneClick, orientation, ...otherProps } = props;
|
||||||
this.props.collapsibleLanes && this.setState((state) => ({ collapsed: !state.collapsed }));
|
const allClassNames = classNames("react-trello-lane", collapsed ? "lane-collapsed" : "", props.className || "");
|
||||||
};
|
const showFooter = collapsibleLanes && cards.length > 0;
|
||||||
|
|
||||||
render() {
|
// Removed the ...otherProps spread from the components.Section
|
||||||
const { loading, isDraggingOver, collapsed } = this.state;
|
return (
|
||||||
const {
|
<components.Section
|
||||||
id,
|
key={id}
|
||||||
cards,
|
onClick={() => onLaneClick && onLaneClick(id)}
|
||||||
collapsibleLanes,
|
draggable={false}
|
||||||
components,
|
className={allClassNames}
|
||||||
onLaneClick,
|
orientation={orientation}
|
||||||
onLaneScroll,
|
>
|
||||||
onCardClick,
|
{renderHeader({ id, cards, ...otherProps })}
|
||||||
onCardAdd,
|
{renderDragContainer(isDraggingOver)}
|
||||||
onBeforeCardDelete,
|
{loading && <components.Loader />}
|
||||||
onCardDelete,
|
{showFooter && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
||||||
onLaneDelete,
|
</components.Section>
|
||||||
onLaneUpdate,
|
);
|
||||||
onCardUpdate,
|
};
|
||||||
onCardMoveAcrossLanes,
|
|
||||||
orientation,
|
|
||||||
...otherProps
|
|
||||||
} = this.props;
|
|
||||||
const allClassNames = classNames("react-trello-lane", this.props.className || "");
|
|
||||||
const showFooter = collapsibleLanes && cards.length > 0;
|
|
||||||
return (
|
|
||||||
<components.Section
|
|
||||||
{...otherProps}
|
|
||||||
key={id}
|
|
||||||
onClick={() => onLaneClick && onLaneClick(id)}
|
|
||||||
draggable={false}
|
|
||||||
className={allClassNames}
|
|
||||||
orientation={orientation}
|
|
||||||
>
|
|
||||||
{this.renderHeader({ id, cards, ...otherProps })}
|
|
||||||
{this.renderDragContainer(isDraggingOver)}
|
|
||||||
{loading && <components.Loader />}
|
|
||||||
{showFooter && <components.LaneFooter onClick={this.toggleLaneCollapsed} collapsed={collapsed} />}
|
|
||||||
</components.Section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Lane.propTypes = {
|
Lane.propTypes = {
|
||||||
actions: PropTypes.object,
|
actions: PropTypes.object,
|
||||||
@@ -320,35 +328,6 @@ Lane.propTypes = {
|
|||||||
orientation: PropTypes.string
|
orientation: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
Lane.defaultProps = {
|
|
||||||
style: {},
|
|
||||||
titleStyle: {},
|
|
||||||
labelStyle: {},
|
|
||||||
label: undefined,
|
|
||||||
editable: false,
|
|
||||||
onLaneUpdate: () => {},
|
|
||||||
onCardAdd: () => {},
|
|
||||||
onCardUpdate: () => {},
|
|
||||||
onCardDelete: () => {},
|
|
||||||
onBeforeCardDelete: () => {},
|
|
||||||
onLaneDelete: () => {},
|
|
||||||
onLaneClick: () => {},
|
|
||||||
onLaneScroll: () => {},
|
|
||||||
onCardClick: () => {},
|
|
||||||
onCardMoveAcrossLanes: () => {},
|
|
||||||
draggable: false,
|
|
||||||
laneDraggable: false,
|
|
||||||
cardDraggable: true,
|
|
||||||
collapsibleLanes: false,
|
|
||||||
droppable: true,
|
|
||||||
canAddLanes: false,
|
|
||||||
hideCardDeleteIcon: false,
|
|
||||||
components: {},
|
|
||||||
handleDragStart: () => {},
|
|
||||||
handleDragEnd: () => {},
|
|
||||||
orientation: "vertical"
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
actions: bindActionCreators(actions, dispatch)
|
actions: bindActionCreators(actions, dispatch)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,52 +51,27 @@ class Container extends Component {
|
|||||||
|
|
||||||
getContainerOptions() {
|
getContainerOptions() {
|
||||||
const functionProps = {};
|
const functionProps = {};
|
||||||
|
const propKeys = [
|
||||||
|
"onDragStart",
|
||||||
|
"onDragEnd",
|
||||||
|
"onDrop",
|
||||||
|
"getChildPayload",
|
||||||
|
"shouldAnimateDrop",
|
||||||
|
"shouldAcceptDrop",
|
||||||
|
"onDragEnter",
|
||||||
|
"onDragLeave",
|
||||||
|
"render",
|
||||||
|
"onDropReady",
|
||||||
|
"getGhostParent"
|
||||||
|
];
|
||||||
|
|
||||||
if (this.props.onDragStart) {
|
propKeys.forEach((key) => {
|
||||||
functionProps.onDragStart = (...p) => this.props.onDragStart(...p);
|
if (this.props[key]) {
|
||||||
}
|
functionProps[key] = (...p) => this.props[key](...p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (this.props.onDragEnd) {
|
return { ...this.props, ...functionProps };
|
||||||
functionProps.onDragEnd = (...p) => this.props.onDragEnd(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onDrop) {
|
|
||||||
functionProps.onDrop = (...p) => this.props.onDrop(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.getChildPayload) {
|
|
||||||
functionProps.getChildPayload = (...p) => this.props.getChildPayload(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.shouldAnimateDrop) {
|
|
||||||
functionProps.shouldAnimateDrop = (...p) => this.props.shouldAnimateDrop(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.shouldAcceptDrop) {
|
|
||||||
functionProps.shouldAcceptDrop = (...p) => this.props.shouldAcceptDrop(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onDragEnter) {
|
|
||||||
functionProps.onDragEnter = (...p) => this.props.onDragEnter(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onDragLeave) {
|
|
||||||
functionProps.onDragLeave = (...p) => this.props.onDragLeave(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.render) {
|
|
||||||
functionProps.render = (...p) => this.props.render(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.onDropReady) {
|
|
||||||
functionProps.onDropReady = (...p) => this.props.onDropReady(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.getGhostParent) {
|
|
||||||
functionProps.getGhostParent = (...p) => this.props.getGhostParent(...p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign({}, this.props, functionProps);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
import React, {Component} from 'react'
|
import React, { Component } from "react";
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from "prop-types";
|
||||||
import {constants} from 'kuika-smooth-dnd'
|
import { constants } from "kuika-smooth-dnd";
|
||||||
|
|
||||||
const {wrapperClass} = constants
|
const { wrapperClass } = constants;
|
||||||
|
|
||||||
class Draggable extends Component {
|
class Draggable extends Component {
|
||||||
render() {
|
render() {
|
||||||
if (this.props.render) {
|
const { render, className, children, ...restProps } = this.props;
|
||||||
return React.cloneElement(this.props.render(), {className: wrapperClass})
|
|
||||||
}
|
|
||||||
|
|
||||||
const clsName = `${this.props.className ? this.props.className + ' ' : ''}`
|
try {
|
||||||
return (
|
if (render) {
|
||||||
<div {...this.props} className={`${clsName}${wrapperClass}`}>
|
return React.cloneElement(render(), { className: wrapperClass });
|
||||||
{this.props.children}
|
}
|
||||||
</div>
|
|
||||||
)
|
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 = {
|
Draggable.propTypes = {
|
||||||
render: PropTypes.func
|
render: PropTypes.func,
|
||||||
}
|
className: PropTypes.string,
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
export default Draggable
|
export default Draggable;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import update from "immutability-helper";
|
import update from "immutability-helper";
|
||||||
|
|
||||||
|
const updateLanes = (state, lanes) => update(state, { lanes: { $set: lanes } });
|
||||||
|
|
||||||
|
const updateLaneCards = (lane, cards) => update(lane, { cards: { $set: cards } });
|
||||||
|
|
||||||
const LaneHelper = {
|
const LaneHelper = {
|
||||||
initialiseLanes: (state, { lanes }) => {
|
initialiseLanes: (state, { lanes }) => {
|
||||||
const newLanes = lanes.map((lane) => {
|
const newLanes = lanes.map((lane) => {
|
||||||
@@ -7,13 +11,13 @@ const LaneHelper = {
|
|||||||
lane.cards && lane.cards.forEach((c) => (c.laneId = lane.id));
|
lane.cards && lane.cards.forEach((c) => (c.laneId = lane.id));
|
||||||
return lane;
|
return lane;
|
||||||
});
|
});
|
||||||
return update(state, { lanes: { $set: newLanes } });
|
return updateLanes(state, newLanes);
|
||||||
},
|
},
|
||||||
|
|
||||||
paginateLane: (state, { laneId, newCards, nextPage }) => {
|
paginateLane: (state, { laneId, newCards, nextPage }) => {
|
||||||
const updatedLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: newCards });
|
const updatedLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: newCards });
|
||||||
updatedLanes.find((lane) => lane.id === laneId).currentPage = nextPage;
|
updatedLanes.find((lane) => lane.id === laneId).currentPage = nextPage;
|
||||||
return update(state, { lanes: { $set: updatedLanes } });
|
return updateLanes(state, updatedLanes);
|
||||||
},
|
},
|
||||||
|
|
||||||
appendCardsToLane: (state, { laneId, newCards, index }) => {
|
appendCardsToLane: (state, { laneId, newCards, index }) => {
|
||||||
@@ -23,12 +27,11 @@ const LaneHelper = {
|
|||||||
.filter((c) => lane.cards.find((card) => card.id === c.id) == null);
|
.filter((c) => lane.cards.find((card) => card.id === c.id) == null);
|
||||||
return state.lanes.map((lane) => {
|
return state.lanes.map((lane) => {
|
||||||
if (lane.id === laneId) {
|
if (lane.id === laneId) {
|
||||||
if (index !== undefined) {
|
const cardsToUpdate =
|
||||||
return update(lane, { cards: { $splice: [[index, 0, ...newCards]] } });
|
index !== undefined
|
||||||
} else {
|
? [...lane.cards.slice(0, index), ...newCards, ...lane.cards.slice(index)]
|
||||||
const cardsToUpdate = [...lane.cards, ...newCards];
|
: [...lane.cards, ...newCards];
|
||||||
return update(lane, { cards: { $set: cardsToUpdate } });
|
return updateLaneCards(lane, cardsToUpdate);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return lane;
|
return lane;
|
||||||
}
|
}
|
||||||
@@ -37,35 +40,29 @@ const LaneHelper = {
|
|||||||
|
|
||||||
appendCardToLane: (state, { laneId, card, index }) => {
|
appendCardToLane: (state, { laneId, card, index }) => {
|
||||||
const newLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: [card], index });
|
const newLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: [card], index });
|
||||||
return update(state, { lanes: { $set: newLanes } });
|
return updateLanes(state, newLanes);
|
||||||
},
|
},
|
||||||
|
|
||||||
addLane: (state, lane) => {
|
addLane: (state, lane) => {
|
||||||
const newLane = { cards: [], ...lane };
|
const newLane = { cards: [], ...lane };
|
||||||
return update(state, { lanes: { $push: [newLane] } });
|
return updateLanes(state, [...state.lanes, newLane]);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateLane: (state, updatedLane) => {
|
updateLane: (state, updatedLane) => {
|
||||||
const newLanes = state.lanes.map((lane) => {
|
const newLanes = state.lanes.map((lane) => (updatedLane.id === lane.id ? { ...lane, ...updatedLane } : lane));
|
||||||
if (updatedLane.id === lane.id) {
|
return updateLanes(state, newLanes);
|
||||||
return { ...lane, ...updatedLane };
|
|
||||||
} else {
|
|
||||||
return lane;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return update(state, { lanes: { $set: newLanes } });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
removeCardFromLane: (state, { laneId, cardId }) => {
|
removeCardFromLane: (state, { laneId, cardId }) => {
|
||||||
const lanes = state.lanes.map((lane) => {
|
const lanes = state.lanes.map((lane) => {
|
||||||
if (lane.id === laneId) {
|
if (lane.id === laneId) {
|
||||||
let newCards = lane.cards.filter((card) => card.id !== cardId);
|
const newCards = lane.cards.filter((card) => card.id !== cardId);
|
||||||
return update(lane, { cards: { $set: newCards } });
|
return updateLaneCards(lane, newCards);
|
||||||
} else {
|
} else {
|
||||||
return lane;
|
return lane;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return update(state, { lanes: { $set: lanes } });
|
return updateLanes(state, lanes);
|
||||||
},
|
},
|
||||||
|
|
||||||
moveCardAcrossLanes: (state, { fromLaneId, toLaneId, cardId, index }) => {
|
moveCardAcrossLanes: (state, { fromLaneId, toLaneId, cardId, index }) => {
|
||||||
@@ -74,50 +71,36 @@ const LaneHelper = {
|
|||||||
if (lane.id === fromLaneId) {
|
if (lane.id === fromLaneId) {
|
||||||
cardToMove = lane.cards.find((card) => card.id === cardId);
|
cardToMove = lane.cards.find((card) => card.id === cardId);
|
||||||
const newCards = lane.cards.filter((card) => card.id !== cardId);
|
const newCards = lane.cards.filter((card) => card.id !== cardId);
|
||||||
return update(lane, { cards: { $set: newCards } });
|
return updateLaneCards(lane, newCards);
|
||||||
} else {
|
} else {
|
||||||
return lane;
|
return lane;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const updatedState = update(state, { lanes: { $set: interimLanes } });
|
return LaneHelper.appendCardToLane(
|
||||||
return LaneHelper.appendCardToLane(updatedState, {
|
{ ...state, lanes: interimLanes },
|
||||||
laneId: toLaneId,
|
{
|
||||||
card: cardToMove,
|
laneId: toLaneId,
|
||||||
index: index
|
card: cardToMove,
|
||||||
});
|
index: index
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCardsForLane: (state, { laneId, cards }) => {
|
updateCardsForLane: (state, { laneId, cards }) => {
|
||||||
const lanes = state.lanes.map((lane) => {
|
const lanes = state.lanes.map((lane) => (lane.id === laneId ? updateLaneCards(lane, cards) : lane));
|
||||||
if (lane.id === laneId) {
|
return updateLanes(state, lanes);
|
||||||
return update(lane, { cards: { $set: cards } });
|
|
||||||
} else {
|
|
||||||
return lane;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return update(state, { lanes: { $set: lanes } });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateCardForLane: (state, { laneId, card: updatedCard }) => {
|
updateCardForLane: (state, { laneId, card: updatedCard }) => {
|
||||||
const lanes = state.lanes.map((lane) => {
|
const lanes = state.lanes.map((lane) => {
|
||||||
if (lane.id === laneId) {
|
if (lane.id === laneId) {
|
||||||
const cards = lane.cards.map((card) => {
|
const cards = lane.cards.map((card) => (card.id === updatedCard.id ? { ...card, ...updatedCard } : card));
|
||||||
if (card.id === updatedCard.id) {
|
return updateLaneCards(lane, cards);
|
||||||
return { ...card, ...updatedCard };
|
|
||||||
} else {
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return update(lane, { cards: { $set: cards } });
|
|
||||||
} else {
|
} else {
|
||||||
return lane;
|
return lane;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return update(state, { lanes: { $set: lanes } });
|
return updateLanes(state, lanes);
|
||||||
},
|
|
||||||
|
|
||||||
updateLanes: (state, lanes) => {
|
|
||||||
return { ...state, ...{ lanes: lanes } };
|
|
||||||
},
|
},
|
||||||
|
|
||||||
moveLane: (state, { oldIndex, newIndex }) => {
|
moveLane: (state, { oldIndex, newIndex }) => {
|
||||||
@@ -128,7 +111,7 @@ const LaneHelper = {
|
|||||||
|
|
||||||
removeLane: (state, { laneId }) => {
|
removeLane: (state, { laneId }) => {
|
||||||
const updatedLanes = state.lanes.filter((lane) => lane.id !== laneId);
|
const updatedLanes = state.lanes.filter((lane) => lane.id !== laneId);
|
||||||
return update(state, { lanes: { $set: updatedLanes } });
|
return updateLanes(state, updatedLanes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export const GlobalStyleHorizontal = createGlobalStyle`
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GlobalStyleVertical = createGlobalStyle`
|
export const GlobalStyleVertical = createGlobalStyle`
|
||||||
.comPlainTextContentEditable {
|
.comPlainTextContentEditable {
|
||||||
-webkit-user-modify: read-write-plaintext-only;
|
-webkit-user-modify: read-write-plaintext-only;
|
||||||
@@ -101,7 +100,8 @@ export const GlobalStyleVertical = createGlobalStyle`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.smooth-dnd-container {
|
.smooth-dnd-container {
|
||||||
//min-height: 100px; // Not needed, just for extra landing space
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-trello-lane {
|
.react-trello-lane {
|
||||||
@@ -186,9 +186,9 @@ export const Header = styled.header`
|
|||||||
export const Section = styled.section`
|
export const Section = styled.section`
|
||||||
background-color: #e3e3e3;
|
background-color: #e3e3e3;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 5px 5px;
|
margin: 2px 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
${getSectionStyles};
|
${getSectionStyles};
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { DelButton, DeleteWrapper } from "../styles/Elements";
|
import { DeleteWrapper } from "../styles/Elements";
|
||||||
|
import { Button } from "antd";
|
||||||
|
|
||||||
const DeleteButton = (props) => {
|
const DeleteButton = (props) => {
|
||||||
return (
|
return (
|
||||||
<DeleteWrapper {...props}>
|
<DeleteWrapper {...props}>
|
||||||
<DelButton>✖</DelButton>
|
<Button type="primary" danger>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
</DeleteWrapper>
|
</DeleteWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user