- Performance / bug fixes / massive deleting of react-trello files we are not using.
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { ExclamationCircleFilled } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Dropdown } from "antd";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { Button } from "antd";
|
||||
import React, { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -16,58 +16,45 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
||||
});
|
||||
|
||||
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
|
||||
const ProductionListColumnAlert = ({ record, insertAuditTrail }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [updateAlert] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleAlertToggle = useCallback(() => {
|
||||
logImEXEvent("production_toggle_alert");
|
||||
|
||||
const newAlertState = !!record.production_vars?.alert ? !record.production_vars.alert : true;
|
||||
|
||||
updateAlert({
|
||||
variables: {
|
||||
jobId: record.id,
|
||||
job: {
|
||||
production_vars: {
|
||||
...record.production_vars,
|
||||
alert: !!record.production_vars && !!record.production_vars.alert ? !record.production_vars.alert : true
|
||||
alert: newAlertState
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.error(`Something went wrong updating production note: ${err.message || ""}`);
|
||||
});
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: record.id,
|
||||
operation: AuditTrailMapping.alertToggle(
|
||||
!!record.production_vars && !!record.production_vars.alert ? !record.production_vars.alert : true
|
||||
),
|
||||
operation: AuditTrailMapping.alertToggle(newAlertState),
|
||||
type: "alertToggle"
|
||||
}).then(() => {
|
||||
if (record.refetch) record.refetch();
|
||||
});
|
||||
}, [updateAlert, insertAuditTrail, record]);
|
||||
|
||||
const menuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "toggleAlert",
|
||||
label:
|
||||
record.production_vars && record.production_vars.alert
|
||||
? t("production.labels.alertoff")
|
||||
: t("production.labels.alerton"),
|
||||
onClick: handleAlertToggle
|
||||
}
|
||||
],
|
||||
[record.production_vars, t, handleAlertToggle]
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown menu={{ items: menuItems }} trigger={["contextMenu"]}>
|
||||
<div style={{ height: "19px" }}>
|
||||
{record.production_vars && record.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
</div>
|
||||
</Dropdown>
|
||||
<div style={{ height: "19px" }}>
|
||||
{record.production_vars?.alert && (
|
||||
<Button className="production-alert" icon={<ExclamationCircleFilled />} onClick={handleAlertToggle} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ProductionListColumnAlert);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from "react";
|
||||
import { AddCardLink } from "../styles/Base";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AddCardLinkComponent = ({ onClick, laneId }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return <AddCardLink onClick={onClick}>{t("trello.labels.add_card")}</AddCardLink>;
|
||||
};
|
||||
|
||||
export default AddCardLinkComponent;
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { TagSpan } from "../../styles/Base";
|
||||
|
||||
const Tag = ({ title, color, bgcolor, tagStyle, ...otherProps }) => {
|
||||
const style = { color: color || "white", backgroundColor: bgcolor || "orange", ...tagStyle };
|
||||
return (
|
||||
<TagSpan style={style} {...otherProps}>
|
||||
{title}
|
||||
</TagSpan>
|
||||
);
|
||||
};
|
||||
|
||||
Tag.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
color: PropTypes.string,
|
||||
bgcolor: PropTypes.string,
|
||||
tagStyle: PropTypes.object
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
@@ -1,62 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import InlineInput from "../../widgets/InlineInput.jsx";
|
||||
import { LaneHeader, RightContent, Title } from "../../styles/Base";
|
||||
import LaneMenu from "./LaneHeader/LaneMenu.jsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const LaneHeaderComponent = ({
|
||||
updateTitle,
|
||||
canAddLanes,
|
||||
onDelete,
|
||||
onDoubleClick,
|
||||
editLaneTitle,
|
||||
label,
|
||||
title,
|
||||
titleStyle,
|
||||
labelStyle
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<LaneHeader onDoubleClick={onDoubleClick} editLaneTitle={editLaneTitle}>
|
||||
<Title style={titleStyle}>
|
||||
{editLaneTitle ? (
|
||||
<InlineInput
|
||||
value={title}
|
||||
border
|
||||
placeholder={t("trello.labels.title")}
|
||||
resize="vertical"
|
||||
onSave={updateTitle}
|
||||
/>
|
||||
) : (
|
||||
title
|
||||
)}
|
||||
</Title>
|
||||
{label && (
|
||||
<RightContent>
|
||||
<span style={labelStyle}>{label}</span>
|
||||
</RightContent>
|
||||
)}
|
||||
{canAddLanes && <LaneMenu onDelete={onDelete} />}
|
||||
</LaneHeader>
|
||||
);
|
||||
};
|
||||
|
||||
LaneHeaderComponent.propTypes = {
|
||||
updateTitle: PropTypes.func,
|
||||
editLaneTitle: PropTypes.bool,
|
||||
canAddLanes: PropTypes.bool,
|
||||
label: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onDelete: PropTypes.func,
|
||||
onDoubleClick: PropTypes.func
|
||||
};
|
||||
|
||||
LaneHeaderComponent.defaultProps = {
|
||||
updateTitle: () => {},
|
||||
editLaneTitle: false,
|
||||
canAddLanes: false
|
||||
};
|
||||
|
||||
export default LaneHeaderComponent;
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { Popover } from "react-popopo";
|
||||
|
||||
import { CustomPopoverContainer, CustomPopoverContent } from "../../../styles/Base";
|
||||
|
||||
import {
|
||||
DeleteWrapper,
|
||||
GenDelButton,
|
||||
LaneMenuContent,
|
||||
LaneMenuHeader,
|
||||
LaneMenuItem,
|
||||
LaneMenuTitle,
|
||||
MenuButton
|
||||
} from "../../../styles/Elements";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const LaneMenu = ({ onDelete }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
position="bottom"
|
||||
PopoverContainer={CustomPopoverContainer}
|
||||
PopoverContent={CustomPopoverContent}
|
||||
trigger={<MenuButton>⋮</MenuButton>}
|
||||
>
|
||||
<LaneMenuHeader>
|
||||
<LaneMenuTitle>{t("trello.labels.lane_actions")}</LaneMenuTitle>
|
||||
<DeleteWrapper>
|
||||
<GenDelButton>✖</GenDelButton>
|
||||
</DeleteWrapper>
|
||||
</LaneMenuHeader>
|
||||
<LaneMenuContent>
|
||||
<LaneMenuItem onClick={onDelete}>{t("trello.labels.delete_lane")}</LaneMenuItem>
|
||||
</LaneMenuContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default LaneMenu;
|
||||
@@ -1,53 +0,0 @@
|
||||
import React, { useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { CardForm, CardHeader, CardRightContent, CardTitle, CardWrapper, Detail } from "../styles/Base";
|
||||
import { AddButton, CancelButton } from "../styles/Elements";
|
||||
import EditableLabel from "../widgets/EditableLabel.jsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const NewCardForm = ({ onCancel, onAdd }) => {
|
||||
const [state, setState] = useState({});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const updateField = (field, value) => {
|
||||
setState((prevState) => ({ ...prevState, [field]: value }));
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
onAdd(state);
|
||||
};
|
||||
|
||||
return (
|
||||
<CardForm>
|
||||
<CardWrapper>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
<EditableLabel
|
||||
placeholder={t("trello.labels.title")}
|
||||
onChange={(val) => updateField("title", val)}
|
||||
autoFocus
|
||||
/>
|
||||
</CardTitle>
|
||||
<CardRightContent>
|
||||
<EditableLabel placeholder={t("trello.labels.label")} onChange={(val) => updateField("label", val)} />
|
||||
</CardRightContent>
|
||||
</CardHeader>
|
||||
<Detail>
|
||||
<EditableLabel
|
||||
placeholder={t("trello.labels.description")}
|
||||
onChange={(val) => updateField("description", val)}
|
||||
/>
|
||||
</Detail>
|
||||
</CardWrapper>
|
||||
<AddButton onClick={handleAdd}>{t("trello.labels.add_card")}</AddButton>
|
||||
<CancelButton onClick={onCancel}>{t("trello.labels.cancel")}</CancelButton>
|
||||
</CardForm>
|
||||
);
|
||||
};
|
||||
|
||||
NewCardForm.propTypes = {
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
onAdd: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default NewCardForm;
|
||||
@@ -1,57 +0,0 @@
|
||||
import React, { useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { LaneTitle, NewLaneButtons, Section } from "../styles/Base";
|
||||
import { AddButton, CancelButton } from "../styles/Elements";
|
||||
import NewLaneTitleEditor from "../widgets/NewLaneTitleEditor.jsx";
|
||||
import { v1 } from "uuid";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const NewLane = ({ onCancel, onAdd }) => {
|
||||
const refInput = useRef(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSubmit = () => {
|
||||
onAdd({
|
||||
id: v1(),
|
||||
title: getValue()
|
||||
});
|
||||
};
|
||||
|
||||
const getValue = () => refInput.current.getValue();
|
||||
|
||||
// TODO: Commented out because it was never called and it was causing a error
|
||||
// const onClickOutside = (a, b, c) => {
|
||||
// if (getValue().length > 0) {
|
||||
// handleSubmit();
|
||||
// } else {
|
||||
// onCancel();
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<LaneTitle>
|
||||
<NewLaneTitleEditor
|
||||
ref={refInput}
|
||||
placeholder={t("trello.labels.title")}
|
||||
onCancel={onCancel}
|
||||
onSave={handleSubmit}
|
||||
resize="vertical"
|
||||
border
|
||||
autoFocus
|
||||
/>
|
||||
</LaneTitle>
|
||||
<NewLaneButtons>
|
||||
<AddButton onClick={handleSubmit}>{t("trello.labels.add_lane")}</AddButton>
|
||||
<CancelButton onClick={onCancel}>{t("trello.labels.cancel")}</CancelButton>
|
||||
</NewLaneButtons>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
NewLane.propTypes = {
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
onAdd: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default NewLane;
|
||||
@@ -1,16 +0,0 @@
|
||||
import React from "react";
|
||||
import { NewLaneSection } from "../styles/Base";
|
||||
import { AddLaneLink } from "../styles/Elements";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const NewLaneSectionComponent = ({ onClick }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<NewLaneSection>
|
||||
<AddLaneLink onClick={onClick}>{t("trello.labels.add_lane")}</AddLaneLink>
|
||||
</NewLaneSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewLaneSectionComponent;
|
||||
@@ -1,11 +1,7 @@
|
||||
import LaneHeader from "./Lane/LaneHeader";
|
||||
import LaneFooter from "./Lane/LaneFooter";
|
||||
import Card from "./Card";
|
||||
import Loader from "./Loader.jsx";
|
||||
import NewLaneForm from "./NewLaneForm.jsx";
|
||||
import NewCardForm from "./NewCardForm.jsx";
|
||||
import AddCardLink from "./AddCardLink";
|
||||
import NewLaneSection from "./NewLaneSection.jsx";
|
||||
|
||||
import { BoardWrapper, StyleHorizontal, GlobalStyle, StyleVertical, ScrollableLane, Section } from "../styles/Base";
|
||||
|
||||
const exports = {
|
||||
@@ -15,14 +11,9 @@ const exports = {
|
||||
BoardWrapper,
|
||||
Loader,
|
||||
ScrollableLane,
|
||||
LaneHeader,
|
||||
LaneFooter,
|
||||
Section,
|
||||
NewLaneForm,
|
||||
NewLaneSection,
|
||||
NewCardForm,
|
||||
Card,
|
||||
AddCardLink
|
||||
Card
|
||||
};
|
||||
|
||||
export default exports;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { BoardContainer } from "../index";
|
||||
import classNames from "classnames";
|
||||
import { useMemo, useState } from "react";
|
||||
import { v1 } from "uuid";
|
||||
|
||||
const Board = ({ id, className, components, orientation, ...additionalProps }) => {
|
||||
const [storeId] = useState(id || v1());
|
||||
|
||||
const allClassNames = useMemo(() => classNames("react-trello-board", className || ""), [className]);
|
||||
const allClassNames = useMemo(() => `react-trello-board ${className || ""}`.trim(), [className]);
|
||||
const OrientationStyle = useMemo(
|
||||
() => (orientation === "horizontal" ? components.StyleHorizontal : components.StyleVertical),
|
||||
[orientation, components.StyleHorizontal, components.StyleVertical]
|
||||
|
||||
@@ -7,6 +7,7 @@ import isEqual from "lodash/isEqual";
|
||||
import Lane from "./Lane";
|
||||
import { PopoverWrapper } from "react-popopo";
|
||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||
import { BoardWrapper } from "../styles/Base.js";
|
||||
|
||||
/**
|
||||
* BoardContainer is a React component that represents a Trello-like board.
|
||||
@@ -17,17 +18,10 @@ import * as actions from "../../../redux/trello/trello.actions.js";
|
||||
* @param {string} props.id - The unique identifier for the board
|
||||
* @param {Object} props.components - Custom components to use in the board
|
||||
* @param {Object} props.data - The initial data for the board
|
||||
* @param {boolean} props.draggable - Whether the board is draggable
|
||||
* @param {Object} props.style - The CSS styles to apply to the board
|
||||
* @param {Function} props.onDataChange - Callback function when the board data changes
|
||||
* @param {Function} props.onDragEnd - Callback function when a drag ends
|
||||
* @param {boolean} props.editable - Whether the board is editable
|
||||
* @param {boolean} props.canAddLanes - Whether lanes can be added to the board
|
||||
* @param {Object} props.laneStyle - The CSS styles to apply to the lanes
|
||||
* @param {string} props.orientation - The orientation of the board ("horizontal" or "vertical")
|
||||
* @param {Function} props.eventBusHandle - Function to handle events from the event bus
|
||||
* @param {Object} props.reducerData - The initial data for the Redux reducer
|
||||
* @param {Object} props.cardStyle - The CSS styles to apply to the cards
|
||||
* @param {Object} props.otherProps - Any other props to pass to the board
|
||||
* @returns {JSX.Element} A Trello-like board
|
||||
*/
|
||||
@@ -35,17 +29,11 @@ const BoardContainer = ({
|
||||
id,
|
||||
components,
|
||||
data,
|
||||
draggable = false,
|
||||
style,
|
||||
onDataChange = () => {},
|
||||
onDragEnd = () => {},
|
||||
editable = false,
|
||||
canAddLanes = false,
|
||||
laneStyle,
|
||||
orientation = "horizontal",
|
||||
eventBusHandle,
|
||||
reducerData,
|
||||
cardStyle,
|
||||
...otherProps
|
||||
}) => {
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
@@ -60,12 +48,20 @@ const BoardContainer = ({
|
||||
const eventBus = {
|
||||
publish: (event) => {
|
||||
switch (event.type) {
|
||||
case "ADD_CARD":
|
||||
return dispatch(actions.addCard({ laneId: event.laneId, card: event.card }));
|
||||
case "REMOVE_CARD":
|
||||
return dispatch(actions.removeCard({ laneId: event.laneId, cardId: event.cardId }));
|
||||
case "REFRESH_BOARD":
|
||||
return dispatch(actions.loadBoard(event.data));
|
||||
// case "ADD_CARD":
|
||||
// return dispatch(actions.addCard({ laneId: event.laneId, card: event.card }));
|
||||
// case "REMOVE_CARD":
|
||||
// return dispatch(actions.removeCard({ laneId: event.laneId, cardId: event.cardId }));
|
||||
// case "REFRESH_BOARD":
|
||||
// return dispatch(actions.loadBoard(event.data));
|
||||
// case "UPDATE_CARDS":
|
||||
// return dispatch(actions.updateCards({ laneId: event.laneId, cards: event.cards }));
|
||||
// case "UPDATE_CARD":
|
||||
// return dispatch(actions.updateCard({ laneId: event.laneId, updatedCard: event.card }));
|
||||
// case "UPDATE_LANES":
|
||||
// return dispatch(actions.updateLanes(event.lanes));
|
||||
// case "UPDATE_LANE":
|
||||
// return dispatch(actions.updateLane(event.lane));
|
||||
case "MOVE_CARD":
|
||||
return dispatch(
|
||||
actions.moveCardAcrossLanes({
|
||||
@@ -76,14 +72,7 @@ const BoardContainer = ({
|
||||
event
|
||||
})
|
||||
);
|
||||
case "UPDATE_CARDS":
|
||||
return dispatch(actions.updateCards({ laneId: event.laneId, cards: event.cards }));
|
||||
case "UPDATE_CARD":
|
||||
return dispatch(actions.updateCard({ laneId: event.laneId, updatedCard: event.card }));
|
||||
case "UPDATE_LANES":
|
||||
return dispatch(actions.updateLanes(event.lanes));
|
||||
case "UPDATE_LANE":
|
||||
return dispatch(actions.updateLane(event.lane));
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -109,13 +98,6 @@ const BoardContainer = ({
|
||||
setIsDragging(true);
|
||||
}, [setIsDragging]);
|
||||
|
||||
const getCardDetails = useCallback(
|
||||
(laneId, cardIndex) => {
|
||||
return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
||||
},
|
||||
[currentReducerData]
|
||||
);
|
||||
|
||||
const passThroughProps = useMemo(
|
||||
() =>
|
||||
pick(
|
||||
@@ -123,59 +105,15 @@ const BoardContainer = ({
|
||||
id,
|
||||
components,
|
||||
data,
|
||||
draggable,
|
||||
style,
|
||||
onDataChange,
|
||||
editable,
|
||||
canAddLanes,
|
||||
laneStyle,
|
||||
orientation,
|
||||
eventBusHandle,
|
||||
reducerData,
|
||||
cardStyle,
|
||||
...otherProps
|
||||
},
|
||||
[
|
||||
"onLaneScroll",
|
||||
"onLaneDelete",
|
||||
"onLaneUpdate",
|
||||
"onCardClick",
|
||||
"onBeforeCardDelete",
|
||||
"onCardDelete",
|
||||
"onCardAdd",
|
||||
"onCardUpdate",
|
||||
"onLaneClick",
|
||||
"laneSortFunction",
|
||||
"draggable",
|
||||
"cardDraggable",
|
||||
"collapsibleLanes",
|
||||
"canAddLanes",
|
||||
"hideCardDeleteIcon",
|
||||
"tagStyle",
|
||||
"handleDragStart",
|
||||
"handleDragEnd",
|
||||
"cardDragClass",
|
||||
"editLaneTitle",
|
||||
"orientation"
|
||||
]
|
||||
["laneSortFunction", "collapsibleLanes", "orientation"]
|
||||
),
|
||||
[
|
||||
id,
|
||||
components,
|
||||
data,
|
||||
draggable,
|
||||
style,
|
||||
onDataChange,
|
||||
|
||||
editable,
|
||||
canAddLanes,
|
||||
laneStyle,
|
||||
orientation,
|
||||
eventBusHandle,
|
||||
reducerData,
|
||||
cardStyle,
|
||||
otherProps
|
||||
]
|
||||
[id, components, data, onDataChange, orientation, eventBusHandle, reducerData, otherProps]
|
||||
);
|
||||
|
||||
const onLaneDrag = useCallback(
|
||||
@@ -207,7 +145,7 @@ const BoardContainer = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<components.BoardWrapper style={style} orientation={orientation}>
|
||||
<BoardWrapper orientation={orientation}>
|
||||
<PopoverWrapper>
|
||||
<DragDropContext onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId={groupName}>
|
||||
{currentReducerData.lanes.map((lane, index) => {
|
||||
@@ -218,13 +156,7 @@ const BoardContainer = ({
|
||||
boardId={groupName}
|
||||
components={components}
|
||||
id={id}
|
||||
getCardDetails={getCardDetails}
|
||||
index={index}
|
||||
droppable={droppable === undefined ? true : droppable}
|
||||
style={laneStyle || lane.style || {}}
|
||||
labelStyle={lane.labelStyle || {}}
|
||||
cardStyle={cardStyle || lane.cardStyle}
|
||||
editable={editable && !lane.disallowAddingCard}
|
||||
{...laneOtherProps}
|
||||
{...passThroughProps}
|
||||
cards={lane.cards}
|
||||
@@ -235,7 +167,7 @@ const BoardContainer = ({
|
||||
})}
|
||||
</DragDropContext>
|
||||
</PopoverWrapper>
|
||||
</components.BoardWrapper>
|
||||
</BoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -247,30 +179,10 @@ BoardContainer.propTypes = {
|
||||
reducerData: PropTypes.object,
|
||||
onDataChange: PropTypes.func,
|
||||
eventBusHandle: PropTypes.func,
|
||||
onLaneScroll: PropTypes.func,
|
||||
onCardClick: PropTypes.func,
|
||||
onBeforeCardDelete: PropTypes.func,
|
||||
onCardDelete: PropTypes.func,
|
||||
onCardAdd: PropTypes.func,
|
||||
onCardUpdate: PropTypes.func,
|
||||
onLaneAdd: PropTypes.func,
|
||||
onLaneDelete: PropTypes.func,
|
||||
onLaneClick: PropTypes.func,
|
||||
onLaneUpdate: PropTypes.func,
|
||||
laneSortFunction: PropTypes.func,
|
||||
draggable: PropTypes.bool,
|
||||
collapsibleLanes: PropTypes.bool,
|
||||
editable: PropTypes.bool,
|
||||
canAddLanes: PropTypes.bool,
|
||||
hideCardDeleteIcon: PropTypes.bool,
|
||||
handleDragStart: PropTypes.func,
|
||||
handleDragEnd: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
tagStyle: PropTypes.object,
|
||||
cardDraggable: PropTypes.bool,
|
||||
cardDragClass: PropTypes.string,
|
||||
orientation: PropTypes.string,
|
||||
cardStyle: PropTypes.object
|
||||
orientation: PropTypes.string
|
||||
};
|
||||
|
||||
export default BoardContainer;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { forwardRef, useCallback, useMemo, useState, useEffect } from "react";
|
||||
import classNames from "classnames";
|
||||
import React, { forwardRef, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { bindActionCreators } from "redux";
|
||||
import { connect } from "react-redux";
|
||||
import { v1 } from "uuid";
|
||||
|
||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||
import { Draggable, Droppable } from "../dnd/lib";
|
||||
import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
|
||||
import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx";
|
||||
import { Section } from "../styles/Base.js";
|
||||
import LaneFooter from "../components/Lane/LaneFooter.jsx";
|
||||
|
||||
const Lane = ({
|
||||
actions,
|
||||
@@ -20,36 +20,14 @@ const Lane = ({
|
||||
laneSortFunction,
|
||||
style = {},
|
||||
cardStyle = {},
|
||||
tagStyle = {},
|
||||
titleStyle = {},
|
||||
labelStyle = {},
|
||||
cards,
|
||||
label,
|
||||
draggable = false,
|
||||
collapsibleLanes = false,
|
||||
droppable = true,
|
||||
onCardClick = () => {},
|
||||
onBeforeCardDelete = () => {},
|
||||
onCardDelete = () => {},
|
||||
onCardAdd = () => {},
|
||||
onCardUpdate = () => {},
|
||||
onLaneDelete = () => {},
|
||||
onLaneUpdate = () => {},
|
||||
onLaneClick = () => {},
|
||||
onLaneScroll = () => {},
|
||||
editable = false,
|
||||
cardDraggable = true,
|
||||
cardDragClass,
|
||||
cardDropClass,
|
||||
canAddLanes = false,
|
||||
hideCardDeleteIcon = false,
|
||||
|
||||
components = {},
|
||||
orientation = "vertical",
|
||||
className,
|
||||
currentPage,
|
||||
...otherProps
|
||||
}) => {
|
||||
const [addCardMode, setAddCardMode] = useState(false);
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
@@ -65,76 +43,11 @@ const Lane = ({
|
||||
return cards.concat().sort((card1, card2) => sortFunction(card1, card2));
|
||||
}, []);
|
||||
|
||||
const removeCard = useCallback(
|
||||
(cardId) => {
|
||||
if (onBeforeCardDelete && typeof onBeforeCardDelete === "function") {
|
||||
onBeforeCardDelete(() => {
|
||||
onCardDelete && onCardDelete(cardId, id);
|
||||
actions.removeCard({ laneId: id, cardId: cardId });
|
||||
});
|
||||
} else {
|
||||
onCardDelete && onCardDelete(cardId, id);
|
||||
actions.removeCard({ laneId: id, cardId: cardId });
|
||||
}
|
||||
},
|
||||
[onBeforeCardDelete, onCardDelete, actions, id]
|
||||
);
|
||||
|
||||
const handleCardClick = useCallback(
|
||||
(e, card) => {
|
||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||
e.stopPropagation();
|
||||
},
|
||||
[onCardClick]
|
||||
);
|
||||
|
||||
const showEditableCard = useCallback(() => {
|
||||
setAddCardMode(true);
|
||||
}, [setAddCardMode]);
|
||||
|
||||
const hideEditableCard = useCallback(() => {
|
||||
setAddCardMode(false);
|
||||
}, [setAddCardMode]);
|
||||
|
||||
const addNewCard = useCallback(
|
||||
(params) => {
|
||||
const laneId = id;
|
||||
const newCardId = v1();
|
||||
hideEditableCard();
|
||||
let card = { id: newCardId, ...params };
|
||||
actions.addCard({ laneId, card });
|
||||
onCardAdd(card, laneId);
|
||||
},
|
||||
[actions, id, onCardAdd, hideEditableCard]
|
||||
);
|
||||
|
||||
const updateCard = useCallback(
|
||||
(updatedCard) => {
|
||||
actions.updateCard({ laneId: id, card: updatedCard });
|
||||
onCardUpdate(id, updatedCard);
|
||||
},
|
||||
[actions, id, onCardUpdate]
|
||||
);
|
||||
|
||||
const removeLane = useCallback(() => {
|
||||
actions.removeLane({ laneId: id });
|
||||
onLaneDelete(id);
|
||||
}, [actions, id, onLaneDelete]);
|
||||
|
||||
const updateTitle = useCallback(
|
||||
(value) => {
|
||||
actions.updateLane({ id, title: value });
|
||||
onLaneUpdate(id, { title: value });
|
||||
},
|
||||
[actions, id, onLaneUpdate]
|
||||
);
|
||||
|
||||
const toggleLaneCollapsed = useCallback(() => {
|
||||
collapsibleLanes && setCollapsed(!collapsed);
|
||||
}, [collapsibleLanes, collapsed]);
|
||||
|
||||
const Card = React.memo(({ provided, item: card, isDragging }) => {
|
||||
const onDeleteCard = () => removeCard(card.id);
|
||||
return (
|
||||
<div
|
||||
{...provided.draggableProps}
|
||||
@@ -144,19 +57,7 @@ const Lane = ({
|
||||
className={`item ${isDragging ? "is-dragging" : ""}`}
|
||||
key={card.id}
|
||||
>
|
||||
<components.Card
|
||||
key={card.id}
|
||||
style={card.style || cardStyle}
|
||||
className="react-trello-card"
|
||||
onDelete={onDeleteCard}
|
||||
onClick={(e) => handleCardClick(e, card)}
|
||||
onChange={(updatedCard) => updateCard(updatedCard)}
|
||||
showDeleteButton={!hideCardDeleteIcon}
|
||||
tagStyle={tagStyle}
|
||||
cardDraggable={cardDraggable}
|
||||
editable={editable}
|
||||
{...card}
|
||||
/>
|
||||
<components.Card key={card.id} style={card.style || cardStyle} className="react-trello-card" {...card} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -174,16 +75,6 @@ const Lane = ({
|
||||
);
|
||||
};
|
||||
|
||||
const renderAddCardLink = useMemo(
|
||||
() => editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />,
|
||||
[editable, addCardMode, showEditableCard, id]
|
||||
);
|
||||
|
||||
const renderNewCardForm = useMemo(
|
||||
() => addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />,
|
||||
[addCardMode, hideEditableCard, addNewCard, id]
|
||||
);
|
||||
|
||||
const ItemWrapper = ({ children, ...props }) => (
|
||||
<div {...props} className="item-wrapper">
|
||||
{children}
|
||||
@@ -196,43 +87,46 @@ const Lane = ({
|
||||
|
||||
const commonProps = {
|
||||
useWindowScroll: true,
|
||||
data: renderedCards,
|
||||
overscan: {
|
||||
main: 22,
|
||||
reverse: 22
|
||||
}
|
||||
data: renderedCards
|
||||
};
|
||||
|
||||
const componentProps =
|
||||
orientation === "vertical"
|
||||
? {
|
||||
...commonProps,
|
||||
scrollerRef: provided.innerRef,
|
||||
listClassName: "grid-container",
|
||||
itemClassName: "grid-item",
|
||||
components: {
|
||||
List: forwardRef(({ style, children, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
{...props}
|
||||
style={{
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)),
|
||||
Item: ({ children, ...props }) => <div {...props}>{children}</div>
|
||||
},
|
||||
itemContent: (index, item) => <ItemWrapper>{renderDraggable(index, item)}</ItemWrapper>
|
||||
}
|
||||
: {
|
||||
...commonProps,
|
||||
components: { Item: HeightPreservingItem },
|
||||
itemContent: (index, item) => renderDraggable(index, item),
|
||||
scrollerRef: provided.innerRef
|
||||
};
|
||||
const verticalProps = {
|
||||
...commonProps,
|
||||
// we are using the useWindowScroll, so we don't need to pass the scrollerRef
|
||||
// scrollerRef: provided.innerRef,
|
||||
listClassName: "grid-container",
|
||||
itemClassName: "grid-item",
|
||||
components: {
|
||||
List: forwardRef(({ style, children, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
{...props}
|
||||
style={{
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)),
|
||||
Item: ({ children, ...props }) => <div {...props}>{children}</div>
|
||||
},
|
||||
itemContent: (index, item) => <ItemWrapper>{renderDraggable(index, item)}</ItemWrapper>,
|
||||
overscan: { main: 10, reverse: 10 }
|
||||
};
|
||||
|
||||
const horizontalProps = {
|
||||
...commonProps,
|
||||
components: { Item: HeightPreservingItem },
|
||||
overscan: { main: 1, reverse: 1 },
|
||||
itemContent: (index, item) => renderDraggable(index, item),
|
||||
scrollerRef: provided.innerRef
|
||||
};
|
||||
|
||||
const componentProps = orientation === "vertical" ? verticalProps : horizontalProps;
|
||||
|
||||
// A collapsed lane does not need to render the Virtuoso component
|
||||
const finalComponentProps = collapsed ? {} : componentProps;
|
||||
|
||||
return (
|
||||
<div style={{ height: "100%" }}>
|
||||
<div
|
||||
@@ -244,8 +138,6 @@ const Lane = ({
|
||||
{isVisible && <FinalComponent {...finalComponentProps} />}
|
||||
{(orientation === "horizontal" || renderedCards.length === 0 || collapsed) && provided.placeholder}
|
||||
</div>
|
||||
{renderAddCardLink}
|
||||
{renderNewCardForm}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -275,18 +167,13 @@ const Lane = ({
|
||||
};
|
||||
|
||||
const renderHeader = (pickedProps) => {
|
||||
return (
|
||||
<components.LaneHeader
|
||||
{...pickedProps}
|
||||
onDelete={removeLane}
|
||||
onDoubleClick={toggleLaneCollapsed}
|
||||
updateTitle={updateTitle}
|
||||
/>
|
||||
);
|
||||
return <components.LaneHeader {...pickedProps} onDoubleClick={toggleLaneCollapsed} />;
|
||||
};
|
||||
|
||||
const allClassNames = classNames("react-trello-lane", collapsed ? "lane-collapsed" : "", className || "");
|
||||
|
||||
const allClassNames = useMemo(
|
||||
() => `react-trello-lane ${collapsed ? "lane-collapsed" : ""} ${className || ""}`.trim(),
|
||||
[collapsed, className]
|
||||
);
|
||||
const passedProps = {
|
||||
actions,
|
||||
id,
|
||||
@@ -296,37 +183,20 @@ const Lane = ({
|
||||
laneSortFunction,
|
||||
style,
|
||||
cardStyle,
|
||||
tagStyle,
|
||||
titleStyle,
|
||||
labelStyle,
|
||||
cards,
|
||||
label,
|
||||
draggable,
|
||||
collapsibleLanes,
|
||||
droppable,
|
||||
editable,
|
||||
cardDraggable,
|
||||
cardDragClass,
|
||||
cardDropClass,
|
||||
canAddLanes,
|
||||
hideCardDeleteIcon,
|
||||
|
||||
components,
|
||||
orientation,
|
||||
className,
|
||||
currentPage,
|
||||
...otherProps
|
||||
};
|
||||
return (
|
||||
<components.Section
|
||||
key={id}
|
||||
onClick={() => onLaneClick && onLaneClick(id)}
|
||||
orientation={orientation}
|
||||
{...passedProps}
|
||||
>
|
||||
<Section key={id} orientation={orientation} {...passedProps}>
|
||||
{renderHeader({ id, cards, ...passedProps })}
|
||||
{renderDragContainer()}
|
||||
{collapsibleLanes && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
||||
</components.Section>
|
||||
{collapsibleLanes && <LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -339,20 +209,9 @@ Lane.propTypes = {
|
||||
laneSortFunction: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
cardStyle: PropTypes.object,
|
||||
tagStyle: PropTypes.object,
|
||||
titleStyle: PropTypes.object,
|
||||
labelStyle: PropTypes.object,
|
||||
cards: PropTypes.array,
|
||||
label: PropTypes.string,
|
||||
draggable: PropTypes.bool,
|
||||
collapsibleLanes: PropTypes.bool,
|
||||
droppable: PropTypes.bool,
|
||||
editable: PropTypes.bool,
|
||||
cardDraggable: PropTypes.bool,
|
||||
cardDragClass: PropTypes.string,
|
||||
cardDropClass: PropTypes.string,
|
||||
canAddLanes: PropTypes.bool,
|
||||
hideCardDeleteIcon: PropTypes.bool,
|
||||
components: PropTypes.object,
|
||||
orientation: PropTypes.string
|
||||
};
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
import update from "immutability-helper";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
|
||||
/**
|
||||
* Update the lanes in the state
|
||||
* @param state
|
||||
* @param lanes
|
||||
* @returns {unknown}
|
||||
*/
|
||||
const updateLanes = (state, lanes) => update(state, { lanes: { $set: lanes } });
|
||||
const updateLaneCards = (lane, cards) => update(lane, { cards: { $set: cards } });
|
||||
|
||||
/**
|
||||
* Helper functions for managing lanes
|
||||
* @type {{moveCardAcrossLanes: (function(*, {fromLaneId: *, toLaneId: *, cardId: *, index: *}): *), initialiseLanes: (function(*, {lanes: *}): {lanes: *})}}
|
||||
*/
|
||||
const LaneHelper = {
|
||||
/**
|
||||
* Initialise the lanes
|
||||
* @param state
|
||||
* @param lanes
|
||||
* @returns {unknown}
|
||||
*/
|
||||
initialiseLanes: (state, { lanes }) => {
|
||||
const newLanes = lanes.map((lane) => {
|
||||
lane.currentPage = 1;
|
||||
@@ -14,79 +29,6 @@ const LaneHelper = {
|
||||
return updateLanes(state, newLanes);
|
||||
},
|
||||
|
||||
paginateLane: (state, { laneId, newCards, nextPage }) => {
|
||||
const updatedLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: newCards });
|
||||
updatedLanes.find((lane) => lane.id === laneId).currentPage = nextPage;
|
||||
return updateLanes(state, updatedLanes);
|
||||
},
|
||||
|
||||
appendCardsToLane: (state, { laneId, newCards, index }) => {
|
||||
const lane = state.lanes.find((lane) => lane.id === laneId);
|
||||
newCards = newCards
|
||||
.map((c) => update(c, { laneId: { $set: laneId } }))
|
||||
.filter((c) => lane.cards.find((card) => card.id === c.id) == null);
|
||||
return state.lanes.map((lane) => {
|
||||
if (lane.id === laneId) {
|
||||
const cardsToUpdate =
|
||||
index !== undefined
|
||||
? [...lane.cards.slice(0, index), ...newCards, ...lane.cards.slice(index)]
|
||||
: [...lane.cards, ...newCards];
|
||||
return updateLaneCards(lane, cardsToUpdate);
|
||||
} else {
|
||||
return lane;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
appendCardToLane: (state, { laneId, card, index }) => {
|
||||
const newLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: [card], index });
|
||||
return updateLanes(state, newLanes);
|
||||
},
|
||||
|
||||
addLane: (state, lane) => {
|
||||
const newLane = { cards: [], ...lane };
|
||||
return updateLanes(state, [...state.lanes, newLane]);
|
||||
},
|
||||
|
||||
updateLane: (state, updatedLane) => {
|
||||
const newLanes = state.lanes.map((lane) => (updatedLane.id === lane.id ? { ...lane, ...updatedLane } : lane));
|
||||
return updateLanes(state, newLanes);
|
||||
},
|
||||
|
||||
removeCardFromLane: (state, { laneId, cardId }) => {
|
||||
// Clone the state to avoid mutation
|
||||
const newLanes = cloneDeep(state.lanes);
|
||||
|
||||
// Find the lane from which the card will be removed
|
||||
const lane = newLanes.find((lane) => lane.id === laneId);
|
||||
|
||||
// Find the card in the lane
|
||||
const cardIndex = lane.cards.findIndex((card) => card.id === cardId);
|
||||
if (cardIndex === -1) {
|
||||
throw new Error("Card not found in the lane");
|
||||
}
|
||||
|
||||
// Remove the card from the lane
|
||||
lane.cards.splice(cardIndex, 1);
|
||||
|
||||
let idx = 0;
|
||||
|
||||
// Update the lane and card indexes for all lanes
|
||||
newLanes.forEach((lane, laneIndex) => {
|
||||
lane.cards.forEach((card, cardIndex) => {
|
||||
card.idx = idx;
|
||||
card.laneIndex = laneIndex;
|
||||
card.cardIndex = cardIndex;
|
||||
card.laneId = lane.id;
|
||||
idx++;
|
||||
});
|
||||
});
|
||||
|
||||
return update(state, {
|
||||
lanes: { $set: newLanes }
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Move a card from one lane to another
|
||||
* @param state
|
||||
@@ -136,28 +78,104 @@ const LaneHelper = {
|
||||
return update(state, {
|
||||
lanes: { $set: newLanes }
|
||||
});
|
||||
},
|
||||
updateCardsForLane: (state, { laneId, cards }) => {
|
||||
const lanes = state.lanes.map((lane) => (lane.id === laneId ? updateLaneCards(lane, cards) : lane));
|
||||
return updateLanes(state, lanes);
|
||||
},
|
||||
|
||||
updateCardForLane: (state, { laneId, card: updatedCard }) => {
|
||||
const lanes = state.lanes.map((lane) => {
|
||||
if (lane.id === laneId) {
|
||||
const cards = lane.cards.map((card) => (card.id === updatedCard.id ? { ...card, ...updatedCard } : card));
|
||||
return updateLaneCards(lane, cards);
|
||||
} else {
|
||||
return lane;
|
||||
}
|
||||
});
|
||||
return updateLanes(state, lanes);
|
||||
},
|
||||
|
||||
removeLane: (state, { laneId }) => {
|
||||
const updatedLanes = state.lanes.filter((lane) => lane.id !== laneId);
|
||||
return updateLanes(state, updatedLanes);
|
||||
}
|
||||
|
||||
// TODO: (Note), the rest of this commented out logic is for the way the original Trello board was implemented
|
||||
// It would not be hard to adapt should we need to use it in the future
|
||||
|
||||
// const updateLaneCards = (lane, cards) => update(lane, { cards: { $set: cards } });
|
||||
// paginateLane: (state, { laneId, newCards, nextPage }) => {
|
||||
// const updatedLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: newCards });
|
||||
// updatedLanes.find((lane) => lane.id === laneId).currentPage = nextPage;
|
||||
// return updateLanes(state, updatedLanes);
|
||||
// },
|
||||
// appendCardsToLane: (state, { laneId, newCards, index }) => {
|
||||
// const lane = state.lanes.find((lane) => lane.id === laneId);
|
||||
// newCards = newCards
|
||||
// .map((c) => update(c, { laneId: { $set: laneId } }))
|
||||
// .filter((c) => lane.cards.find((card) => card.id === c.id) == null);
|
||||
// return state.lanes.map((lane) => {
|
||||
// if (lane.id === laneId) {
|
||||
// const cardsToUpdate =
|
||||
// index !== undefined
|
||||
// ? [...lane.cards.slice(0, index), ...newCards, ...lane.cards.slice(index)]
|
||||
// : [...lane.cards, ...newCards];
|
||||
// return updateLaneCards(lane, cardsToUpdate);
|
||||
// } else {
|
||||
// return lane;
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
|
||||
// appendCardToLane: (state, { laneId, card, index }) => {
|
||||
// const newLanes = LaneHelper.appendCardsToLane(state, { laneId: laneId, newCards: [card], index });
|
||||
// return updateLanes(state, newLanes);
|
||||
// },
|
||||
//
|
||||
// addLane: (state, lane) => {
|
||||
// const newLane = { cards: [], ...lane };
|
||||
// return updateLanes(state, [...state.lanes, newLane]);
|
||||
// },
|
||||
//
|
||||
// updateLane: (state, updatedLane) => {
|
||||
// const newLanes = state.lanes.map((lane) => (updatedLane.id === lane.id ? { ...lane, ...updatedLane } : lane));
|
||||
// return updateLanes(state, newLanes);
|
||||
// },
|
||||
//
|
||||
// removeCardFromLane: (state, { laneId, cardId }) => {
|
||||
// // Clone the state to avoid mutation
|
||||
// const newLanes = cloneDeep(state.lanes);
|
||||
//
|
||||
// // Find the lane from which the card will be removed
|
||||
// const lane = newLanes.find((lane) => lane.id === laneId);
|
||||
//
|
||||
// // Find the card in the lane
|
||||
// const cardIndex = lane.cards.findIndex((card) => card.id === cardId);
|
||||
// if (cardIndex === -1) {
|
||||
// throw new Error("Card not found in the lane");
|
||||
// }
|
||||
//
|
||||
// // Remove the card from the lane
|
||||
// lane.cards.splice(cardIndex, 1);
|
||||
//
|
||||
// let idx = 0;
|
||||
//
|
||||
// // Update the lane and card indexes for all lanes
|
||||
// newLanes.forEach((lane, laneIndex) => {
|
||||
// lane.cards.forEach((card, cardIndex) => {
|
||||
// card.idx = idx;
|
||||
// card.laneIndex = laneIndex;
|
||||
// card.cardIndex = cardIndex;
|
||||
// card.laneId = lane.id;
|
||||
// idx++;
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// return update(state, {
|
||||
// lanes: { $set: newLanes }
|
||||
// });
|
||||
// },
|
||||
// updateCardsForLane: (state, { laneId, cards }) => {
|
||||
// const lanes = state.lanes.map((lane) => (lane.id === laneId ? updateLaneCards(lane, cards) : lane));
|
||||
// return updateLanes(state, lanes);
|
||||
// },
|
||||
//
|
||||
// updateCardForLane: (state, { laneId, card: updatedCard }) => {
|
||||
// const lanes = state.lanes.map((lane) => {
|
||||
// if (lane.id === laneId) {
|
||||
// const cards = lane.cards.map((card) => (card.id === updatedCard.id ? { ...card, ...updatedCard } : card));
|
||||
// return updateLaneCards(lane, cards);
|
||||
// } else {
|
||||
// return lane;
|
||||
// }
|
||||
// });
|
||||
// return updateLanes(state, lanes);
|
||||
// },
|
||||
//
|
||||
// removeLane: (state, { laneId }) => {
|
||||
// const updatedLanes = state.lanes.filter((lane) => lane.id !== laneId);
|
||||
// return updateLanes(state, updatedLanes);
|
||||
// }
|
||||
};
|
||||
|
||||
export default LaneHelper;
|
||||
|
||||
@@ -24,6 +24,10 @@ const getSectionStyles = (props) => {
|
||||
white-space: nowrap;
|
||||
// overflow-y: none;
|
||||
min-width: 8.5%;
|
||||
flex-wrap: wrap;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
`;
|
||||
}
|
||||
return `
|
||||
@@ -31,6 +35,18 @@ const getSectionStyles = (props) => {
|
||||
`;
|
||||
};
|
||||
|
||||
const getLaneFooterStyles = (props) => {
|
||||
if (props.operation === "horizontal") {
|
||||
return `
|
||||
height: 50px;
|
||||
margin-top: 20px;
|
||||
`;
|
||||
}
|
||||
return `
|
||||
height: 25px;
|
||||
`;
|
||||
};
|
||||
|
||||
export const GlobalStyle = createGlobalStyle`
|
||||
.comPlainTextContentEditable {
|
||||
-webkit-user-modify: read-write-plaintext-only;
|
||||
@@ -84,15 +100,18 @@ export const StyleHorizontal = styled.div`
|
||||
.react-trello-lane.lane-collapsed {
|
||||
min-height: 15px;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.item-wrapper {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
white-space: nowrap;
|
||||
min-width: 4%;
|
||||
}
|
||||
|
||||
.react-trello-card {
|
||||
height: auto;
|
||||
margin: 2px;
|
||||
@@ -118,10 +137,12 @@ export const StyleVertical = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
width: 8%; // TODO: THIS IS WHERE WE GET VERTICAL CARD CUSTOMIZATION
|
||||
display: flex;
|
||||
@@ -133,6 +154,7 @@ export const StyleVertical = styled.div`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -219,27 +241,12 @@ export const Section = styled.section`
|
||||
${getSectionStyles};
|
||||
`;
|
||||
|
||||
export const LaneHeader = styled(Header)`
|
||||
margin-bottom: 0;
|
||||
${(props) =>
|
||||
props.editLaneTitle &&
|
||||
css`
|
||||
padding: 0;
|
||||
line-height: 30px;
|
||||
`} ${(props) =>
|
||||
!props.editLaneTitle &&
|
||||
css`
|
||||
padding: 0 5px;
|
||||
`};
|
||||
`;
|
||||
|
||||
export const LaneFooter = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
height: 10px;
|
||||
${getLaneFooterStyles};
|
||||
`;
|
||||
|
||||
export const ScrollableLane = styled.div`
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from "react";
|
||||
import { DeleteWrapper } from "../styles/Elements";
|
||||
import { Button } from "antd";
|
||||
|
||||
const DeleteButton = (props) => {
|
||||
return (
|
||||
<DeleteWrapper {...props}>
|
||||
<Button type="primary" danger>
|
||||
Delete
|
||||
</Button>
|
||||
</DeleteWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteButton;
|
||||
@@ -1,87 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
class EditableLabel extends React.Component {
|
||||
constructor({value}) {
|
||||
super()
|
||||
this.state = {value: value}
|
||||
}
|
||||
|
||||
getText = el => {
|
||||
return el.innerText
|
||||
}
|
||||
|
||||
onTextChange = ev => {
|
||||
const value = this.getText(ev.target)
|
||||
this.setState({value: value})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autoFocus) {
|
||||
this.refDiv.focus()
|
||||
}
|
||||
}
|
||||
|
||||
onBlur = () => {
|
||||
this.props.onChange(this.state.value)
|
||||
}
|
||||
|
||||
onPaste = ev => {
|
||||
ev.preventDefault()
|
||||
const value = ev.clipboardData.getData('text')
|
||||
document.execCommand('insertText', false, value)
|
||||
}
|
||||
|
||||
getClassName = () => {
|
||||
const placeholder = this.state.value === '' ? 'comPlainTextContentEditable--has-placeholder' : ''
|
||||
return `comPlainTextContentEditable ${placeholder}`
|
||||
}
|
||||
|
||||
onKeyDown = e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.props.onChange(this.state.value)
|
||||
this.refDiv.blur()
|
||||
e.preventDefault()
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.refDiv.value = this.props.value
|
||||
this.setState({value: this.props.value})
|
||||
// this.refDiv.blur()
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const placeholder = this.props.value.length > 0 ? false : this.props.placeholder
|
||||
return (
|
||||
<div
|
||||
ref={ref => (this.refDiv = ref)}
|
||||
contentEditable="true"
|
||||
className={this.getClassName()}
|
||||
onPaste={this.onPaste}
|
||||
onBlur={this.onBlur}
|
||||
onInput={this.onTextChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EditableLabel.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
inline: PropTypes.bool,
|
||||
value: PropTypes.string
|
||||
}
|
||||
|
||||
EditableLabel.defaultProps = {
|
||||
onChange: () => {},
|
||||
placeholder: '',
|
||||
autoFocus: false,
|
||||
inline: false,
|
||||
value: ''
|
||||
}
|
||||
export default EditableLabel
|
||||
@@ -1,106 +0,0 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { InlineInput } from "../styles/Base";
|
||||
import autosize from "autosize";
|
||||
|
||||
const InlineInputController = ({ onSave, border, placeholder, value, autoFocus, resize, onCancel }) => {
|
||||
const inputRef = useRef(null);
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
|
||||
// Effect for autosizing and initial autoFocus
|
||||
useEffect(() => {
|
||||
if (inputRef.current && resize !== "none") {
|
||||
autosize(inputRef.current);
|
||||
}
|
||||
if (inputRef.current && autoFocus) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [resize, autoFocus]);
|
||||
|
||||
// Effect to update value when props change
|
||||
useEffect(() => {
|
||||
setInputValue(value);
|
||||
}, [value]);
|
||||
|
||||
const handleFocus = (e) => e.target.select();
|
||||
|
||||
const handleMouseDown = (e) => {
|
||||
if (document.activeElement !== e.target) {
|
||||
e.preventDefault();
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
updateValue();
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
// Enter
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 27) {
|
||||
// Escape
|
||||
setInputValue(value); // Reset to initial value
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
} else if (e.keyCode === 9) {
|
||||
// Tab
|
||||
if (inputValue.length === 0) {
|
||||
onCancel();
|
||||
}
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const updateValue = () => {
|
||||
if (inputValue !== value) {
|
||||
onSave(inputValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineInput
|
||||
ref={inputRef}
|
||||
border={border}
|
||||
onMouseDown={handleMouseDown}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={inputValue.length === 0 ? undefined : placeholder}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
dataGramm="false"
|
||||
rows={1}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
InlineInputController.propTypes = {
|
||||
onSave: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
border: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
resize: PropTypes.oneOf(["none", "vertical", "horizontal"])
|
||||
};
|
||||
|
||||
InlineInputController.defaultProps = {
|
||||
onSave: () => {},
|
||||
onCancel: () => {},
|
||||
placeholder: "",
|
||||
value: "",
|
||||
border: false,
|
||||
autoFocus: false,
|
||||
resize: "none"
|
||||
};
|
||||
|
||||
export default InlineInputController;
|
||||
@@ -1,94 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { InlineInput } from "../styles/Base";
|
||||
import autosize from "autosize";
|
||||
|
||||
class NewLaneTitleEditor extends React.Component {
|
||||
onKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.refInput.blur();
|
||||
this.props.onSave();
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.keyCode === 27) {
|
||||
this.cancel();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.keyCode === 9) {
|
||||
if (this.getValue().length === 0) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.props.onSave();
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
cancel = () => {
|
||||
this.setValue("");
|
||||
this.props.onCancel();
|
||||
this.refInput.blur();
|
||||
};
|
||||
|
||||
getValue = () => this.refInput.value;
|
||||
setValue = (value) => (this.refInput.value = value);
|
||||
|
||||
saveValue = () => {
|
||||
if (this.getValue() !== this.props.value) {
|
||||
this.props.onSave(this.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
focus = () => this.refInput.focus();
|
||||
|
||||
setRef = (ref) => {
|
||||
this.refInput = ref;
|
||||
if (this.props.resize !== "none") {
|
||||
autosize(this.refInput);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { autoFocus, resize, border, autoResize, value, placeholder } = this.props;
|
||||
|
||||
return (
|
||||
<InlineInput
|
||||
style={{ resize: resize }}
|
||||
ref={this.setRef}
|
||||
border={border}
|
||||
onKeyDown={this.onKeyDown}
|
||||
placeholder={value.length === 0 ? undefined : placeholder}
|
||||
defaultValue={value}
|
||||
rows={3}
|
||||
autoResize={autoResize}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NewLaneTitleEditor.propTypes = {
|
||||
onSave: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
border: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
autoFocus: PropTypes.bool,
|
||||
autoResize: PropTypes.bool,
|
||||
resize: PropTypes.oneOf(["none", "vertical", "horizontal"])
|
||||
};
|
||||
|
||||
NewLaneTitleEditor.defaultProps = {
|
||||
inputRef: () => {},
|
||||
onSave: () => {},
|
||||
onCancel: () => {},
|
||||
placeholder: "",
|
||||
value: "",
|
||||
border: false,
|
||||
autoFocus: false,
|
||||
autoResize: false,
|
||||
resize: "none"
|
||||
};
|
||||
|
||||
export default NewLaneTitleEditor;
|
||||
@@ -1,11 +0,0 @@
|
||||
import DeleteButton from "./DeleteButton";
|
||||
import EditableLabel from "./EditableLabel";
|
||||
import InlineInput from "./InlineInput";
|
||||
|
||||
const exports = {
|
||||
DeleteButton,
|
||||
EditableLabel,
|
||||
InlineInput
|
||||
};
|
||||
|
||||
export default exports;
|
||||
Reference in New Issue
Block a user