Board Container and Lane, the last remaining class components are now functional components utilizing up to date react stuff, defaultProps deprecation fixed (rolled into function decleration)

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-05-21 17:12:48 -04:00
parent 55d729339f
commit 19dfec2a34
2 changed files with 220 additions and 185 deletions

View File

@@ -9,42 +9,42 @@ 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";
const BoardContainer = (props) => { const BoardContainer = ({
id,
components,
data,
draggable = false,
laneDraggable = true,
laneDragClass = "react_trello_dragLaneClass",
laneDropClass = "react_trello_dragLaneDropClass",
style,
onDataChange = () => {},
onCardAdd = () => {},
onCardUpdate = () => {},
onCardClick = () => {},
onBeforeCardDelete = () => {},
onCardDelete = () => {},
onLaneScroll = () => {},
onLaneClick = () => {},
onLaneAdd = () => {},
onLaneDelete = () => {},
onLaneUpdate = () => {},
editable = false,
canAddLanes = false,
laneStyle,
onCardMoveAcrossLanes = () => {},
orientation = "horizontal",
eventBusHandle,
handleLaneDragStart = () => {},
handleLaneDragEnd = () => {},
reducerData,
cardStyle,
...otherProps
}) => {
const [addLaneMode, setAddLaneMode] = useState(false); const [addLaneMode, setAddLaneMode] = useState(false);
const {
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;
const dispatch = useDispatch(); const dispatch = useDispatch();
const reducerData = useSelector((state) => (state.trello.lanes ? state.trello : {})); const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
const groupName = `TrelloBoard${id}`; const groupName = `TrelloBoard${id}`;
@@ -91,10 +91,10 @@ const BoardContainer = (props) => {
}, [data, eventBusHandle, dispatch, wireEventBus]); }, [data, eventBusHandle, dispatch, wireEventBus]);
useEffect(() => { useEffect(() => {
if (!isEqual(reducerData, props.reducerData)) { if (!isEqual(currentReducerData, reducerData)) {
onDataChange(reducerData); onDataChange(currentReducerData);
} }
}, [reducerData, props.reducerData, onDataChange]); }, [currentReducerData, reducerData, onDataChange]);
const onDragStart = useCallback( const onDragStart = useCallback(
({ payload }) => { ({ payload }) => {
@@ -115,16 +115,16 @@ const BoardContainer = (props) => {
const getCardDetails = useCallback( const getCardDetails = useCallback(
(laneId, cardIndex) => { (laneId, cardIndex) => {
return reducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex]; return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
}, },
[reducerData] [currentReducerData]
); );
const getLaneDetails = useCallback( const getLaneDetails = useCallback(
(index) => { (index) => {
return reducerData.lanes[index]; return currentReducerData.lanes[index];
}, },
[reducerData] [currentReducerData]
); );
const hideEditableLane = () => { const hideEditableLane = () => {
@@ -141,31 +141,65 @@ const BoardContainer = (props) => {
onLaneAdd(params); onLaneAdd(params);
}; };
const passThroughProps = pick(props, [ const passThroughProps = pick(
"onCardMoveAcrossLanes", {
"onLaneScroll", id,
"onLaneDelete", components,
"onLaneUpdate", data,
"onCardClick", draggable,
"onBeforeCardDelete", laneDraggable,
"onCardDelete", laneDragClass,
"onCardAdd", laneDropClass,
"onCardUpdate", style,
"onLaneClick", onDataChange,
"laneSortFunction", onCardAdd,
"draggable", onCardUpdate,
"laneDraggable", onCardClick,
"cardDraggable", onBeforeCardDelete,
"collapsibleLanes", onCardDelete,
"canAddLanes", onLaneScroll,
"hideCardDeleteIcon", onLaneClick,
"tagStyle", onLaneAdd,
"handleDragStart", onLaneDelete,
"handleDragEnd", onLaneUpdate,
"cardDragClass", editable,
"editLaneTitle", canAddLanes,
"orientation" laneStyle,
]); onCardMoveAcrossLanes,
orientation,
eventBusHandle,
handleLaneDragStart,
handleLaneDragEnd,
reducerData,
cardStyle,
...otherProps
},
[
"onCardMoveAcrossLanes",
"onLaneScroll",
"onLaneDelete",
"onLaneUpdate",
"onCardClick",
"onBeforeCardDelete",
"onCardDelete",
"onCardAdd",
"onCardUpdate",
"onLaneClick",
"laneSortFunction",
"draggable",
"laneDraggable",
"cardDraggable",
"collapsibleLanes",
"canAddLanes",
"hideCardDeleteIcon",
"tagStyle",
"handleDragStart",
"handleDragEnd",
"cardDragClass",
"editLaneTitle",
"orientation"
]
);
return ( return (
<components.BoardWrapper style={style} orientation={orientation} draggable={false}> <components.BoardWrapper style={style} orientation={orientation} draggable={false}>
@@ -180,8 +214,8 @@ const BoardContainer = (props) => {
getChildPayload={(index) => getLaneDetails(index)} getChildPayload={(index) => getLaneDetails(index)}
groupName={groupName} groupName={groupName}
> >
{reducerData.lanes.map((lane, index) => { {currentReducerData.lanes.map((lane, index) => {
const { id, droppable, ...otherProps } = lane; const { id, droppable, ...laneOtherProps } = lane;
const laneToRender = ( const laneToRender = (
<Lane <Lane
key={id} key={id}
@@ -193,9 +227,9 @@ const BoardContainer = (props) => {
droppable={droppable === undefined ? true : droppable} droppable={droppable === undefined ? true : droppable}
style={laneStyle || lane.style || {}} style={laneStyle || lane.style || {}}
labelStyle={lane.labelStyle || {}} labelStyle={lane.labelStyle || {}}
cardStyle={props.cardStyle || lane.cardStyle} cardStyle={cardStyle || lane.cardStyle}
editable={editable && !lane.disallowAddingCard} editable={editable && !lane.disallowAddingCard}
{...otherProps} {...laneOtherProps}
{...passThroughProps} {...passThroughProps}
/> />
); );
@@ -251,32 +285,9 @@ BoardContainer.propTypes = {
cardDragClass: PropTypes.string, cardDragClass: PropTypes.string,
laneDragClass: PropTypes.string, laneDragClass: PropTypes.string,
laneDropClass: PropTypes.string, laneDropClass: PropTypes.string,
onCardMoveAcrossLanes: PropTypes.func.isRequired, onCardMoveAcrossLanes: PropTypes.func,
orientation: PropTypes.string orientation: PropTypes.string,
}; cardStyle: PropTypes.object
BoardContainer.defaultProps = {
onDataChange: () => {},
handleDragStart: () => {},
handleDragEnd: () => {},
handleLaneDragStart: () => {},
handleLaneDragEnd: () => {},
onCardUpdate: () => {},
onLaneAdd: () => {},
onLaneDelete: () => {},
onCardMoveAcrossLanes: () => {},
onLaneUpdate: () => {},
editable: false,
canAddLanes: false,
hideCardDeleteIcon: false,
draggable: false,
collapsibleLanes: false,
laneDraggable: true,
cardDraggable: true,
cardDragClass: "react_trello_dragClass",
laneDragClass: "react_trello_dragLaneClass",
laneDropClass: "react_trello_dragLaneDropClass",
orientation: "horizontal"
}; };
export default BoardContainer; export default BoardContainer;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react"; import React, { useCallback, useEffect, useRef, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { bindActionCreators } from "redux"; import { bindActionCreators } from "redux";
@@ -12,39 +12,51 @@ import Draggable from "../dnd/Draggable.jsx";
import * as actions from "../../../redux/trello/trello.actions.js"; import * as actions from "../../../redux/trello/trello.actions.js";
const defaultProps = { function Lane({
style: {}, actions,
titleStyle: {}, id,
labelStyle: {}, boardId,
label: undefined, title,
editable: false, index,
onLaneUpdate: () => {}, laneSortFunction,
onCardAdd: () => {}, style = {},
onCardUpdate: () => {}, cardStyle = {},
onCardDelete: () => {}, tagStyle = {},
onBeforeCardDelete: () => {}, titleStyle = {},
onLaneDelete: () => {}, labelStyle = {},
onLaneClick: () => {}, cards,
onLaneScroll: () => {}, label,
onCardClick: () => {}, draggable = false,
onCardMoveAcrossLanes: () => {}, collapsibleLanes = false,
draggable: false, droppable = true,
laneDraggable: false, onCardMoveAcrossLanes = () => {},
cardDraggable: true, onCardClick = () => {},
collapsibleLanes: false, onBeforeCardDelete = () => {},
droppable: true, onCardDelete = () => {},
canAddLanes: false, onCardAdd = () => {},
hideCardDeleteIcon: false, onCardUpdate = () => {},
components: {}, onLaneDelete = () => {},
handleDragStart: () => {}, onLaneUpdate = () => {},
handleDragEnd: () => {}, onLaneClick = () => {},
orientation: "vertical" onLaneScroll = () => {},
}; editable = false,
laneDraggable = false,
const Lane = (internalProps) => { cardDraggable = true,
const props = useMemo(() => ({ ...defaultProps, ...internalProps }), [internalProps]); cardDragClass,
cardDropClass,
canAddLanes = false,
hideCardDeleteIcon = false,
components = {},
getCardDetails,
handleDragStart = () => {},
handleDragEnd = () => {},
orientation = "vertical",
className,
currentPage,
...otherProps
}) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(props.currentPage); const [currentPageFinal, setCurrentPageFinal] = useState(currentPage);
const [addCardMode, setAddCardMode] = useState(false); const [addCardMode, setAddCardMode] = useState(false);
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [isDraggingOver, setIsDraggingOver] = useState(false); const [isDraggingOver, setIsDraggingOver] = useState(false);
@@ -52,23 +64,22 @@ const Lane = (internalProps) => {
const laneRef = useRef(null); const laneRef = useRef(null);
useEffect(() => { useEffect(() => {
if (!isEqual(props.cards, currentPage)) { if (!isEqual(cards, currentPageFinal)) {
setCurrentPage(props.currentPage); setCurrentPageFinal(currentPage);
} }
}, [props.cards, props.currentPage, currentPage]); }, [cards, currentPage, currentPageFinal]);
const handleScroll = useCallback( const handleScroll = useCallback(
(evt) => { (evt) => {
const node = evt.target; const node = evt.target;
const elemScrollPosition = node.scrollHeight - node.scrollTop - node.clientHeight; const elemScrollPosition = node.scrollHeight - node.scrollTop - node.clientHeight;
const { onLaneScroll } = props;
if (elemScrollPosition < 1 && onLaneScroll && !loading) { if (elemScrollPosition < 1 && onLaneScroll && !loading) {
const nextPage = currentPage + 1; const nextPage = currentPageFinal + 1;
setLoading(true); setLoading(true);
onLaneScroll(nextPage, props.id).then((moreCards) => { onLaneScroll(nextPage, id).then((moreCards) => {
if ((moreCards || []).length > 0) { if ((moreCards || []).length > 0) {
props.actions.paginateLane({ actions.paginateLane({
laneId: props.id, laneId: id,
newCards: moreCards, newCards: moreCards,
nextPage: nextPage nextPage: nextPage
}); });
@@ -77,7 +88,7 @@ const Lane = (internalProps) => {
}); });
} }
}, },
[currentPage, loading, props] [currentPageFinal, loading, onLaneScroll, id, actions]
); );
useEffect(() => { useEffect(() => {
@@ -99,19 +110,18 @@ const Lane = (internalProps) => {
}; };
const removeCard = (cardId) => { const removeCard = (cardId) => {
if (props.onBeforeCardDelete && typeof props.onBeforeCardDelete === "function") { if (onBeforeCardDelete && typeof onBeforeCardDelete === "function") {
props.onBeforeCardDelete(() => { onBeforeCardDelete(() => {
props.onCardDelete && props.onCardDelete(cardId, props.id); onCardDelete && onCardDelete(cardId, id);
props.actions.removeCard({ laneId: props.id, cardId: cardId }); actions.removeCard({ laneId: id, cardId: cardId });
}); });
} else { } else {
props.onCardDelete && props.onCardDelete(cardId, props.id); onCardDelete && onCardDelete(cardId, id);
props.actions.removeCard({ laneId: props.id, cardId: cardId }); actions.removeCard({ laneId: id, cardId: cardId });
} }
}; };
const handleCardClick = (e, card) => { const handleCardClick = (e, card) => {
const { onCardClick } = props;
onCardClick && onCardClick(card.id, card.metadata, card.laneId); onCardClick && onCardClick(card.id, card.metadata, card.laneId);
e.stopPropagation(); e.stopPropagation();
}; };
@@ -125,25 +135,23 @@ const Lane = (internalProps) => {
}; };
const addNewCard = (params) => { const addNewCard = (params) => {
const laneId = props.id; const laneId = id;
const id = v1(); const newCardId = v1();
hideEditableCard(); hideEditableCard();
let card = { id, ...params }; let card = { id: newCardId, ...params };
props.actions.addCard({ laneId, card }); actions.addCard({ laneId, card });
props.onCardAdd(card, laneId); onCardAdd(card, laneId);
}; };
const onDragStart = ({ payload }) => { const onDragStart = ({ payload }) => {
const { handleDragStart } = props;
handleDragStart && handleDragStart(payload.id, payload.laneId); handleDragStart && handleDragStart(payload.id, payload.laneId);
}; };
const shouldAcceptDrop = (sourceContainerOptions) => { const shouldAcceptDrop = (sourceContainerOptions) => {
return props.droppable && sourceContainerOptions.groupName === groupName; return droppable && sourceContainerOptions.groupName === groupName;
}; };
const onDragEnd = (laneId, result) => { const onDragEnd = (laneId, result) => {
const { handleDragEnd } = props;
const { addedIndex, payload } = result; const { addedIndex, payload } = result;
if (isDraggingOver) { if (isDraggingOver) {
@@ -154,56 +162,40 @@ const Lane = (internalProps) => {
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) {
props.actions.moveCardAcrossLanes({ actions.moveCardAcrossLanes({
fromLaneId: payload.laneId, fromLaneId: payload.laneId,
toLaneId: laneId, toLaneId: laneId,
cardId: payload.id, cardId: payload.id,
index: addedIndex index: addedIndex
}); });
props.onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex); onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex);
} }
return response; return response;
} }
}; };
const updateCard = (updatedCard) => { const updateCard = (updatedCard) => {
props.actions.updateCard({ laneId: props.id, card: updatedCard }); actions.updateCard({ laneId: id, card: updatedCard });
props.onCardUpdate(props.id, updatedCard); onCardUpdate(id, updatedCard);
}; };
const removeLane = () => { const removeLane = () => {
const { id } = props; actions.removeLane({ laneId: id });
props.actions.removeLane({ laneId: id }); onLaneDelete(id);
props.onLaneDelete(id);
}; };
const updateTitle = (value) => { const updateTitle = (value) => {
props.actions.updateLane({ id: props.id, title: value }); actions.updateLane({ id, title: value });
props.onLaneUpdate(props.id, { title: value }); onLaneUpdate(id, { title: value });
}; };
const toggleLaneCollapsed = () => { const toggleLaneCollapsed = () => {
props.collapsibleLanes && setCollapsed(!collapsed); collapsibleLanes && setCollapsed(!collapsed);
}; };
const groupName = `TrelloBoard${props.boardId}Lane`; const groupName = `TrelloBoard${boardId}Lane`;
const renderDragContainer = (isDraggingOver) => { const renderDragContainer = (isDraggingOver) => {
const {
id,
cards,
laneSortFunction,
editable,
hideCardDeleteIcon,
cardDraggable,
cardDragClass,
cardDropClass,
tagStyle,
cardStyle,
components,
orientation
} = props;
const stableCards = collapsed ? [] : cards; const stableCards = collapsed ? [] : cards;
const cardList = sortCards(stableCards, laneSortFunction).map((card, idx) => { const cardList = sortCards(stableCards, laneSortFunction).map((card, idx) => {
@@ -243,7 +235,7 @@ const Lane = (internalProps) => {
onDragEnter={() => setIsDraggingOver(true)} onDragEnter={() => setIsDraggingOver(true)}
onDragLeave={() => setIsDraggingOver(false)} onDragLeave={() => setIsDraggingOver(false)}
shouldAcceptDrop={shouldAcceptDrop} shouldAcceptDrop={shouldAcceptDrop}
getChildPayload={(index) => props.getCardDetails(id, index)} getChildPayload={(index) => getCardDetails(id, index)}
> >
{cardList} {cardList}
</Container> </Container>
@@ -254,7 +246,6 @@ const Lane = (internalProps) => {
}; };
const renderHeader = (pickedProps) => { const renderHeader = (pickedProps) => {
const { components } = props;
return ( return (
<components.LaneHeader <components.LaneHeader
{...pickedProps} {...pickedProps}
@@ -265,11 +256,43 @@ const Lane = (internalProps) => {
); );
}; };
const { id, cards, collapsibleLanes, components, onLaneClick, orientation, ...otherProps } = props; const allClassNames = classNames("react-trello-lane", collapsed ? "lane-collapsed" : "", className || "");
const allClassNames = classNames("react-trello-lane", collapsed ? "lane-collapsed" : "", props.className || "");
const showFooter = collapsibleLanes && cards.length > 0; const showFooter = collapsibleLanes && cards.length > 0;
// Removed the ...otherProps spread from the components.Section const passedProps = {
actions,
id,
boardId,
title,
index,
laneSortFunction,
style,
cardStyle,
tagStyle,
titleStyle,
labelStyle,
cards,
label,
draggable,
collapsibleLanes,
droppable,
editable,
laneDraggable,
cardDraggable,
cardDragClass,
cardDropClass,
canAddLanes,
hideCardDeleteIcon,
components,
getCardDetails,
handleDragStart,
handleDragEnd,
orientation,
className,
currentPage,
...otherProps
};
return ( return (
<components.Section <components.Section
key={id} key={id}
@@ -277,14 +300,15 @@ const Lane = (internalProps) => {
draggable={false} draggable={false}
className={allClassNames} className={allClassNames}
orientation={orientation} orientation={orientation}
{...passedProps}
> >
{renderHeader({ id, cards, ...otherProps })} {renderHeader({ id, cards, ...passedProps })}
{renderDragContainer(isDraggingOver)} {renderDragContainer(isDraggingOver)}
{loading && <components.Loader />} {loading && <components.Loader />}
{showFooter && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />} {showFooter && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
</components.Section> </components.Section>
); );
}; }
Lane.propTypes = { Lane.propTypes = {
actions: PropTypes.object, actions: PropTypes.object,