1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-18 20:59:44 +02:00

Change user size on card, save card description on click outside, prevent list deletion if any filter is active

This commit is contained in:
Maksim Eltyshev 2020-04-30 05:27:46 +05:00
parent febc614ce8
commit ba2017705b
11 changed files with 74 additions and 38 deletions

View file

@ -61,7 +61,7 @@ const Filter = React.memo(
<User <User
name={user.name} name={user.name}
avatarUrl={user.avatarUrl} avatarUrl={user.avatarUrl}
size="small" size="tiny"
onClick={() => handleUserRemoveClick(user.id)} onClick={() => handleUserRemoveClick(user.id)}
/> />
</span> </span>

View file

@ -73,7 +73,7 @@
.tab { .tab {
border-radius: 3px 3px 0 0; border-radius: 3px 3px 0 0;
min-width: 160px; min-width: 100px;
position: relative; position: relative;
transition: all 0.1s ease; transition: all 0.1s ease;
} }

View file

@ -112,7 +112,7 @@ const Card = React.memo(
key={user.id} key={user.id}
className={classNames(styles.attachment, styles.attachmentRight)} className={classNames(styles.attachment, styles.attachmentRight)}
> >
<User name={user.name} avatarUrl={user.avatarUrl} size="tiny" /> <User name={user.name} avatarUrl={user.avatarUrl} size="small" />
</span> </span>
))} ))}
</span> </span>

View file

