mirror of
https://github.com/plankanban/planka.git
synced 2025-07-19 05:09:43 +02:00
Make columns itself scrollable, fix action creation when moving card, little refactoring
This commit is contained in:
parent
66c570f234
commit
3a1929efba
44 changed files with 549 additions and 438 deletions
|
@ -1,26 +1,29 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import HeaderContainer from '../containers/HeaderContainer';
|
import ModalTypes from '../constants/ModalTypes';
|
||||||
import ProjectsContainer from '../containers/ProjectsContainer';
|
import FixedWrapperContainer from '../containers/FixedWrapperContainer';
|
||||||
|
import StaticWrapperContainer from '../containers/StaticWrapperContainer';
|
||||||
import UsersModalContainer from '../containers/UsersModalContainer';
|
import UsersModalContainer from '../containers/UsersModalContainer';
|
||||||
import UserSettingsModalContainer from '../containers/UserSettingsModalContainer';
|
import UserSettingsModalContainer from '../containers/UserSettingsModalContainer';
|
||||||
import AddProjectModalContainer from '../containers/AddProjectModalContainer';
|
import AddProjectModalContainer from '../containers/AddProjectModalContainer';
|
||||||
|
|
||||||
const App = ({ isUsersModalOpened, isUserSettingsModalOpened, isAddProjectModalOpened }) => (
|
const App = ({ currentModal }) => (
|
||||||
<>
|
<>
|
||||||
<HeaderContainer />
|
<FixedWrapperContainer />
|
||||||
<ProjectsContainer />
|
<StaticWrapperContainer />
|
||||||
{isUsersModalOpened && <UsersModalContainer />}
|
{currentModal === ModalTypes.USERS && <UsersModalContainer />}
|
||||||
{isUserSettingsModalOpened && <UserSettingsModalContainer />}
|
{currentModal === ModalTypes.USER_SETTINGS && <UserSettingsModalContainer />}
|
||||||
{isAddProjectModalOpened && <AddProjectModalContainer />}
|
{currentModal === ModalTypes.ADD_PROJECT && <AddProjectModalContainer />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
isUsersModalOpened: PropTypes.bool.isRequired,
|
currentModal: PropTypes.oneOf(Object.values(ModalTypes)),
|
||||||
isUserSettingsModalOpened: PropTypes.bool.isRequired,
|
};
|
||||||
isAddProjectModalOpened: PropTypes.bool.isRequired,
|
|
||||||
|
App.defaultProps = {
|
||||||
|
currentModal: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Input } from 'semantic-ui-react';
|
import { Button, Form, Input } from 'semantic-ui-react';
|
||||||
|
@ -12,48 +12,23 @@ const DEFAULT_DATA = {
|
||||||
name: '',
|
name: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddList = React.forwardRef(({ children, onCreate }, ref) => {
|
const AddList = React.memo(({ onCreate, onClose }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isOpened, setIsOpened] = useState(false);
|
|
||||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||||
const [selectNameFieldState, selectNameField] = useToggle();
|
const [selectNameFieldState, selectNameField] = useToggle();
|
||||||
|
|
||||||
const nameField = useRef(null);
|
const nameField = useRef(null);
|
||||||
|
|
||||||
const open = useCallback(() => {
|
|
||||||
setIsOpened(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const close = useCallback(() => {
|
|
||||||
setIsOpened(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useImperativeHandle(
|
|
||||||
ref,
|
|
||||||
() => ({
|
|
||||||
open,
|
|
||||||
close,
|
|
||||||
}),
|
|
||||||
[open, close],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChildrenClick = useCallback(() => {
|
|
||||||
open();
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const handleFieldKeyDown = useCallback(
|
const handleFieldKeyDown = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
close();
|
onClose();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[close],
|
[onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(onClose);
|
||||||
isOpened,
|
|
||||||
close,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
const cleanData = {
|
const cleanData = {
|
||||||
|
@ -73,21 +48,13 @@ const AddList = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
}, [onCreate, data, setData, selectNameField]);
|
}, [onCreate, data, setData, selectNameField]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpened) {
|
|
||||||
nameField.current.select();
|
nameField.current.select();
|
||||||
}
|
}, []);
|
||||||
}, [isOpened]);
|
|
||||||
|
|
||||||
useDidUpdate(() => {
|
useDidUpdate(() => {
|
||||||
nameField.current.select();
|
nameField.current.select();
|
||||||
}, [selectNameFieldState]);
|
}, [selectNameFieldState]);
|
||||||
|
|
||||||
if (!isOpened) {
|
|
||||||
return React.cloneElement(children, {
|
|
||||||
onClick: handleChildrenClick,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className={styles.wrapper} onSubmit={handleSubmit}>
|
<Form className={styles.wrapper} onSubmit={handleSubmit}>
|
||||||
<Input
|
<Input
|
||||||
|
@ -115,8 +82,8 @@ const AddList = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
AddList.propTypes = {
|
AddList.propTypes = {
|
||||||
children: PropTypes.element.isRequired,
|
|
||||||
onCreate: PropTypes.func.isRequired,
|
onCreate: PropTypes.func.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(AddList);
|
export default AddList;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||||
|
@ -35,6 +35,18 @@ const Board = React.memo(
|
||||||
onLabelDelete,
|
onLabelDelete,
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
const [isAddListOpened, setIsAddListOpened] = useState(false);
|
||||||
|
|
||||||
|
const wrapper = useRef(null);
|
||||||
|
const prevPosition = useRef(null);
|
||||||
|
|
||||||
|
const handleAddListClick = useCallback(() => {
|
||||||
|
setIsAddListOpened(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddListClose = useCallback(() => {
|
||||||
|
setIsAddListOpened(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleDragStart = useCallback(() => {
|
const handleDragStart = useCallback(() => {
|
||||||
closePopup();
|
closePopup();
|
||||||
|
@ -66,8 +78,58 @@ const Board = React.memo(
|
||||||
[onListMove, onCardMove],
|
[onListMove, onCardMove],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleMouseDown = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (event.target !== wrapper.current && !event.target.dataset.dragScroller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPosition.current = event.clientX;
|
||||||
|
},
|
||||||
|
[wrapper],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleWindowMouseMove = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (!prevPosition.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
window.scrollBy({
|
||||||
|
left: prevPosition.current - event.clientX,
|
||||||
|
});
|
||||||
|
|
||||||
|
prevPosition.current = event.clientX;
|
||||||
|
},
|
||||||
|
[prevPosition],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleWindowMouseUp = useCallback(() => {
|
||||||
|
prevPosition.current = null;
|
||||||
|
}, [prevPosition]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAddListOpened) {
|
||||||
|
window.scroll(document.body.scrollWidth, 0);
|
||||||
|
}
|
||||||
|
}, [listIds, isAddListOpened]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('mouseup', handleWindowMouseUp);
|
||||||
|
window.addEventListener('mousemove', handleWindowMouseMove);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mouseup', handleWindowMouseUp);
|
||||||
|
window.removeEventListener('mousemove', handleWindowMouseMove);
|
||||||
|
};
|
||||||
|
}, [handleWindowMouseUp, handleWindowMouseMove]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||||
|
<div ref={wrapper} className={styles.wrapper} onMouseDown={handleMouseDown}>
|
||||||
<Filter
|
<Filter
|
||||||
users={filterUsers}
|
users={filterUsers}
|
||||||
labels={filterLabels}
|
labels={filterLabels}
|
||||||
|
@ -81,31 +143,46 @@ const Board = React.memo(
|
||||||
onLabelUpdate={onLabelUpdate}
|
onLabelUpdate={onLabelUpdate}
|
||||||
onLabelDelete={onLabelDelete}
|
onLabelDelete={onLabelDelete}
|
||||||
/>
|
/>
|
||||||
<div className={styles.wrapper}>
|
<div>
|
||||||
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
<DragDropContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
|
||||||
<Droppable droppableId="board" type={DroppableTypes.LIST} direction="horizontal">
|
<Droppable droppableId="board" type={DroppableTypes.LIST} direction="horizontal">
|
||||||
{({ innerRef, droppableProps, placeholder }) => (
|
{({ innerRef, droppableProps, placeholder }) => (
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
<div
|
||||||
<div {...droppableProps} data-drag-scroller ref={innerRef} className={styles.lists}>
|
{...droppableProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
data-drag-scroller
|
||||||
|
ref={innerRef}
|
||||||
|
className={styles.lists}
|
||||||
|
>
|
||||||
{listIds.map((listId, index) => (
|
{listIds.map((listId, index) => (
|
||||||
<ListContainer key={listId} id={listId} index={index} />
|
<ListContainer key={listId} id={listId} index={index} />
|
||||||
))}
|
))}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
<div data-drag-scroller className={styles.list}>
|
<div data-drag-scroller className={styles.list}>
|
||||||
<AddList onCreate={onListCreate}>
|
{isAddListOpened ? (
|
||||||
<button type="button" className={styles.addListButton}>
|
<AddList
|
||||||
|
isOpened={isAddListOpened}
|
||||||
|
onCreate={onListCreate}
|
||||||
|
onClose={handleAddListClose}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.addListButton}
|
||||||
|
onClick={handleAddListClick}
|
||||||
|
>
|
||||||
<PlusMathIcon className={styles.addListButtonIcon} />
|
<PlusMathIcon className={styles.addListButtonIcon} />
|
||||||
<span className={styles.addListButtonText}>
|
<span className={styles.addListButtonText}>
|
||||||
{listIds.length > 0 ? t('action.addAnotherList') : t('action.addList')}
|
{listIds.length > 0 ? t('action.addAnotherList') : t('action.addList')}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</AddList>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</Droppable>
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{isCardModalOpened && <CardModalContainer />}
|
{isCardModalOpened && <CardModalContainer />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,5 +48,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
height: 100%;
|
margin: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,14 @@ const Filter = React.memo(
|
||||||
}) => {
|
}) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
const handleUserRemoveClick = useCallback(
|
const handleRemoveUserClick = useCallback(
|
||||||
(id) => {
|
(id) => {
|
||||||
onUserRemove(id);
|
onUserRemove(id);
|
||||||
},
|
},
|
||||||
[onUserRemove],
|
[onUserRemove],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleLabelRemoveClick = useCallback(
|
const handleRemoveLabelClick = useCallback(
|
||||||
(id) => {
|
(id) => {
|
||||||
onLabelRemove(id);
|
onLabelRemove(id);
|
||||||
},
|
},
|
||||||
|
@ -62,7 +62,7 @@ const Filter = React.memo(
|
||||||
name={user.name}
|
name={user.name}
|
||||||
avatarUrl={user.avatarUrl}
|
avatarUrl={user.avatarUrl}
|
||||||
size="tiny"
|
size="tiny"
|
||||||
onClick={() => handleUserRemoveClick(user.id)}
|
onClick={() => handleRemoveUserClick(user.id)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
@ -91,7 +91,7 @@ const Filter = React.memo(
|
||||||
name={label.name}
|
name={label.name}
|
||||||
color={label.color}
|
color={label.color}
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleLabelRemoveClick(label.id)}
|
onClick={() => handleRemoveLabelClick(label.id)}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -2,16 +2,13 @@ import pick from 'lodash/pick';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useTranslation, Trans } from 'react-i18next';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
import { Button, Icon } from 'semantic-ui-react';
|
import { Button, Icon } from 'semantic-ui-react';
|
||||||
import { closePopup } from '../../lib/popup';
|
import { closePopup } from '../../lib/popup';
|
||||||
import { DragScroller } from '../../lib/custom-ui';
|
|
||||||
|
|
||||||
import Paths from '../../constants/Paths';
|
import Paths from '../../constants/Paths';
|
||||||
import DroppableTypes from '../../constants/DroppableTypes';
|
import DroppableTypes from '../../constants/DroppableTypes';
|
||||||
import BoardWrapperContainer from '../../containers/BoardWrapperContainer';
|
|
||||||
import AddPopup from './AddPopup';
|
import AddPopup from './AddPopup';
|
||||||
import EditPopup from './EditPopup';
|
import EditPopup from './EditPopup';
|
||||||
|
|
||||||
|
@ -19,8 +16,6 @@ import styles from './Boards.module.css';
|
||||||
|
|
||||||
const Boards = React.memo(
|
const Boards = React.memo(
|
||||||
({ items, currentId, isEditable, onCreate, onUpdate, onMove, onDelete }) => {
|
({ items, currentId, isEditable, onCreate, onUpdate, onMove, onDelete }) => {
|
||||||
const [t] = useTranslation();
|
|
||||||
|
|
||||||
const handleDragStart = useCallback(() => {
|
const handleDragStart = useCallback(() => {
|
||||||
closePopup();
|
closePopup();
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -139,28 +134,6 @@ const Boards = React.memo(
|
||||||
) : (
|
) : (
|
||||||
<div className={styles.tabs}>{renderItems(items)}</div>
|
<div className={styles.tabs}>{renderItems(items)}</div>
|
||||||
)}
|
)}
|
||||||
<DragScroller className={styles.board}>
|
|
||||||
{currentId ? (
|
|
||||||
<BoardWrapperContainer />
|
|
||||||
) : (
|
|
||||||
<div className={styles.message}>
|
|
||||||
<Icon
|
|
||||||
inverted
|
|
||||||
name="hand point up outline"
|
|
||||||
size="huge"
|
|
||||||
className={styles.messageIcon}
|
|
||||||
/>
|
|
||||||
<h1 className={styles.messageTitle}>
|
|
||||||
{t('common.openBoard', {
|
|
||||||
context: 'title',
|
|
||||||
})}
|
|
||||||
</h1>
|
|
||||||
<div className={styles.messageContent}>
|
|
||||||
<Trans i18nKey="common.createNewOneOrSelectExistingOne" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DragScroller>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,15 +9,6 @@
|
||||||
background: rgba(34, 36, 38, 0.3) !important;
|
background: rgba(34, 36, 38, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: 0 -20px 8px;
|
|
||||||
overflow: auto;
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editButton {
|
.editButton {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
|
@ -45,32 +36,6 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
|
||||||
align-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
color: #fff;
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageIcon {
|
|
||||||
margin-top: -84px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageTitle {
|
|
||||||
font-size: 32px;
|
|
||||||
margin: 24px 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageContent {
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 4px 0 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
border-radius: 3px 3px 0 0;
|
border-radius: 3px 3px 0 0;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
@ -104,7 +69,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 38px;
|
height: 38px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin-bottom: 16px;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ const ActionsStep = React.memo(
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [step, openStep, handleBack] = useSteps();
|
const [step, openStep, handleBack] = useSteps();
|
||||||
|
|
||||||
const handleNameEditClick = useCallback(() => {
|
const handleEditNameClick = useCallback(() => {
|
||||||
onNameEdit();
|
onNameEdit();
|
||||||
onClose();
|
onClose();
|
||||||
}, [onNameEdit, onClose]);
|
}, [onNameEdit, onClose]);
|
||||||
|
@ -178,7 +178,7 @@ const ActionsStep = React.memo(
|
||||||
</Popup.Header>
|
</Popup.Header>
|
||||||
<Popup.Content>
|
<Popup.Content>
|
||||||
<Menu secondary vertical className={styles.menu}>
|
<Menu secondary vertical className={styles.menu}>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleNameEditClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
|
||||||
{t('action.editTitle', {
|
{t('action.editTitle', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -69,8 +69,8 @@ const EditName = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||||
isOpened,
|
|
||||||
close,
|
close,
|
||||||
|
isOpened,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
|
|
|
@ -66,8 +66,8 @@ const EditComment = React.forwardRef(({ children, defaultData, onUpdate }, ref)
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||||
isOpened,
|
|
||||||
close,
|
close,
|
||||||
|
isOpened,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
|
|
|
@ -117,7 +117,7 @@ const CardModal = React.memo(
|
||||||
[onUpdate],
|
[onUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleToggleSubscribeClick = useCallback(() => {
|
const handleToggleSubscriptionClick = useCallback(() => {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
isSubscribed: !isSubscribed,
|
isSubscribed: !isSubscribed,
|
||||||
});
|
});
|
||||||
|
@ -359,7 +359,7 @@ const CardModal = React.memo(
|
||||||
<Button
|
<Button
|
||||||
fluid
|
fluid
|
||||||
className={styles.actionButton}
|
className={styles.actionButton}
|
||||||
onClick={handleToggleSubscribeClick}
|
onClick={handleToggleSubscriptionClick}
|
||||||
>
|
>
|
||||||
<Icon name="paper plane outline" className={styles.actionIcon} />
|
<Icon name="paper plane outline" className={styles.actionIcon} />
|
||||||
{isSubscribed ? t('action.unsubscribe') : t('action.subscribe')}
|
{isSubscribed ? t('action.unsubscribe') : t('action.subscribe')}
|
||||||
|
@ -378,7 +378,7 @@ const CardModal = React.memo(
|
||||||
<Button
|
<Button
|
||||||
fluid
|
fluid
|
||||||
className={styles.actionButton}
|
className={styles.actionButton}
|
||||||
onClick={handleToggleSubscribeClick}
|
onClick={handleToggleSubscriptionClick}
|
||||||
>
|
>
|
||||||
<Icon name="share square outline" className={styles.actionIcon} />
|
<Icon name="share square outline" className={styles.actionIcon} />
|
||||||
{t('action.move')}
|
{t('action.move')}
|
||||||
|
|
|
@ -56,8 +56,8 @@ const EditDescription = React.forwardRef(({ children, defaultValue, onUpdate },
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||||
isOpened,
|
|
||||||
close,
|
close,
|
||||||
|
isOpened,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
|
|
|
@ -18,7 +18,7 @@ const ActionsStep = React.memo(({ onNameEdit, onDelete, onClose }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [step, openStep, handleBack] = useSteps();
|
const [step, openStep, handleBack] = useSteps();
|
||||||
|
|
||||||
const handleNameEditClick = useCallback(() => {
|
const handleEditNameClick = useCallback(() => {
|
||||||
onNameEdit();
|
onNameEdit();
|
||||||
onClose();
|
onClose();
|
||||||
}, [onNameEdit, onClose]);
|
}, [onNameEdit, onClose]);
|
||||||
|
@ -50,7 +50,7 @@ const ActionsStep = React.memo(({ onNameEdit, onDelete, onClose }) => {
|
||||||
</Popup.Header>
|
</Popup.Header>
|
||||||
<Popup.Content>
|
<Popup.Content>
|
||||||
<Menu secondary vertical className={styles.menu}>
|
<Menu secondary vertical className={styles.menu}>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleNameEditClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
|
||||||
{t('action.editDescription', {
|
{t('action.editDescription', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -71,8 +71,8 @@ const Add = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||||
isOpened,
|
|
||||||
close,
|
close,
|
||||||
|
isOpened,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
|
|
|
@ -61,8 +61,8 @@ const EditName = React.forwardRef(({ children, defaultValue, onUpdate }, ref) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
||||||
isOpened,
|
|
||||||
close,
|
close,
|
||||||
|
isOpened,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
|
|
24
client/src/components/FixedWrapper/FixedWrapper.jsx
Normal file
24
client/src/components/FixedWrapper/FixedWrapper.jsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import HeaderContainer from '../../containers/HeaderContainer';
|
||||||
|
import ProjectContainer from '../../containers/ProjectContainer';
|
||||||
|
|
||||||
|
import styles from './FixedWrapper.module.css';
|
||||||
|
|
||||||
|
const FixedWrapper = ({ projectId }) => (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<HeaderContainer />
|
||||||
|
{projectId && <ProjectContainer />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
FixedWrapper.propTypes = {
|
||||||
|
projectId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
FixedWrapper.defaultProps = {
|
||||||
|
projectId: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FixedWrapper;
|
|
@ -0,0 +1,5 @@
|
||||||
|
.wrapper {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
3
client/src/components/FixedWrapper/index.js
Normal file
3
client/src/components/FixedWrapper/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import FixedWrapper from './FixedWrapper';
|
||||||
|
|
||||||
|
export default FixedWrapper;
|
|
@ -111,4 +111,6 @@ NotificationsStep.propTypes = {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withPopup(NotificationsStep);
|
export default withPopup(NotificationsStep, {
|
||||||
|
position: 'bottom right',
|
||||||
|
});
|
||||||
|
|
|
@ -18,12 +18,12 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [step, openStep, handleBack] = useSteps();
|
const [step, openStep, handleBack] = useSteps();
|
||||||
|
|
||||||
const handleNameEditClick = useCallback(() => {
|
const handleEditNameClick = useCallback(() => {
|
||||||
onNameEdit();
|
onNameEdit();
|
||||||
onClose();
|
onClose();
|
||||||
}, [onNameEdit, onClose]);
|
}, [onNameEdit, onClose]);
|
||||||
|
|
||||||
const handleCardAddClick = useCallback(() => {
|
const handleAddCardClick = useCallback(() => {
|
||||||
onCardAdd();
|
onCardAdd();
|
||||||
onClose();
|
onClose();
|
||||||
}, [onCardAdd, onClose]);
|
}, [onCardAdd, onClose]);
|
||||||
|
@ -55,12 +55,12 @@ const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onDelete, onClose }) =>
|
||||||
</Popup.Header>
|
</Popup.Header>
|
||||||
<Popup.Content>
|
<Popup.Content>
|
||||||
<Menu secondary vertical className={styles.menu}>
|
<Menu secondary vertical className={styles.menu}>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleNameEditClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
|
||||||
{t('action.editTitle', {
|
{t('action.editTitle', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item className={styles.menuItem} onClick={handleCardAddClick}>
|
<Menu.Item className={styles.menuItem} onClick={handleAddCardClick}>
|
||||||
{t('action.addCard', {
|
{t('action.addCard', {
|
||||||
context: 'title',
|
context: 'title',
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
@ -13,22 +14,13 @@ const DEFAULT_DATA = {
|
||||||
name: '',
|
name: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddCard = React.forwardRef(({ children, onCreate }, ref) => {
|
const AddCard = React.memo(({ isOpened, onCreate, onClose }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
const [isOpened, setIsOpened] = useState(false);
|
|
||||||
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
const [data, handleFieldChange, setData] = useForm(DEFAULT_DATA);
|
||||||
const [selectNameFieldState, selectNameField] = useToggle();
|
const [selectNameFieldState, selectNameField] = useToggle();
|
||||||
|
|
||||||
const nameField = useRef(null);
|
const nameField = useRef(null);
|
||||||
|
|
||||||
const open = useCallback(() => {
|
|
||||||
setIsOpened(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const close = useCallback(() => {
|
|
||||||
setIsOpened(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const submit = useCallback(() => {
|
const submit = useCallback(() => {
|
||||||
const cleanData = {
|
const cleanData = {
|
||||||
...data,
|
...data,
|
||||||
|
@ -46,19 +38,6 @@ const AddCard = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
selectNameField();
|
selectNameField();
|
||||||
}, [onCreate, data, setData, selectNameField]);
|
}, [onCreate, data, setData, selectNameField]);
|
||||||
|
|
||||||
useImperativeHandle(
|
|
||||||
ref,
|
|
||||||
() => ({
|
|
||||||
open,
|
|
||||||
close,
|
|
||||||
}),
|
|
||||||
[open, close],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChildrenClick = useCallback(() => {
|
|
||||||
open();
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const handleFieldKeyDown = useCallback(
|
const handleFieldKeyDown = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
|
@ -69,19 +48,16 @@ const AddCard = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
close();
|
onClose();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[close, submit],
|
[onClose, submit],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(
|
const [handleFieldBlur, handleControlMouseOver, handleControlMouseOut] = useClosableForm(onClose);
|
||||||
isOpened,
|
|
||||||
close,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSubmit = useCallback(() => {
|
const handleSubmit = useCallback(() => {
|
||||||
submit();
|
submit();
|
||||||
|
@ -97,14 +73,11 @@ const AddCard = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
nameField.current.ref.current.select();
|
nameField.current.ref.current.select();
|
||||||
}, [selectNameFieldState]);
|
}, [selectNameFieldState]);
|
||||||
|
|
||||||
if (!isOpened) {
|
|
||||||
return React.cloneElement(children, {
|
|
||||||
onClick: handleChildrenClick,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className={styles.wrapper} onSubmit={handleSubmit}>
|
<Form
|
||||||
|
className={classNames(styles.wrapper, !isOpened && styles.wrapperClosed)}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
<div className={styles.fieldWrapper}>
|
<div className={styles.fieldWrapper}>
|
||||||
<TextArea
|
<TextArea
|
||||||
ref={nameField}
|
ref={nameField}
|
||||||
|
@ -135,8 +108,9 @@ const AddCard = React.forwardRef(({ children, onCreate }, ref) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
AddCard.propTypes = {
|
AddCard.propTypes = {
|
||||||
children: PropTypes.element.isRequired,
|
isOpened: PropTypes.bool.isRequired,
|
||||||
onCreate: PropTypes.func.isRequired,
|
onCreate: PropTypes.func.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(AddCard);
|
export default AddCard;
|
||||||
|
|
|
@ -24,3 +24,7 @@
|
||||||
.wrapper {
|
.wrapper {
|
||||||
padding-bottom: 8px !important;
|
padding-bottom: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wrapperClosed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
@ -17,9 +17,10 @@ import styles from './List.module.css';
|
||||||
const List = React.memo(
|
const List = React.memo(
|
||||||
({ id, index, name, isPersisted, cardIds, onUpdate, onDelete, onCardCreate }) => {
|
({ id, index, name, isPersisted, cardIds, onUpdate, onDelete, onCardCreate }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
|
||||||
|
|
||||||
const addCard = useRef(null);
|
|
||||||
const editName = useRef(null);
|
const editName = useRef(null);
|
||||||
|
const listWrapper = useRef(null);
|
||||||
|
|
||||||
const handleHeaderClick = useCallback(() => {
|
const handleHeaderClick = useCallback(() => {
|
||||||
if (isPersisted) {
|
if (isPersisted) {
|
||||||
|
@ -36,14 +37,28 @@ const List = React.memo(
|
||||||
[onUpdate],
|
[onUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleAddCardClick = useCallback(() => {
|
||||||
|
setIsAddCardOpened(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAddCardClose = useCallback(() => {
|
||||||
|
setIsAddCardOpened(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleNameEdit = useCallback(() => {
|
const handleNameEdit = useCallback(() => {
|
||||||
editName.current.open();
|
editName.current.open();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCardAdd = useCallback(() => {
|
const handleCardAdd = useCallback(() => {
|
||||||
addCard.current.open();
|
setIsAddCardOpened(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isAddCardOpened) {
|
||||||
|
listWrapper.current.scrollTop = listWrapper.current.scrollHeight;
|
||||||
|
}
|
||||||
|
}, [cardIds, isAddCardOpened]);
|
||||||
|
|
||||||
const cardsNode = (
|
const cardsNode = (
|
||||||
<Droppable
|
<Droppable
|
||||||
droppableId={`list:${id}`}
|
droppableId={`list:${id}`}
|
||||||
|
@ -58,15 +73,12 @@ const List = React.memo(
|
||||||
<CardContainer key={cardId} id={cardId} index={cardIndex} />
|
<CardContainer key={cardId} id={cardId} index={cardIndex} />
|
||||||
))}
|
))}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
<AddCard
|
||||||
|
isOpened={isAddCardOpened}
|
||||||
|
onCreate={onCardCreate}
|
||||||
|
onClose={handleAddCardClose}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AddCard ref={addCard} onCreate={onCardCreate}>
|
|
||||||
<button type="button" disabled={!isPersisted} className={styles.addCardButton}>
|
|
||||||
<PlusMathIcon className={styles.addCardButtonIcon} />
|
|
||||||
<span className={styles.addCardButtonText}>
|
|
||||||
{cardIds.length > 0 ? t('action.addAnotherCard') : t('action.addCard')}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</AddCard>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</Droppable>
|
||||||
|
@ -99,8 +111,26 @@ const List = React.memo(
|
||||||
</ActionsPopup>
|
</ActionsPopup>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
ref={listWrapper}
|
||||||
|
className={classNames(styles.listWrapper, isAddCardOpened && styles.listWrapperFull)}
|
||||||
|
>
|
||||||
<div className={styles.list}>{cardsNode}</div>
|
<div className={styles.list}>{cardsNode}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!isAddCardOpened && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={!isPersisted}
|
||||||
|
className={styles.addCardButton}
|
||||||
|
onClick={handleAddCardClick}
|
||||||
|
>
|
||||||
|
<PlusMathIcon className={styles.addCardButtonIcon} />
|
||||||
|
<span className={styles.addCardButtonText}>
|
||||||
|
{cardIds.length > 0 ? t('action.addAnotherCard') : t('action.addCard')}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
.addCardButton {
|
.addCardButton {
|
||||||
background: none !important;
|
background: #dfe3e6 !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
border-radius: 0 0 3px 3px !important;
|
||||||
color: #6b808c !important;
|
color: #6b808c !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
|
@ -8,18 +9,14 @@
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
font-weight: normal !important;
|
font-weight: normal !important;
|
||||||
height: 36px !important;
|
height: 36px !important;
|
||||||
margin: 0 -8px !important;
|
outline: none !important;
|
||||||
padding: 8px !important;
|
padding: 8px !important;
|
||||||
text-align: left !important;
|
text-align: left !important;
|
||||||
width: calc(100% + 16px);
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.addCardButton:active {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.addCardButton:hover {
|
.addCardButton:hover {
|
||||||
background-color: #092d4221 !important;
|
background-color: #c3cbd0 !important;
|
||||||
color: #17394d !important;
|
color: #17394d !important;
|
||||||
fill: #17394d !important;
|
fill: #17394d !important;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +37,7 @@
|
||||||
|
|
||||||
.cards {
|
.cards {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
min-height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
@ -96,18 +94,48 @@
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
background: #dfe3e6 !important;
|
background: #dfe3e6 !important;
|
||||||
border-radius: 0 0 3px 3px !important;
|
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0 8px !important;
|
padding: 0 8px !important;
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
white-space: normal !important;
|
white-space: normal !important;
|
||||||
|
width: 272px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper {
|
||||||
|
background: #dfe3e6;
|
||||||
|
max-height: calc(100vh - 300px);
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 290px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper:hover {
|
||||||
|
width: 272px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapper::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listWrapperFull {
|
||||||
|
max-height: calc(100vh - 264px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
border-radius: 3px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
margin: 0 4px !important;
|
margin: 0 4px !important;
|
||||||
|
overflow: hidden;
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
width: 272px !important;
|
width: 272px !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import ProjectContainer from '../containers/ProjectContainer';
|
|
||||||
|
|
||||||
const ProjectWrapper = React.memo(({ isProjectNotFound, isBoardNotFound, isCardNotFound }) => {
|
|
||||||
const [t] = useTranslation();
|
|
||||||
|
|
||||||
if (isCardNotFound) {
|
|
||||||
return (
|
|
||||||
<h1>
|
|
||||||
{t('common.cardNotFound', {
|
|
||||||
context: 'title',
|
|
||||||
})}
|
|
||||||
</h1>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBoardNotFound) {
|
|
||||||
return (
|
|
||||||
<h1>
|
|
||||||
{t('common.boardNotFound', {
|
|
||||||
context: 'title',
|
|
||||||
})}
|
|
||||||
</h1>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isProjectNotFound) {
|
|
||||||
return (
|
|
||||||
<h1>
|
|
||||||
{t('common.projectNotFound', {
|
|
||||||
context: 'title',
|
|
||||||
})}
|
|
||||||
</h1>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <ProjectContainer />;
|
|
||||||
});
|
|
||||||
|
|
||||||
ProjectWrapper.propTypes = {
|
|
||||||
isProjectNotFound: PropTypes.bool.isRequired,
|
|
||||||
isBoardNotFound: PropTypes.bool.isRequired,
|
|
||||||
isCardNotFound: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProjectWrapper;
|
|
|
@ -1,4 +1,3 @@
|
||||||
import isUndefined from 'lodash/isUndefined';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -7,15 +6,13 @@ import { Link } from 'react-router-dom';
|
||||||
import { Container, Grid } from 'semantic-ui-react';
|
import { Container, Grid } from 'semantic-ui-react';
|
||||||
|
|
||||||
import Paths from '../../constants/Paths';
|
import Paths from '../../constants/Paths';
|
||||||
import ProjectWrapperContainer from '../../containers/ProjectWrapperContainer';
|
|
||||||
import { ReactComponent as PlusIcon } from '../../assets/images/plus-icon.svg';
|
import { ReactComponent as PlusIcon } from '../../assets/images/plus-icon.svg';
|
||||||
|
|
||||||
import styles from './Projects.module.css';
|
import styles from './Projects.module.css';
|
||||||
|
|
||||||
const Projects = React.memo(({ items, currentId, isEditable, onAdd }) => {
|
const Projects = React.memo(({ items, isEditable, onAdd }) => {
|
||||||
const [t] = useTranslation();
|
const [t] = useTranslation();
|
||||||
|
|
||||||
if (isUndefined(currentId)) {
|
|
||||||
return (
|
return (
|
||||||
<Container className={styles.cardsWrapper}>
|
<Container className={styles.cardsWrapper}>
|
||||||
<Grid className={styles.gridFix}>
|
<Grid className={styles.gridFix}>
|
||||||
|
@ -53,26 +50,12 @@ const Projects = React.memo(({ items, currentId, isEditable, onAdd }) => {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.wrapper}>
|
|
||||||
<div className={styles.project}>
|
|
||||||
<ProjectWrapperContainer />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Projects.propTypes = {
|
Projects.propTypes = {
|
||||||
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
items: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
|
||||||
currentId: PropTypes.string,
|
|
||||||
isEditable: PropTypes.bool.isRequired,
|
isEditable: PropTypes.bool.isRequired,
|
||||||
onAdd: PropTypes.func.isRequired,
|
onAdd: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
Projects.defaultProps = {
|
|
||||||
currentId: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Projects;
|
export default Projects;
|
||||||
|
|
106
client/src/components/StaticWrapper/StaticWrapper.jsx
Normal file
106
client/src/components/StaticWrapper/StaticWrapper.jsx
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { useTranslation, Trans } from 'react-i18next';
|
||||||
|
import { Icon } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
import ProjectsContainer from '../../containers/ProjectsContainer';
|
||||||
|
import BoardWrapperContainer from '../../containers/BoardWrapperContainer';
|
||||||
|
|
||||||
|
import styles from './StaticWrapper.module.css';
|
||||||
|
|
||||||
|
const StaticWrapper = ({ cardId, boardId, projectId }) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.overflowX = 'auto'; // TODO: only for board
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (projectId === undefined) {
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<ProjectsContainer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardId === null) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.root, styles.flex)}>
|
||||||
|
<div className={styles.message}>
|
||||||
|
<h1>
|
||||||
|
{t('common.cardNotFound', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardId === null) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.root, styles.flex)}>
|
||||||
|
<div className={styles.message}>
|
||||||
|
<h1>
|
||||||
|
{t('common.boardNotFound', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectId === null) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.root, styles.flex)}>
|
||||||
|
<div className={styles.message}>
|
||||||
|
<h1>
|
||||||
|
{t('common.projectNotFound', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardId === undefined) {
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.board, styles.flex)}>
|
||||||
|
<div className={styles.message}>
|
||||||
|
<Icon inverted name="hand point up outline" size="huge" className={styles.messageIcon} />
|
||||||
|
<h1 className={styles.messageTitle}>
|
||||||
|
{t('common.openBoard', {
|
||||||
|
context: 'title',
|
||||||
|
})}
|
||||||
|
</h1>
|
||||||
|
<div className={styles.messageContent}>
|
||||||
|
<Trans i18nKey="common.createNewOneOrSelectExistingOne" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames(styles.board, styles.flex)}>
|
||||||
|
<BoardWrapperContainer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticWrapper.propTypes = {
|
||||||
|
cardId: PropTypes.string,
|
||||||
|
boardId: PropTypes.string,
|
||||||
|
projectId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticWrapper.defaultProps = {
|
||||||
|
cardId: undefined,
|
||||||
|
boardId: undefined,
|
||||||
|
projectId: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StaticWrapper;
|
38
client/src/components/StaticWrapper/StaticWrapper.module.css
Normal file
38
client/src/components/StaticWrapper/StaticWrapper.module.css
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
.board {
|
||||||
|
margin-top: 168px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
align-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageIcon {
|
||||||
|
margin-top: -84px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageTitle {
|
||||||
|
font-size: 32px;
|
||||||
|
margin: 24px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageContent {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 4px 0 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
3
client/src/components/StaticWrapper/index.js
Normal file
3
client/src/components/StaticWrapper/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import StaticWrapper from './StaticWrapper';
|
||||||
|
|
||||||
|
export default StaticWrapper;
|
|
@ -46,4 +46,6 @@ UserStep.propTypes = {
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withPopup(UserStep);
|
export default withPopup(UserStep, {
|
||||||
|
position: 'bottom right',
|
||||||
|
});
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
const USERS = 'USERS';
|
const USERS = 'USERS';
|
||||||
|
|
||||||
const USER_SETTINGS = 'USER_SETTINGS';
|
const USER_SETTINGS = 'USER_SETTINGS';
|
||||||
|
|
||||||
const ADD_PROJECT = 'ADD_PROJECT';
|
const ADD_PROJECT = 'ADD_PROJECT';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { currentModalSelector } from '../selectors';
|
import { currentModalSelector } from '../selectors';
|
||||||
import ModalTypes from '../constants/ModalTypes';
|
|
||||||
import App from '../components/App';
|
import App from '../components/App';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const currentModal = currentModalSelector(state);
|
const currentModal = currentModalSelector(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isUsersModalOpened: currentModal === ModalTypes.USERS,
|
currentModal,
|
||||||
isUserSettingsModalOpened: currentModal === ModalTypes.USER_SETTINGS,
|
|
||||||
isAddProjectModalOpened: currentModal === ModalTypes.ADD_PROJECT,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
14
client/src/containers/FixedWrapperContainer.js
Normal file
14
client/src/containers/FixedWrapperContainer.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { pathSelector } from '../selectors';
|
||||||
|
import FixedWrapper from '../components/FixedWrapper';
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const { projectId } = pathSelector(state);
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(FixedWrapper);
|
|
@ -1,16 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { pathSelector } from '../selectors';
|
|
||||||
import ProjectWrapper from '../components/ProjectWrapper';
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
const { cardId, boardId, projectId } = pathSelector(state);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isProjectNotFound: projectId === null,
|
|
||||||
isBoardNotFound: boardId === null,
|
|
||||||
isCardNotFound: cardId === null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(ProjectWrapper);
|
|
|
@ -1,18 +1,16 @@
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { currentUserSelector, pathSelector, projectsForCurrentUserSelector } from '../selectors';
|
import { currentUserSelector, projectsForCurrentUserSelector } from '../selectors';
|
||||||
import { openAddProjectModal } from '../actions/entry';
|
import { openAddProjectModal } from '../actions/entry';
|
||||||
import Projects from '../components/Projects';
|
import Projects from '../components/Projects';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const { projectId } = pathSelector(state);
|
|
||||||
const { isAdmin } = currentUserSelector(state);
|
const { isAdmin } = currentUserSelector(state);
|
||||||
const projects = projectsForCurrentUserSelector(state);
|
const projects = projectsForCurrentUserSelector(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: projects,
|
items: projects,
|
||||||
currentId: projectId,
|
|
||||||
isEditable: isAdmin,
|
isEditable: isAdmin,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
16
client/src/containers/StaticWrapperContainer.js
Normal file
16
client/src/containers/StaticWrapperContainer.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { pathSelector } from '../selectors';
|
||||||
|
import StaticWrapper from '../components/StaticWrapper';
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const { cardId, boardId, projectId } = pathSelector(state);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cardId,
|
||||||
|
boardId,
|
||||||
|
projectId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(StaticWrapper);
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
export default (isOpened, close) => {
|
export default (close, isOpened = true) => {
|
||||||
const isClosable = useRef(null);
|
const isClosable = useRef(null);
|
||||||
|
|
||||||
const handleFieldBlur = useCallback(() => {
|
const handleFieldBlur = useCallback(() => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ body {
|
||||||
#root {
|
#root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-datepicker {
|
.react-datepicker {
|
||||||
|
@ -100,3 +100,30 @@ body {
|
||||||
top: 0;
|
top: 0;
|
||||||
margin: 9px 0;
|
margin: 9px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
-webkit-transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:window-inactive {
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(128, 135, 139, 0.8);
|
||||||
|
}
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const DragScroller = React.memo(({ children, ...props }) => {
|
|
||||||
const wrapper = useRef(null);
|
|
||||||
const prevPosition = useRef(null);
|
|
||||||
|
|
||||||
const handleMouseDown = useCallback(
|
|
||||||
(event) => {
|
|
||||||
if (event.target !== wrapper.current && !event.target.dataset.dragScroller) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prevPosition.current = event.clientX;
|
|
||||||
},
|
|
||||||
[wrapper],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleWindowMouseMove = useCallback(
|
|
||||||
(event) => {
|
|
||||||
if (!prevPosition.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const position = event.clientX;
|
|
||||||
|
|
||||||
wrapper.current.scrollLeft -= -prevPosition.current + position;
|
|
||||||
prevPosition.current = position;
|
|
||||||
},
|
|
||||||
[wrapper, prevPosition],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleWindowMouseUp = useCallback(() => {
|
|
||||||
prevPosition.current = null;
|
|
||||||
}, [prevPosition]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('mouseup', handleWindowMouseUp);
|
|
||||||
window.addEventListener('mousemove', handleWindowMouseMove);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('mouseup', handleWindowMouseUp);
|
|
||||||
window.removeEventListener('mousemove', handleWindowMouseMove);
|
|
||||||
};
|
|
||||||
}, [handleWindowMouseUp, handleWindowMouseMove]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
/* eslint-disable jsx-a11y/no-static-element-interactions, react/jsx-props-no-spreading */
|
|
||||||
<div {...props} ref={wrapper} onMouseDown={handleMouseDown}>
|
|
||||||
{/* eslint-enable jsx-a11y/no-static-element-interactions, react/jsx-props-no-spreading */}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
DragScroller.propTypes = {
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DragScroller;
|
|
|
@ -1,3 +0,0 @@
|
||||||
import DragScroller from './DragScroller';
|
|
||||||
|
|
||||||
export default DragScroller;
|
|
8
client/src/lib/custom-ui/index.css
vendored
8
client/src/lib/custom-ui/index.css
vendored
|
@ -707,7 +707,7 @@ input::selection {
|
||||||
|
|
||||||
/* Force Simple Scrollbars */
|
/* Force Simple Scrollbars */
|
||||||
|
|
||||||
body ::-webkit-scrollbar {
|
/* body ::-webkit-scrollbar {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
@ -732,11 +732,11 @@ body ::-webkit-scrollbar-thumb:window-inactive {
|
||||||
|
|
||||||
body ::-webkit-scrollbar-thumb:hover {
|
body ::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(128, 135, 139, 0.8);
|
background: rgba(128, 135, 139, 0.8);
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* Inverted UI */
|
/* Inverted UI */
|
||||||
|
|
||||||
body .ui.inverted::-webkit-scrollbar-track {
|
/* body .ui.inverted::-webkit-scrollbar-track {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +750,7 @@ body .ui.inverted::-webkit-scrollbar-thumb:window-inactive {
|
||||||
|
|
||||||
body .ui.inverted::-webkit-scrollbar-thumb:hover {
|
body .ui.inverted::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(255, 255, 255, 0.35);
|
background: rgba(255, 255, 255, 0.35);
|
||||||
}
|
} */
|
||||||
|
|
||||||
/*******************************
|
/*******************************
|
||||||
Global Overrides
|
Global Overrides
|
||||||
|
|
|
@ -2,6 +2,5 @@ import Input from './components/Input';
|
||||||
import Popup from './components/Popup';
|
import Popup from './components/Popup';
|
||||||
import Markdown from './components/Markdown';
|
import Markdown from './components/Markdown';
|
||||||
import FilePicker from './components/FilePicker';
|
import FilePicker from './components/FilePicker';
|
||||||
import DragScroller from './components/DragScroller';
|
|
||||||
|
|
||||||
export { Input, Popup, Markdown, FilePicker, DragScroller };
|
export { Input, Popup, Markdown, FilePicker };
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
|
||||||
|
|
||||||
import styles from './Popup.module.css';
|
import styles from './Popup.module.css';
|
||||||
|
|
||||||
export default (WrappedComponent) => {
|
export default (WrappedComponent, defaultProps) => {
|
||||||
const Popup = React.memo(({ children, ...props }) => {
|
const Popup = React.memo(({ children, ...props }) => {
|
||||||
const [isOpened, setIsOpened] = useState(false);
|
const [isOpened, setIsOpened] = useState(false);
|
||||||
|
|
||||||
|
@ -49,11 +49,17 @@ export default (WrappedComponent) => {
|
||||||
on="click"
|
on="click"
|
||||||
open={isOpened}
|
open={isOpened}
|
||||||
position="bottom left"
|
position="bottom left"
|
||||||
|
popperModifiers={{
|
||||||
|
preventOverflow: {
|
||||||
|
boundariesElement: 'window',
|
||||||
|
},
|
||||||
|
}}
|
||||||
className={styles.wrapper}
|
className={styles.wrapper}
|
||||||
onOpen={handleOpen}
|
onOpen={handleOpen}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
{...defaultProps} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
>
|
>
|
||||||
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
|
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
|
||||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||||
|
|
|
@ -53,7 +53,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputs.toBoard.id === inputs.board.id) {
|
if (inputs.toBoard.id === inputs.board.id) {
|
||||||
delete inputs.toList; // eslint-disable-line no-param-reassign
|
delete inputs.toBoard; // eslint-disable-line no-param-reassign
|
||||||
} else {
|
} else {
|
||||||
values.boardId = inputs.toBoard.id;
|
values.boardId = inputs.toBoard.id;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue