- 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 { ExclamationCircleFilled } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Dropdown } from "antd";
|
import { Button } from "antd";
|
||||||
import React, { useCallback, useMemo } from "react";
|
import React, { useCallback } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -16,58 +16,45 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
|
const ProductionListColumnAlert = ({ record, insertAuditTrail }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [updateAlert] = useMutation(UPDATE_JOB);
|
const [updateAlert] = useMutation(UPDATE_JOB);
|
||||||
|
|
||||||
const handleAlertToggle = useCallback(() => {
|
const handleAlertToggle = useCallback(() => {
|
||||||
logImEXEvent("production_toggle_alert");
|
logImEXEvent("production_toggle_alert");
|
||||||
|
|
||||||
|
const newAlertState = !!record.production_vars?.alert ? !record.production_vars.alert : true;
|
||||||
|
|
||||||
updateAlert({
|
updateAlert({
|
||||||
variables: {
|
variables: {
|
||||||
jobId: record.id,
|
jobId: record.id,
|
||||||
job: {
|
job: {
|
||||||
production_vars: {
|
production_vars: {
|
||||||
...record.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({
|
insertAuditTrail({
|
||||||
jobid: record.id,
|
jobid: record.id,
|
||||||
operation: AuditTrailMapping.alertToggle(
|
operation: AuditTrailMapping.alertToggle(newAlertState),
|
||||||
!!record.production_vars && !!record.production_vars.alert ? !record.production_vars.alert : true
|
|
||||||
),
|
|
||||||
type: "alertToggle"
|
type: "alertToggle"
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (record.refetch) record.refetch();
|
if (record.refetch) record.refetch();
|
||||||
});
|
});
|
||||||
}, [updateAlert, insertAuditTrail, record]);
|
}, [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 (
|
return (
|
||||||
<Dropdown menu={{ items: menuItems }} trigger={["contextMenu"]}>
|
<div style={{ height: "19px" }}>
|
||||||
<div style={{ height: "19px" }}>
|
{record.production_vars?.alert && (
|
||||||
{record.production_vars && record.production_vars.alert ? (
|
<Button className="production-alert" icon={<ExclamationCircleFilled />} onClick={handleAlertToggle} />
|
||||||
<ExclamationCircleFilled className="production-alert" />
|
)}
|
||||||
) : null}
|
</div>
|
||||||
</div>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ProductionListColumnAlert);
|
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 LaneFooter from "./Lane/LaneFooter";
|
||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
import Loader from "./Loader.jsx";
|
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";
|
import { BoardWrapper, StyleHorizontal, GlobalStyle, StyleVertical, ScrollableLane, Section } from "../styles/Base";
|
||||||
|
|
||||||
const exports = {
|
const exports = {
|
||||||
@@ -15,14 +11,9 @@ const exports = {
|
|||||||
BoardWrapper,
|
BoardWrapper,
|
||||||
Loader,
|
Loader,
|
||||||
ScrollableLane,
|
ScrollableLane,
|
||||||
LaneHeader,
|
|
||||||
LaneFooter,
|
LaneFooter,
|
||||||
Section,
|
Section,
|
||||||
NewLaneForm,
|
Card
|
||||||
NewLaneSection,
|
|
||||||
NewCardForm,
|
|
||||||
Card,
|
|
||||||
AddCardLink
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default exports;
|
export default exports;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { BoardContainer } from "../index";
|
import { BoardContainer } from "../index";
|
||||||
import classNames from "classnames";
|
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { v1 } from "uuid";
|
import { v1 } from "uuid";
|
||||||
|
|
||||||
const Board = ({ id, className, components, orientation, ...additionalProps }) => {
|
const Board = ({ id, className, components, orientation, ...additionalProps }) => {
|
||||||
const [storeId] = useState(id || v1());
|
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(
|
const OrientationStyle = useMemo(
|
||||||
() => (orientation === "horizontal" ? components.StyleHorizontal : components.StyleVertical),
|
() => (orientation === "horizontal" ? components.StyleHorizontal : components.StyleVertical),
|
||||||
[orientation, components.StyleHorizontal, components.StyleVertical]
|
[orientation, components.StyleHorizontal, components.StyleVertical]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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";
|
||||||
|
import { BoardWrapper } from "../styles/Base.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BoardContainer is a React component that represents a Trello-like board.
|
* 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 {string} props.id - The unique identifier for the board
|
||||||
* @param {Object} props.components - Custom components to use in the board
|
* @param {Object} props.components - Custom components to use in the board
|
||||||
* @param {Object} props.data - The initial data for 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 {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 {string} props.orientation - The orientation of the board ("horizontal" or "vertical")
|
||||||
* @param {Function} props.eventBusHandle - Function to handle events from the event bus
|
* @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.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
|
* @param {Object} props.otherProps - Any other props to pass to the board
|
||||||
* @returns {JSX.Element} A Trello-like board
|
* @returns {JSX.Element} A Trello-like board
|
||||||
*/
|
*/
|
||||||
@@ -35,17 +29,11 @@ const BoardContainer = ({
|
|||||||
id,
|
id,
|
||||||
components,
|
components,
|
||||||
data,
|
data,
|
||||||
draggable = false,
|
|
||||||
style,
|
|
||||||
onDataChange = () => {},
|
onDataChange = () => {},
|
||||||
onDragEnd = () => {},
|
onDragEnd = () => {},
|
||||||
editable = false,
|
|
||||||
canAddLanes = false,
|
|
||||||
laneStyle,
|
|
||||||
orientation = "horizontal",
|
orientation = "horizontal",
|
||||||
eventBusHandle,
|
eventBusHandle,
|
||||||
reducerData,
|
reducerData,
|
||||||
cardStyle,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
}) => {
|
}) => {
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
@@ -60,12 +48,20 @@ const BoardContainer = ({
|
|||||||
const eventBus = {
|
const eventBus = {
|
||||||
publish: (event) => {
|
publish: (event) => {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "ADD_CARD":
|
// case "ADD_CARD":
|
||||||
return dispatch(actions.addCard({ laneId: event.laneId, card: event.card }));
|
// return dispatch(actions.addCard({ laneId: event.laneId, card: event.card }));
|
||||||
case "REMOVE_CARD":
|
// case "REMOVE_CARD":
|
||||||
return dispatch(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 dispatch(actions.loadBoard(event.data));
|
// 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":
|
case "MOVE_CARD":
|
||||||
return dispatch(
|
return dispatch(
|
||||||
actions.moveCardAcrossLanes({
|
actions.moveCardAcrossLanes({
|
||||||
@@ -76,14 +72,7 @@ const BoardContainer = ({
|
|||||||
event
|
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:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -109,13 +98,6 @@ const BoardContainer = ({
|
|||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
}, [setIsDragging]);
|
}, [setIsDragging]);
|
||||||
|
|
||||||
const getCardDetails = useCallback(
|
|
||||||
(laneId, cardIndex) => {
|
|
||||||
return currentReducerData.lanes.find((lane) => lane.id === laneId).cards[cardIndex];
|
|
||||||
},
|
|
||||||
[currentReducerData]
|
|
||||||
);
|
|
||||||
|
|
||||||
const passThroughProps = useMemo(
|
const passThroughProps = useMemo(
|
||||||
() =>
|
() =>
|
||||||
pick(
|
pick(
|
||||||
@@ -123,59 +105,15 @@ const BoardContainer = ({
|
|||||||
id,
|
id,
|
||||||
components,
|
components,
|
||||||
data,
|
data,
|
||||||
draggable,
|
|
||||||
style,
|
|
||||||
onDataChange,
|
onDataChange,
|
||||||
editable,
|
|
||||||
canAddLanes,
|
|
||||||
laneStyle,
|
|
||||||
orientation,
|
orientation,
|
||||||
eventBusHandle,
|
eventBusHandle,
|
||||||
reducerData,
|
reducerData,
|
||||||
cardStyle,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
},
|
},
|
||||||
[
|
["laneSortFunction", "collapsibleLanes", "orientation"]
|
||||||
"onLaneScroll",
|
|
||||||
"onLaneDelete",
|
|
||||||
"onLaneUpdate",
|
|
||||||
"onCardClick",
|
|
||||||
"onBeforeCardDelete",
|
|
||||||
"onCardDelete",
|
|
||||||
"onCardAdd",
|
|
||||||
"onCardUpdate",
|
|
||||||
"onLaneClick",
|
|
||||||
"laneSortFunction",
|
|
||||||
"draggable",
|
|
||||||
"cardDraggable",
|
|
||||||
"collapsibleLanes",
|
|
||||||
"canAddLanes",
|
|
||||||
"hideCardDeleteIcon",
|
|
||||||
"tagStyle",
|
|
||||||
"handleDragStart",
|
|
||||||
"handleDragEnd",
|
|
||||||
"cardDragClass",
|
|
||||||
"editLaneTitle",
|
|
||||||
"orientation"
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
[
|
[id, components, data, onDataChange, orientation, eventBusHandle, reducerData, otherProps]
|
||||||
id,
|
|
||||||
components,
|
|
||||||
data,
|
|
||||||
draggable,
|
|
||||||
style,
|
|
||||||
onDataChange,
|
|
||||||
|
|
||||||
editable,
|
|
||||||
canAddLanes,
|
|
||||||
laneStyle,
|
|
||||||
orientation,
|
|
||||||
eventBusHandle,
|
|
||||||
reducerData,
|
|
||||||
cardStyle,
|
|
||||||
otherProps
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLaneDrag = useCallback(
|
const onLaneDrag = useCallback(
|
||||||
@@ -207,7 +145,7 @@ const BoardContainer = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<components.BoardWrapper style={style} orientation={orientation}>
|
<BoardWrapper orientation={orientation}>
|
||||||
<PopoverWrapper>
|
<PopoverWrapper>
|
||||||
<DragDropContext onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId={groupName}>
|
<DragDropContext onDragEnd={onLaneDrag} onDragStart={onDragStart} contextId={groupName}>
|
||||||
{currentReducerData.lanes.map((lane, index) => {
|
{currentReducerData.lanes.map((lane, index) => {
|
||||||
@@ -218,13 +156,7 @@ const BoardContainer = ({
|
|||||||
boardId={groupName}
|
boardId={groupName}
|
||||||
components={components}
|
components={components}
|
||||||
id={id}
|
id={id}
|
||||||
getCardDetails={getCardDetails}
|
|
||||||
index={index}
|
index={index}
|
||||||
droppable={droppable === undefined ? true : droppable}
|
|
||||||
style={laneStyle || lane.style || {}}
|
|
||||||
labelStyle={lane.labelStyle || {}}
|
|
||||||
cardStyle={cardStyle || lane.cardStyle}
|
|
||||||
editable={editable && !lane.disallowAddingCard}
|
|
||||||
{...laneOtherProps}
|
{...laneOtherProps}
|
||||||
{...passThroughProps}
|
{...passThroughProps}
|
||||||
cards={lane.cards}
|
cards={lane.cards}
|
||||||
@@ -235,7 +167,7 @@ const BoardContainer = ({
|
|||||||
})}
|
})}
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</PopoverWrapper>
|
</PopoverWrapper>
|
||||||
</components.BoardWrapper>
|
</BoardWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -247,30 +179,10 @@ BoardContainer.propTypes = {
|
|||||||
reducerData: PropTypes.object,
|
reducerData: PropTypes.object,
|
||||||
onDataChange: PropTypes.func,
|
onDataChange: PropTypes.func,
|
||||||
eventBusHandle: 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,
|
laneSortFunction: PropTypes.func,
|
||||||
draggable: PropTypes.bool,
|
|
||||||
collapsibleLanes: PropTypes.bool,
|
collapsibleLanes: PropTypes.bool,
|
||||||
editable: PropTypes.bool,
|
|
||||||
canAddLanes: PropTypes.bool,
|
|
||||||
hideCardDeleteIcon: PropTypes.bool,
|
|
||||||
handleDragStart: PropTypes.func,
|
|
||||||
handleDragEnd: PropTypes.func,
|
handleDragEnd: PropTypes.func,
|
||||||
style: PropTypes.object,
|
orientation: PropTypes.string
|
||||||
tagStyle: PropTypes.object,
|
|
||||||
cardDraggable: PropTypes.bool,
|
|
||||||
cardDragClass: PropTypes.string,
|
|
||||||
orientation: PropTypes.string,
|
|
||||||
cardStyle: PropTypes.object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BoardContainer;
|
export default BoardContainer;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React, { forwardRef, useCallback, useMemo, useState, useEffect } from "react";
|
import React, { forwardRef, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import classNames from "classnames";
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { bindActionCreators } from "redux";
|
import { bindActionCreators } from "redux";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { v1 } from "uuid";
|
|
||||||
|
|
||||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||||
import { Draggable, Droppable } from "../dnd/lib";
|
import { Draggable, Droppable } from "../dnd/lib";
|
||||||
import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
|
import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
|
||||||
import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx";
|
import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx";
|
||||||
|
import { Section } from "../styles/Base.js";
|
||||||
|
import LaneFooter from "../components/Lane/LaneFooter.jsx";
|
||||||
|
|
||||||
const Lane = ({
|
const Lane = ({
|
||||||
actions,
|
actions,
|
||||||
@@ -20,36 +20,14 @@ const Lane = ({
|
|||||||
laneSortFunction,
|
laneSortFunction,
|
||||||
style = {},
|
style = {},
|
||||||
cardStyle = {},
|
cardStyle = {},
|
||||||
tagStyle = {},
|
|
||||||
titleStyle = {},
|
|
||||||
labelStyle = {},
|
|
||||||
cards,
|
cards,
|
||||||
label,
|
|
||||||
draggable = false,
|
|
||||||
collapsibleLanes = 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 = {},
|
components = {},
|
||||||
orientation = "vertical",
|
orientation = "vertical",
|
||||||
className,
|
className,
|
||||||
currentPage,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
}) => {
|
}) => {
|
||||||
const [addCardMode, setAddCardMode] = useState(false);
|
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
const [isVisible, setIsVisible] = useState(true);
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
|
|
||||||
@@ -65,76 +43,11 @@ const Lane = ({
|
|||||||
return cards.concat().sort((card1, card2) => sortFunction(card1, card2));
|
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(() => {
|
const toggleLaneCollapsed = useCallback(() => {
|
||||||
collapsibleLanes && setCollapsed(!collapsed);
|
collapsibleLanes && setCollapsed(!collapsed);
|
||||||
}, [collapsibleLanes, collapsed]);
|
}, [collapsibleLanes, collapsed]);
|
||||||
|
|
||||||
const Card = React.memo(({ provided, item: card, isDragging }) => {
|
const Card = React.memo(({ provided, item: card, isDragging }) => {
|
||||||
const onDeleteCard = () => removeCard(card.id);
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...provided.draggableProps}
|
{...provided.draggableProps}
|
||||||
@@ -144,19 +57,7 @@ const Lane = ({
|
|||||||
className={`item ${isDragging ? "is-dragging" : ""}`}
|
className={`item ${isDragging ? "is-dragging" : ""}`}
|
||||||
key={card.id}
|
key={card.id}
|
||||||
>
|
>
|
||||||
<components.Card
|
<components.Card key={card.id} style={card.style || cardStyle} className="react-trello-card" {...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}
|
|
||||||
/>
|
|
||||||
</div>
|
</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 }) => (
|
const ItemWrapper = ({ children, ...props }) => (
|
||||||
<div {...props} className="item-wrapper">
|
<div {...props} className="item-wrapper">
|
||||||
{children}
|
{children}
|
||||||
@@ -196,43 +87,46 @@ const Lane = ({
|
|||||||
|
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
useWindowScroll: true,
|
useWindowScroll: true,
|
||||||
data: renderedCards,
|
data: renderedCards
|
||||||
overscan: {
|
|
||||||
main: 22,
|
|
||||||
reverse: 22
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const componentProps =
|
const verticalProps = {
|
||||||
orientation === "vertical"
|
...commonProps,
|
||||||
? {
|
// we are using the useWindowScroll, so we don't need to pass the scrollerRef
|
||||||
...commonProps,
|
// scrollerRef: provided.innerRef,
|
||||||
scrollerRef: provided.innerRef,
|
listClassName: "grid-container",
|
||||||
listClassName: "grid-container",
|
itemClassName: "grid-item",
|
||||||
itemClassName: "grid-item",
|
components: {
|
||||||
components: {
|
List: forwardRef(({ style, children, ...props }, ref) => (
|
||||||
List: forwardRef(({ style, children, ...props }, ref) => (
|
<div
|
||||||
<div
|
ref={ref}
|
||||||
ref={ref}
|
{...props}
|
||||||
{...props}
|
style={{
|
||||||
style={{
|
...style
|
||||||
...style
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{children}
|
||||||
{children}
|
</div>
|
||||||
</div>
|
)),
|
||||||
)),
|
Item: ({ children, ...props }) => <div {...props}>{children}</div>
|
||||||
Item: ({ children, ...props }) => <div {...props}>{children}</div>
|
},
|
||||||
},
|
itemContent: (index, item) => <ItemWrapper>{renderDraggable(index, item)}</ItemWrapper>,
|
||||||
itemContent: (index, item) => <ItemWrapper>{renderDraggable(index, item)}</ItemWrapper>
|
overscan: { main: 10, reverse: 10 }
|
||||||
}
|
};
|
||||||
: {
|
|
||||||
...commonProps,
|
const horizontalProps = {
|
||||||
components: { Item: HeightPreservingItem },
|
...commonProps,
|
||||||
itemContent: (index, item) => renderDraggable(index, item),
|
components: { Item: HeightPreservingItem },
|
||||||
scrollerRef: provided.innerRef
|
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;
|
const finalComponentProps = collapsed ? {} : componentProps;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: "100%" }}>
|
<div style={{ height: "100%" }}>
|
||||||
<div
|
<div
|
||||||
@@ -244,8 +138,6 @@ const Lane = ({
|
|||||||
{isVisible && <FinalComponent {...finalComponentProps} />}
|
{isVisible && <FinalComponent {...finalComponentProps} />}
|
||||||
{(orientation === "horizontal" || renderedCards.length === 0 || collapsed) && provided.placeholder}
|
{(orientation === "horizontal" || renderedCards.length === 0 || collapsed) && provided.placeholder}
|
||||||
</div>
|
</div>
|
||||||
{renderAddCardLink}
|
|
||||||
{renderNewCardForm}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -275,18 +167,13 @@ const Lane = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderHeader = (pickedProps) => {
|
const renderHeader = (pickedProps) => {
|
||||||
return (
|
return <components.LaneHeader {...pickedProps} onDoubleClick={toggleLaneCollapsed} />;
|
||||||
<components.LaneHeader
|
|
||||||
{...pickedProps}
|
|
||||||
onDelete={removeLane}
|
|
||||||
onDoubleClick={toggleLaneCollapsed}
|
|
||||||
updateTitle={updateTitle}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = {
|
const passedProps = {
|
||||||
actions,
|
actions,
|
||||||
id,
|
id,
|
||||||
@@ -296,37 +183,20 @@ const Lane = ({
|
|||||||
laneSortFunction,
|
laneSortFunction,
|
||||||
style,
|
style,
|
||||||
cardStyle,
|
cardStyle,
|
||||||
tagStyle,
|
|
||||||
titleStyle,
|
|
||||||
labelStyle,
|
|
||||||
cards,
|
cards,
|
||||||
label,
|
|
||||||
draggable,
|
|
||||||
collapsibleLanes,
|
collapsibleLanes,
|
||||||
droppable,
|
|
||||||
editable,
|
|
||||||
cardDraggable,
|
|
||||||
cardDragClass,
|
|
||||||
cardDropClass,
|
|
||||||
canAddLanes,
|
|
||||||
hideCardDeleteIcon,
|
|
||||||
components,
|
components,
|
||||||
orientation,
|
orientation,
|
||||||
className,
|
className,
|
||||||
currentPage,
|
|
||||||
...otherProps
|
...otherProps
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<components.Section
|
<Section key={id} orientation={orientation} {...passedProps}>
|
||||||
key={id}
|
|
||||||
onClick={() => onLaneClick && onLaneClick(id)}
|
|
||||||
orientation={orientation}
|
|
||||||
{...passedProps}
|
|
||||||
>
|
|
||||||
{renderHeader({ id, cards, ...passedProps })}
|
{renderHeader({ id, cards, ...passedProps })}
|
||||||
{renderDragContainer()}
|
{renderDragContainer()}
|
||||||
{collapsibleLanes && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
{collapsibleLanes && <LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
||||||
</components.Section>
|
</Section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -339,20 +209,9 @@ Lane.propTypes = {
|
|||||||
laneSortFunction: PropTypes.func,
|
laneSortFunction: PropTypes.func,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
cardStyle: PropTypes.object,
|
cardStyle: PropTypes.object,
|
||||||
tagStyle: PropTypes.object,
|
|
||||||
titleStyle: PropTypes.object,
|
|
||||||
labelStyle: PropTypes.object,
|
|
||||||
cards: PropTypes.array,
|
cards: PropTypes.array,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
draggable: PropTypes.bool,
|
|
||||||
collapsibleLanes: 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,
|
components: PropTypes.object,
|
||||||
orientation: PropTypes.string
|
orientation: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
import update from "immutability-helper";
|
import update from "immutability-helper";
|
||||||
import cloneDeep from "lodash/cloneDeep";
|
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 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 = {
|
const LaneHelper = {
|
||||||
|
/**
|
||||||
|
* Initialise the lanes
|
||||||
|
* @param state
|
||||||
|
* @param lanes
|
||||||
|
* @returns {unknown}
|
||||||
|
*/
|
||||||
initialiseLanes: (state, { lanes }) => {
|
initialiseLanes: (state, { lanes }) => {
|
||||||
const newLanes = lanes.map((lane) => {
|
const newLanes = lanes.map((lane) => {
|
||||||
lane.currentPage = 1;
|
lane.currentPage = 1;
|
||||||
@@ -14,79 +29,6 @@ const LaneHelper = {
|
|||||||
return updateLanes(state, newLanes);
|
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
|
* Move a card from one lane to another
|
||||||
* @param state
|
* @param state
|
||||||
@@ -136,28 +78,104 @@ const LaneHelper = {
|
|||||||
return update(state, {
|
return update(state, {
|
||||||
lanes: { $set: newLanes }
|
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;
|
export default LaneHelper;
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ const getSectionStyles = (props) => {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
// overflow-y: none;
|
// overflow-y: none;
|
||||||
min-width: 8.5%;
|
min-width: 8.5%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: stretch;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return `
|
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`
|
export const GlobalStyle = createGlobalStyle`
|
||||||
.comPlainTextContentEditable {
|
.comPlainTextContentEditable {
|
||||||
-webkit-user-modify: read-write-plaintext-only;
|
-webkit-user-modify: read-write-plaintext-only;
|
||||||
@@ -84,15 +100,18 @@ export const StyleHorizontal = styled.div`
|
|||||||
.react-trello-lane.lane-collapsed {
|
.react-trello-lane.lane-collapsed {
|
||||||
min-height: 15px;
|
min-height: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-wrapper {
|
.item-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
min-width: 4%;
|
min-width: 4%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-trello-card {
|
.react-trello-card {
|
||||||
height: auto;
|
height: auto;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
@@ -118,10 +137,12 @@ export const StyleVertical = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-container {
|
.grid-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-item {
|
.grid-item {
|
||||||
width: 8%; // TODO: THIS IS WHERE WE GET VERTICAL CARD CUSTOMIZATION
|
width: 8%; // TODO: THIS IS WHERE WE GET VERTICAL CARD CUSTOMIZATION
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -133,6 +154,7 @@ export const StyleVertical = styled.div`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-wrapper {
|
.item-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -219,27 +241,12 @@ export const Section = styled.section`
|
|||||||
${getSectionStyles};
|
${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`
|
export const LaneFooter = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
${getLaneFooterStyles};
|
||||||
height: 10px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ScrollableLane = styled.div`
|
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