diff --git a/client/package-lock.json b/client/package-lock.json index e89da4a..821a8b2 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1162,22 +1162,6 @@ } } }, - "@babel/runtime-corejs2": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.4.tgz", - "integrity": "sha512-hKNcmHQbBSJFnZ82ewYtWDZ3fXkP/l1XcfRtm7c8gHPM/DMecJtFFBEp7KMLZTuHwwb7RfemHdsEnd7L916Z6A==", - "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - } - } - }, "@babel/template": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", @@ -1259,11 +1243,11 @@ "dev": true }, "@lourenci/react-kanban": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@lourenci/react-kanban/-/react-kanban-0.15.0.tgz", - "integrity": "sha512-/2XjB26iXcvpwDwlT3sz8/ptQ7QyTpMGlrPf1f02+V1Z4jdbVMo6Luz1sGlHe/TP68N8yz69/YT9qwqHZ6YYmQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@lourenci/react-kanban/-/react-kanban-2.0.0.tgz", + "integrity": "sha512-ieNi7d/01wgT9t8kN7Z/RBubyZq9VvKXyU/5sj+UlrY8h4GPRNrN11jLV/ykg55lhyXGKgXDk3ObYurOfrmu3w==", "requires": { - "react-beautiful-dnd": "^11.0.0" + "react-beautiful-dnd": "^13.0.0" } }, "@redux-saga/core": { @@ -2530,7 +2514,8 @@ "core-js": { "version": "2.6.10", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", - "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==", + "dev": true }, "core-js-compat": { "version": "3.4.1", @@ -2635,9 +2620,9 @@ } }, "css-box-model": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.0.tgz", - "integrity": "sha512-lri0br+jSNV0kkkiGEp9y9y3Njq2PmpqbeGWRFQJuZteZzY9iC9GZhQ8Y4WpPwM/2YocjHePxy14igJY7YKzkA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", "requires": { "tiny-invariant": "^1.0.6" } @@ -6201,18 +6186,32 @@ } }, "react-beautiful-dnd": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-11.0.5.tgz", - "integrity": "sha512-7llby9U+jIfkINcyxPHVWU0HFYzqxMemUYgGHsFsbx4fZo1n/pW6sYKYzhxGxR3Ap5HxqswcQkKUZX4uEUWhlw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz", + "integrity": "sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==", "requires": { - "@babel/runtime-corejs2": "^7.4.5", - "css-box-model": "^1.1.2", - "memoize-one": "^5.0.4", - "raf-schd": "^4.0.0", - "react-redux": "^7.0.3", - "redux": "^4.0.1", - "tiny-invariant": "^1.0.4", - "use-memo-one": "^1.1.0" + "@babel/runtime": "^7.8.4", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.1.1", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } } }, "react-dom": { diff --git a/client/package.json b/client/package.json index 8652106..446f3d4 100644 --- a/client/package.json +++ b/client/package.json @@ -48,7 +48,7 @@ "ts-loader": "^7.0.2" }, "dependencies": { - "@lourenci/react-kanban": "^0.15.0", + "@lourenci/react-kanban": "^2.0.0", "bulma": "^0.7.2", "bulma-switch": "^2.0.0", "react": "^16.12.0", diff --git a/client/src/components/BoardPage/BoardPage.tsx b/client/src/components/BoardPage/BoardPage.tsx index 68aeda2..f186703 100644 --- a/client/src/components/BoardPage/BoardPage.tsx +++ b/client/src/components/BoardPage/BoardPage.tsx @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; import { Page } from '../Page'; import { connect, DispatchProp } from 'react-redux'; -import Board from '@lourenci/react-kanban'; +import Board, { addColumn } from '@lourenci/react-kanban'; import { fetchBoards } from '../../store/actions/boards'; import { createIssue } from '../../store/actions/issues'; import { buildKanboard, moveCard } from '../../store/actions/kanboards'; @@ -13,6 +13,7 @@ export interface BoardPageProps extends DispatchProp { board: any kanboard: any } + export class BoardPage extends React.Component { state = { @@ -24,6 +25,7 @@ export class BoardPage extends React.Component { project: "" }, compactMode: true, + hasError: false, } onNewCardTitleChange: (evt: any) => void; @@ -41,19 +43,22 @@ export class BoardPage extends React.Component { this.onNewCardSaveClick = this.onNewCardSaveClick.bind(this); } + componentDidCatch(error, errorInfo) { + // You can also log the error to an error reporting service + console.error(error, errorInfo); + } + render() { const { board } = this.props; return ( -
- {this.renderBoard()} -
+ {this.renderBoard()}
); } renderBoard() { - const { kanboard } = this.props; + const { kanboard, board } = this.props; if (!kanboard) { return @@ -61,28 +66,48 @@ export class BoardPage extends React.Component { return ( -
-
-
- - +
-
- - {kanboard} - - { this.renderNewCardModal() } + +
+
+ + {kanboard} + + { this.renderNewCardModal() } +
@@ -167,28 +192,49 @@ export class BoardPage extends React.Component {
-

{lane.title}

+
+ {lane.cards.length} +
+ +

+ {lane.title} +

-
- +
) } - onCardDragEnd(source: any, dest: any) { + onMinimizeColumn(e: any) { + e.currentTarget.closest('.react-kanban-column').classList.toggle('minimized'); + } + + onCardDragEnd(card: any, source: any, dest: any) { const { board } = this.props; this.props.dispatch(moveCard( board.id, - source.fromLaneId, + source.fromColumnId, source.fromPosition, - dest.toLaneId, + dest.toColumnId, dest.toPosition )); } diff --git a/client/src/components/BoardPage/IssueCard.tsx b/client/src/components/BoardPage/IssueCard.tsx index 217f610..4b4f305 100644 --- a/client/src/components/BoardPage/IssueCard.tsx +++ b/client/src/components/BoardPage/IssueCard.tsx @@ -16,41 +16,50 @@ export class IssueCard extends React.PureComponent { return (
-
+
- { - card.issue.assignee && !compact ? -
-
- Image -
- {`@${card.issue.assignee.login}`} -
- : null - }
-

- {`#${card.issue.number}`}  - { !compact && card.issue.milestone ? {`- ${card.issue.milestone.title}`} : null } - { !compact ?
: ' - ' } - {card.issue.title} -

+ { !compact && + + } + { compact && + {`#${card.issue.number}`} + } + {card.issue.title ? card.issue.title : ''}
+ { !compact && + + }
- { - !compact ? -
- -
- -
-
: - null - }
); diff --git a/client/src/sass/_all.scss b/client/src/sass/_all.scss index 0cba34d..3bddada 100644 --- a/client/src/sass/_all.scss +++ b/client/src/sass/_all.scss @@ -1,5 +1,6 @@ @import 'bulma/bulma.sass'; +@import '../../node_modules/@lourenci/react-kanban/dist/styles.css'; @import 'bulma-switch/dist/css/bulma-switch.sass'; @import '_base.scss'; @import '_loader.scss'; -@import '_kanboard.scss'; \ No newline at end of file +@import '_kanboard.scss'; diff --git a/client/src/sass/_base.scss b/client/src/sass/_base.scss index 8d158ef..1e501a7 100644 --- a/client/src/sass/_base.scss +++ b/client/src/sass/_base.scss @@ -18,4 +18,8 @@ html, body { #app { display: flex; flex-direction: column; +} + +.mr-1 { + margin-right: 5px; } \ No newline at end of file diff --git a/client/src/sass/_kanboard.scss b/client/src/sass/_kanboard.scss index e1e29a8..674f199 100644 --- a/client/src/sass/_kanboard.scss +++ b/client/src/sass/_kanboard.scss @@ -44,6 +44,12 @@ .kanboard-lane { margin-bottom: $size-small; + background: $white; + top: 0; + + .expand { + display: none; + } } } @@ -51,4 +57,142 @@ justify-content: center; align-items: center; width: 50% !important; +} + +.react-kanban-board { + max-height: calc(100vh - calc(52px * 2)); + overflow: hidden; + overflow-x: scroll; + + scrollbar-color: $grey-lighter, #f1f1f1; + scrollbar-width: 5px; + + &::-webkit-scrollbar { + width: 5px; + height: 5px; + } + + /* Track */ + &::-webkit-scrollbar-track { + background: #f1f1f1; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: $grey-lighter; + } + + /* Handle on hover */ + &::-webkit-scrollbar-thumb:hover { + background: $green; + } +} + +.react-kanban-column { + transition: width ease .2s; + max-width: 305px; + min-width: 305px; + position: relative; + + max-height: 100%; + overflow-x: hidden; + overflow-y: scroll; + + scrollbar-color: $grey-lighter, #f1f1f1; + scrollbar-width: 5px; + + .kanboard-card { + display: block; + } + + &.minimized { + max-width: 70px; + min-width: 70px; + + writing-mode: vertical-rl; + text-orientation: sideways-right; + + + + .level-item { + margin-right: 0; + } + + .level-left { + margin-right: 0; + } + + .level-right.is-show-expand { + display: none; + } + + .kanboard-lane { + /*margin-right: -1em;*/ + + h3 { + margin-top: .5em; + /*margin-right: -.5em;*/ + } + + .tag { + writing-mode: horizontal-tb; + } + + .expand { + display: block !important; + margin-top: .5em; + } + } + + .kanboard-card { + display: none; + } + + } + + + &::-webkit-scrollbar { + width: 5px; + } + + /* Track */ + &::-webkit-scrollbar-track { + background: #f1f1f1; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: $grey-lighter; + } + + /* Handle on hover */ + &::-webkit-scrollbar-thumb:hover { + background: $green; + } +} + +.react-kanban-card__title { + position: sticky; + position: -webkit-sticky; +} + +.react-kanban-column { + background: white !important; +} + + +.kanboard-card { + overflow-wrap: break-word; + + a { + display: inline-block; + } + + .level { + margin-bottom: 0; + } + + .level-item { + max-width: 100%; + } } \ No newline at end of file diff --git a/client/src/store/reducers/kanboards.ts b/client/src/store/reducers/kanboards.ts index 5a099e0..4d1ff08 100644 --- a/client/src/store/reducers/kanboards.ts +++ b/client/src/store/reducers/kanboards.ts @@ -37,22 +37,22 @@ function handleMoveCard(state: any, action: any) { const kanboard = state.byID[boardID]; - const lanes = [ ...kanboard.lanes ]; - const fromLane = lanes[fromLaneID]; - const toLane = lanes[toLaneID]; + const columns = [ ...kanboard.columns ]; + const fromLane = columns[fromLaneID]; + const toLane = columns[toLaneID]; const card = fromLane.cards[fromPosition]; const fromCards = [ ...fromLane.cards ]; if (fromLaneID !== toLaneID) { fromCards.splice(fromPosition, 1); - lanes[fromLaneID] = { + columns[fromLaneID] = { ...fromLane, cards: fromCards, }; const toCards = [ ...toLane.cards ]; toCards.splice(toPosition, 0, card); - lanes[toLaneID] = { + columns[toLaneID] = { ...toLane, cards: toCards, }; @@ -67,7 +67,7 @@ function handleMoveCard(state: any, action: any) { ...state.byID, [boardID]: { ...state.byID[boardID], - lanes, + columns, }, } }; diff --git a/client/src/store/sagas/kanboards.ts b/client/src/store/sagas/kanboards.ts index f3d8f78..f510af2 100644 --- a/client/src/store/sagas/kanboards.ts +++ b/client/src/store/sagas/kanboards.ts @@ -22,7 +22,7 @@ export function* moveCardSaga(action: any) { } }); - const toLane = kanboard.lanes[toLaneID]; + const toLane = kanboard.columns[toLaneID]; const card = toLane.cards[toPosition]; if (!card) return; @@ -145,7 +145,7 @@ function createKanboard(board: Board, issues: any) { const kanboard = { id: board.id, - lanes: createKanboardLanes(board, issues), + columns: createKanboardLanes(board, issues), }; return kanboard;