diff --git a/src/components/Card.js b/src/components/Card.js index e5d2952c7..b2a4d0b20 100644 --- a/src/components/Card.js +++ b/src/components/Card.js @@ -1,14 +1,7 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' -import { - MovableCardWrapper, - CardHeader, - CardRightContent, - CardTitle, - Detail, - Footer -} from 'rt/styles/Base' +import {MovableCardWrapper, CardHeader, CardRightContent, CardTitle, Detail, Footer} from 'rt/styles/Base' import InlineInput from 'rt/widgets/InlineInput' import Tag from './Card/Tag' import DeleteButton from 'rt/widgets/DeleteButton' @@ -19,7 +12,7 @@ class Card extends Component { e.stopPropagation() } - render() { + render() { const { showDeleteButton, style, @@ -38,38 +31,66 @@ class Card extends Component { t } = this.props - const updateCard = (card) => { + const updateCard = card => { onChange({...card, id}) } return ( - - - - {editable ? updateCard({title: value})} /> : title} - - - {editable ? updateCard({label: value})} /> : label} - - {showDeleteButton && } - + + {(title || label) && ( + + + {editable ? ( + updateCard({title: value})} + /> + ) : ( + title + )} + + + {editable ? ( + updateCard({label: value})} + /> + ) : ( + label + )} + + {showDeleteButton && } + + )} - {editable ? updateCard({description: value})} /> : description} + {editable ? ( + updateCard({description: value})} + /> + ) : ( + description + )} - {tags && tags.length> 0 && ( -
- {tags.map(tag => ( - - ))} -
- )} + {tags && + tags.length > 0 && ( +
+ {tags.map(tag => ( + + ))} +
+ )}
- ) + ) } } @@ -84,7 +105,7 @@ Card.propTypes = { title: PropTypes.string.isRequired, label: PropTypes.string, description: PropTypes.string, - tags: PropTypes.array, + tags: PropTypes.array } Card.defaultProps = { @@ -93,9 +114,9 @@ Card.defaultProps = { onClick: () => {}, style: {}, tagStyle: {}, - title: 'no title', + title: null, description: '', - label: '', + label: null, tags: [], className: '' } diff --git a/src/controllers/BoardContainer.js b/src/controllers/BoardContainer.js index 7603b8940..fdf5c9d05 100644 --- a/src/controllers/BoardContainer.js +++ b/src/controllers/BoardContainer.js @@ -7,14 +7,19 @@ import PropTypes from 'prop-types' import pick from 'lodash/pick' import isEqual from 'lodash/isEqual' import Lane from './Lane' -import { PopoverWrapper } from 'react-popopo' +import {PopoverWrapper} from 'react-popopo' import * as boardActions from 'rt/actions/BoardActions' import * as laneActions from 'rt/actions/LaneActions' class BoardContainer extends Component { state = { - addLaneMode: false + addLaneMode: false, + isDragging: false, + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0 } componentDidMount() { @@ -23,6 +28,74 @@ class BoardContainer extends Component { if (eventBusHandle) { this.wireEventBus() } + + const boardElement = document.querySelector('.react-trello-board') + if (boardElement) { + // Add user-select: none to prevent text selection + boardElement.style.userSelect = 'none' + boardElement.style.WebkitUserSelect = 'none' + boardElement.style.MozUserSelect = 'none' + boardElement.style.msUserSelect = 'none' + + boardElement.addEventListener('mousedown', this.handleMouseDown) + boardElement.addEventListener('mousemove', this.handleMouseMove) + boardElement.addEventListener('mouseup', this.handleMouseUp) + boardElement.addEventListener('mouseleave', this.handleMouseLeave) + } + } + + componentWillUnmount() { + const boardElement = document.querySelector('.react-trello-board') + if (boardElement) { + boardElement.removeEventListener('mousedown', this.handleMouseDown) + boardElement.removeEventListener('mousemove', this.handleMouseMove) + boardElement.removeEventListener('mouseup', this.handleMouseUp) + boardElement.removeEventListener('mouseleave', this.handleMouseLeave) + } + } + + handleMouseDown = e => { + // Prevent dragging when interacting with lanes or cards + if ( + e.target.closest('.react-trello-card') || + e.target.closest('.react-trello-lane') || + e.target.closest('.draggable') + ) + return + + // Prevent text selection + e.preventDefault() + + this.setState({ + isDragging: true, + startX: e.pageX - e.currentTarget.offsetLeft, + startY: e.pageY - e.currentTarget.offsetTop, + scrollLeft: e.currentTarget.scrollLeft, + scrollTop: e.currentTarget.scrollTop + }) + } + + handleMouseMove = e => { + const {isDragging, startX, scrollLeft} = this.state + if (!isDragging) return + + const boardElement = document.querySelector('.react-trello-board') + + e.preventDefault() + + const x = e.pageX - boardElement.offsetLeft + const walkX = (x - startX) * 1.5 + + // Only allow horizontal scrolling + boardElement.scrollLeft = scrollLeft - walkX + } + + handleMouseUp = () => { + this.setState({isDragging: false}) + } + + handleMouseLeave = () => { + this.setState({isDragging: false}) } UNSAFE_componentWillReceiveProps(nextProps) { @@ -204,8 +277,10 @@ class BoardContainer extends Component { {canAddLanes && ( - {editable && !addLaneMode ? : ( - addLaneMode && + {editable && !addLaneMode ? ( + + ) : ( + addLaneMode && )} )} @@ -250,11 +325,11 @@ BoardContainer.propTypes = { laneDragClass: PropTypes.string, laneDropClass: PropTypes.string, onCardMoveAcrossLanes: PropTypes.func.isRequired, - t: PropTypes.func.isRequired, + t: PropTypes.func.isRequired } BoardContainer.defaultProps = { - t: v=>v, + t: v => v, onDataChange: () => {}, handleDragStart: () => {}, handleDragEnd: () => {}, diff --git a/src/controllers/Lane.js b/src/controllers/Lane.js index 3a8303a81..1ecea9e87 100644 --- a/src/controllers/Lane.js +++ b/src/controllers/Lane.js @@ -47,7 +47,7 @@ class Lane extends Component { sortCards(cards, sortFunction) { if (!cards) return [] if (!sortFunction) return cards - return cards.concat().sort(function (card1, card2) { + return cards.concat().sort(function(card1, card2) { return sortFunction(card1, card2) }) } @@ -190,9 +190,10 @@ class Lane extends Component { }) return ( - + 0 return ( @@ -328,4 +330,7 @@ const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(laneActions, dispatch) }) -export default connect(null, mapDispatchToProps)(Lane) +export default connect( + null, + mapDispatchToProps +)(Lane) diff --git a/src/styles/Base.js b/src/styles/Base.js index c8ab5896a..8a803ce28 100644 --- a/src/styles/Base.js +++ b/src/styles/Base.js @@ -34,6 +34,24 @@ export const GlobalStyle = createGlobalStyle` line-height: 32px; width: 32px; } + + .scrollable-lane::-webkit-scrollbar { + width: 5px; + height: 8px; + } + + .scrollable-lane::-webkit-scrollbar-thumb { + background: #888; + border-radius: 10px; + } + + .scrollable-lane::-webkit-scrollbar-thumb:hover { + background: #555; + } + + .scrollable-lane::-webkit-scrollbar-track { + background: transparent; + } ` export const CustomPopoverContainer = styled(PopoverContainer)`