@ -21,19 +21,15 @@ const EditDescription = React.forwardRef(({ children, defaultValue, onUpdate },
}, [defaultValue, setValue]); }, [defaultValue, setValue]);
const close = useCallback(() => { const close = useCallback(() => {
setIsOpened(false);
setValue(null);
}, [setValue]);
const submit = useCallback(() => {
const cleanValue = value.trim() || null; const cleanValue = value.trim() || null;
if (cleanValue !== defaultValue) { if (cleanValue !== defaultValue) {
onUpdate(cleanValue); onUpdate(cleanValue);
} }
close(); setIsOpened(false);
}, [defaultValue, onUpdate, value, close]); setValue(null);
}, [defaultValue, onUpdate, value, setValue]);
useImperativeHandle( useImperativeHandle(
ref, ref,
@ -53,10 +49,10 @@ const EditDescription = React.forwardRef(({ children, defaultValue, onUpdate },
const handleFieldKeyDown = useCallback( const handleFieldKeyDown = useCallback(
(event) => { (event) => {
if (event.ctrlKey && event.key === 'Enter') { if (event.ctrlKey && event.key === 'Enter') {
submit(); close();
} }
}, },
[submit], [close],
); );
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm( const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
@ -65,8 +61,8 @@ const EditDescription = React.forwardRef(({ children, defaultValue, onUpdate },
); );
const handleSubmit = useCallback(() => { const handleSubmit = useCallback(() => {
submit(); close();
}, [submit]); }, [close]);
useEffect(() => { useEffect(() => {
if (isOpened) { if (isOpened) {

View file

@ -65,11 +65,13 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
context: 'title', context: 'title',
})} })}
</Menu.Item> </Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}> {onDelete && (
{t('action.deleteList', { <Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
context: 'title', {t('action.deleteList', {
})} context: 'title',
</Menu.Item> })}
</Menu.Item>
)}
</Menu> </Menu>
</Popup.Content> </Popup.Content>
</> </>
@ -79,8 +81,12 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
ActionsStep.propTypes = { ActionsStep.propTypes = {
onNameEdit: PropTypes.func.isRequired, onNameEdit: PropTypes.func.isRequired,
onCardAdd: PropTypes.func.isRequired, onCardAdd: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired, onDelete: PropTypes.func,
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
}; };
ActionsStep.defaultProps = {
onDelete: undefined,
};
export default withPopup(ActionsStep); export default withPopup(ActionsStep);

View file

@ -114,8 +114,12 @@ List.propTypes = {
isPersisted: PropTypes.bool.isRequired, isPersisted: PropTypes.bool.isRequired,
cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types cardIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
onUpdate: PropTypes.func.isRequired, onUpdate: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired, onDelete: PropTypes.func,
onCardCreate: PropTypes.func.isRequired, onCardCreate: PropTypes.func.isRequired,
}; };
List.defaultProps = {
onDelete: undefined,
};
export default List; export default List;

View file

@ -16,20 +16,20 @@ const SIZES = {
// TODO: move to styles // TODO: move to styles
const STYLES = { const STYLES = {
tiny: { tiny: {
fontSize: '10px',
fontWeight: '400',
height: '20px',
padding: '5.5px 0px 4.5px',
width: '20px',
},
small: {
fontSize: '12px', fontSize: '12px',
fontWeight: '400', fontWeight: '400',
height: '24px', height: '24px',
lineHeight: '20px', lineHeight: '20px',
padding: '2px 0px', padding: '2px 0',
width: '24px', width: '24px',
}, },
small: {
fontSize: '12px',
fontWeight: '400',
height: '28px',
padding: '8px 0',
width: '28px',
},
medium: { medium: {
fontSize: '14px', fontSize: '14px',
fontWeight: '500', fontWeight: '500',

View file

@ -1,7 +1,12 @@
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import omit from 'lodash/omit';
import { makeCardIdsByListIdSelector, makeListByIdSelector } from '../selectors'; import {
isAnyFilterActiveForCurrentBoardSelector,
makeCardIdsByListIdSelector,
makeListByIdSelector,
} from '../selectors';
import { createCard, deleteList, updateList } from '../actions/entry'; import { createCard, deleteList, updateList } from '../actions/entry';
import List from '../components/List'; import List from '../components/List';
@ -12,6 +17,7 @@ const makeMapStateToProps = () => {
return (state, { id, index }) => { return (state, { id, index }) => {
const { name, isPersisted } = listByIdSelector(state, id); const { name, isPersisted } = listByIdSelector(state, id);
const cardIds = cardIdsByListIdSelector(state, id); const cardIds = cardIdsByListIdSelector(state, id);
const isAnyFilterActive = isAnyFilterActiveForCurrentBoardSelector(state);
return { return {
id, id,
@ -19,6 +25,7 @@ const makeMapStateToProps = () => {
name, name,
isPersisted, isPersisted,
cardIds, cardIds,
isDeletable: !isAnyFilterActive,
}; };
}; };
}; };
@ -33,4 +40,9 @@ const mapDispatchToProps = (dispatch, { id }) =>
dispatch, dispatch,
); );
export default connect(makeMapStateToProps, mapDispatchToProps)(List); const mergeProps = (stateProps, dispatchProps) => ({
...omit(stateProps, 'isDeletable'),
...(stateProps.isDeletable ? dispatchProps : omit(dispatchProps, 'onDelete')),
});
export default connect(makeMapStateToProps, mapDispatchToProps, mergeProps)(List);

View file

@ -5,10 +5,10 @@ import { logoutService } from './login';
import { closeModalService } from './modal'; import { closeModalService } from './modal';
import { deleteNotificationsRequest, fetchUsersRequest } from '../requests'; import { deleteNotificationsRequest, fetchUsersRequest } from '../requests';
import { import {
attachmentWithIdExistsSelector,
currentModalSelector, currentModalSelector,
currentUserIdSelector, currentUserIdSelector,
currentUserSelector, currentUserSelector,
isAttachmentWithIdExistsSelector,
pathSelector, pathSelector,
} from '../../../selectors'; } from '../../../selectors';
import { import {
@ -210,9 +210,9 @@ export function* deleteTaskReceivedService(task) {
} }
export function* createAttachmentReceivedService(attachment, requestId) { export function* createAttachmentReceivedService(attachment, requestId) {
const exists = yield select(attachmentWithIdExistsSelector, requestId); const isExists = yield select(isAttachmentWithIdExistsSelector, requestId);
if (!exists) { if (!isExists) {
yield put(createAttachmentReceived(attachment)); yield put(createAttachmentReceived(attachment));
} }
} }

View file

@ -1,9 +1,27 @@
import { createSelector } from 'redux-orm'; import { createSelector } from 'redux-orm';
import orm from '../orm'; import orm from '../orm';
import { pathSelector } from './path';
// eslint-disable-next-line import/prefer-default-export export const isAnyFilterActiveForCurrentBoardSelector = createSelector(
export const attachmentWithIdExistsSelector = () => orm,
(state) => pathSelector(state).boardId,
({ Board }, id) => {
if (!id) {
return false;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return false;
}
return boardModel.filterUsers.exists() || boardModel.filterLabels.exists();
},
);
export const isAttachmentWithIdExistsSelector = () =>
createSelector( createSelector(
orm, orm,
(_, id) => id, (_, id) => id,

View file

@ -4,9 +4,9 @@ const knex = require('knex')(config); // eslint-disable-line import/order
(async () => { (async () => {
try { try {
const exists = await knex.schema.hasTable(config.migrations.tableName); const isExists = await knex.schema.hasTable(config.migrations.tableName);
if (!exists) { if (!isExists) {
await knex.migrate.latest(); await knex.migrate.latest();
await knex.seed.run(); await knex.seed.run();
} }