feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev
2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions
+107
View File
@@ -0,0 +1,107 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { useInView } from 'react-intersection-observer';
import { useTranslation } from 'react-i18next';
import { Button, Loader } from 'semantic-ui-react';
import selectors from '../../../selectors';
import { BoardMembershipRoles } from '../../../constants/Enums';
import Card from '../../cards/Card';
import AddCard from '../../cards/AddCard';
import PlusMathIcon from '../../../assets/images/plus-math-icon.svg?react';
import styles from './ListView.module.scss';
const ListView = React.memo(
({ cardIds, isCardsFetching, isAllCardsFetched, onCardsFetch, onCardCreate }) => {
const canAddCard = useSelector((state) => {
const boardMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
return !!boardMembership && boardMembership.role === BoardMembershipRoles.EDITOR;
});
const [t] = useTranslation();
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
const [inViewRef] = useInView({
threshold: 1,
onChange: (inView) => {
if (inView && onCardsFetch) {
onCardsFetch();
}
},
});
const handleAddCardClick = useCallback(() => {
setIsAddCardOpened(true);
}, []);
const handleAddCardClose = useCallback(() => {
setIsAddCardOpened(false);
}, []);
return (
<div className={styles.wrapper}>
{canAddCard &&
(onCardCreate && isAddCardOpened ? (
<div className={styles.segment}>
<AddCard onCreate={onCardCreate} onClose={handleAddCardClose} />
</div>
) : (
<Button
type="button"
disabled={!onCardCreate}
className={styles.addCardButton}
onClick={handleAddCardClick}
>
<PlusMathIcon className={styles.addCardButtonIcon} />
<span className={styles.addCardButtonText}>
{onCardCreate ? t('action.addCard') : t('common.atLeastOneListMustBePresent')}
</span>
</Button>
))}
{cardIds.length > 0 && (
<div className={classNames(styles.segment, styles.cards)}>
{cardIds.map((cardId, cardIndex) => (
<div key={cardId} className={styles.card}>
<Card isInline id={cardId} index={cardIndex} />
</div>
))}
</div>
)}
{isCardsFetching !== undefined && isAllCardsFetched !== undefined && (
<div className={styles.loaderWrapper}>
{isCardsFetching ? (
<Loader active inverted inline="centered" size="small" />
) : (
!isAllCardsFetched && <div ref={inViewRef} />
)}
</div>
)}
</div>
);
},
);
ListView.propTypes = {
cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
isCardsFetching: PropTypes.bool,
isAllCardsFetched: PropTypes.bool,
onCardsFetch: PropTypes.func,
onCardCreate: PropTypes.func,
};
ListView.defaultProps = {
isCardsFetching: undefined,
isAllCardsFetched: undefined,
onCardsFetch: undefined,
onCardCreate: undefined,
};
export default ListView;