Skip to content

Commit 541ffff

Browse files
committed
[#278 #263] Available to inline edit Lane title
1 parent 36da27a commit 541ffff

10 files changed

Lines changed: 1067 additions & 25 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ This is the container component that encapsulates the lanes and cards
111111
| editable | boolean | Makes the entire board editable. Allow cards to be added or deleted Default: false |
112112
| canAddLanes | boolean | Allows new lanes to be added to the board. Default: false |
113113
| hideCardDeleteIcon | boolean | Disable showing the delete icon to the top right corner of the card (when board is editable) |
114+
| editLaneTitle | boolean | Allow inline lane title edit Default: false |
114115

115116

116117
### Callbacks and handlers
@@ -128,7 +129,8 @@ This is the container component that encapsulates the lanes and cards
128129
| onCardMoveAcrossLanes | function | Called when a card is moved across lanes `onCardMoveAcrossLanes(fromLaneId, toLaneId, cardId, index)` |
129130
| onLaneAdd | function | Called when a new lane is added: `onLaneAdd(params)` |
130131
| onLaneDelete | function | Called when a lane is deleted `onLaneDelete(laneId)` |
131-
| onLaneClick | function | Called when a lane is clicked: `onLaneClick(laneId)`. Card clicks are not propagated to lane click event |
132+
| onLaneUpdate | function | Called when a lane attributes are updated `onLaneUpdate(laneId, data)` |
133+
| onLaneClick | function | Called when a lane is clicked `onLaneClick(laneId)`. Card clicks are not propagated to lane click event |
132134
| onLaneScroll | function | Called when a lane is scrolled to the end: `onLaneScroll(requestedPage, laneId)` |
133135

134136
### Other functions

src/actions/LaneActions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const removeCard = createAction('REMOVE_CARD')
55
export const moveCardAcrossLanes = createAction('MOVE_CARD')
66
export const updateCards = createAction('UPDATE_CARDS')
77
export const updateLanes = createAction('UPDATE_LANES')
8+
export const updateLane = createAction('UPDATE_LANE')
89
export const paginateLane = createAction('PAGINATE_LANE')
910
export const moveLane = createAction('MOVE_LANE')
1011
export const removeLane = createAction('REMOVE_LANE')

src/components/Lane/LaneHeader.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,42 @@ import {Title, LaneHeader, RightContent } from 'styles/Base'
66
import LaneMenu from './LaneHeader/LaneMenu'
77

88
const LaneHeaderComponent = ({
9-
updateTitle, canAddLanes, onDelete, onDoubleClick, inlineEditTitle, label, title, titleStyle, labelStyle, t
9+
updateTitle, canAddLanes, onDelete, onDoubleClick, editLaneTitle, label, title, titleStyle, labelStyle, t
1010
}) => {
1111

1212
return (
1313
<LaneHeader onDoubleClick={onDoubleClick}>
1414
<Title style={titleStyle}>
15-
{inlineEditTitle ?
16-
<EditableLabel value={title} border placeholder={t('placeholder.title')} onSave={updateTitle} /> :
17-
title
18-
}
15+
{editLaneTitle ?
16+
<EditableLabel value={title} border inline placeholder={t('placeholder.title')} onChange={updateTitle} /> :
17+
title
18+
}
1919
</Title>
2020
{label && (
2121
<RightContent>
2222
<span style={labelStyle}>{label}</span>
2323
</RightContent>
24-
)}
24+
)}
2525
{canAddLanes && <LaneMenu t={t} onDelete={onDelete}/>}
2626
</LaneHeader>
2727
)
2828
}
2929

3030
LaneHeaderComponent.propTypes = {
3131
updateTitle: PropTypes.func,
32-
inlineEditTitle: PropTypes.bool,
32+
editLaneTitle: PropTypes.bool,
3333
canAddLanes: PropTypes.bool,
3434
label: PropTypes.string,
3535
title: PropTypes.string,
3636
onDelete: PropTypes.func,
3737
onDoubleClick: PropTypes.func,
38+
editLaneTitle: PropTypes.bool,
3839
t: PropTypes.func.isRequired
3940
}
4041

4142
LaneHeaderComponent.defaultProps = {
4243
updateTitle: () => {},
43-
inlineEditTitle: false,
44+
editLaneTitle: false,
4445
canAddLanes: false
4546
}
4647

src/components/widgets/EditableLabel.js

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import React from 'react'
22
import PropTypes from 'prop-types'
33

44
class EditableLabel extends React.Component {
5-
state = {text: ''}
5+
constructor({value}) {
6+
super()
7+
this.state = { value: value }
8+
}
69

710
getText = el => {
811
return el.innerText
912
}
1013

1114
onTextChange = ev => {
12-
const text = this.getText(ev.target)
13-
this.setState({text: text})
15+
const value = this.getText(ev.target)
16+
this.setState({value: value})
1417
}
1518

1619
componentDidMount() {
@@ -20,20 +23,35 @@ class EditableLabel extends React.Component {
2023
}
2124

2225
onBlur = () => {
23-
this.props.onChange(this.state.text)
26+
this.props.onChange(this.state.value)
2427
}
2528

2629
onPaste = ev => {
2730
ev.preventDefault()
28-
const text = ev.clipboardData.getData('text')
29-
document.execCommand('insertText', false, text)
31+
const value = ev.clipboardData.getData('text')
32+
document.execCommand('insertText', false, value)
3033
}
3134

3235
getClassName = () => {
33-
const placeholder = this.state.text === '' ? 'comPlainTextContentEditable--has-placeholder' : ''
36+
const placeholder = this.state.value === '' ? 'comPlainTextContentEditable--has-placeholder' : ''
3437
return `comPlainTextContentEditable ${placeholder}`
3538
}
3639

40+
onKeyDown = (e) => {
41+
if(e.keyCode === 13) {
42+
this.props.onChange(this.state.value)
43+
this.refDiv.blur()
44+
e.preventDefault()
45+
}
46+
if(e.keyCode === 27) {
47+
this.refDiv.value = this.props.value
48+
this.setState({value: this.props.value})
49+
// this.refDiv.blur()
50+
e.preventDefault()
51+
e.stopPropagation()
52+
}
53+
}
54+
3755
render() {
3856
return (
3957
<div
@@ -43,21 +61,26 @@ class EditableLabel extends React.Component {
4361
onPaste={this.onPaste}
4462
onBlur={this.onBlur}
4563
onInput={this.onTextChange}
46-
placeholder={this.props.placeholder}
47-
/>
64+
onKeyDown={this.onKeyDown}
65+
placeholder={this.props.value.length == 0 ? false : this.props.placeholder}
66+
>{this.props.value}</div>
4867
)
4968
}
5069
}
5170

52-
EditableLabel.defaultProps = {
53-
onChange: () => {},
54-
placeholder: '',
55-
autoFocus: false
56-
}
5771
EditableLabel.propTypes = {
5872
onChange: PropTypes.func,
5973
placeholder: PropTypes.string,
60-
autoFocus: PropTypes.bool
74+
autoFocus: PropTypes.bool,
75+
inline: PropTypes.bool,
76+
value: PropTypes.string,
6177
}
6278

79+
EditableLabel.defaultProps = {
80+
onChange: () => {},
81+
placeholder: '',
82+
autoFocus: false,
83+
inline: false,
84+
value: ''
85+
}
6386
export default EditableLabel

src/controllers/BoardContainer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class BoardContainer extends Component {
7676
})
7777
case 'UPDATE_LANES':
7878
return actions.updateLanes(event.lanes)
79+
case 'UPDATE_LANE':
80+
return actions.updateLane(event.lane)
7981
}
8082
}
8183
}
@@ -132,6 +134,7 @@ class BoardContainer extends Component {
132134
'onCardMoveAcrossLanes',
133135
'onLaneScroll',
134136
'onLaneDelete',
137+
'onLaneUpdate',
135138
'onCardClick',
136139
'onCardDelete',
137140
'onCardAdd',
@@ -146,6 +149,7 @@ class BoardContainer extends Component {
146149
'handleDragStart',
147150
'handleDragEnd',
148151
'cardDragClass',
152+
'editLaneTitle',
149153
't'
150154
])
151155

@@ -211,6 +215,7 @@ BoardContainer.propTypes = {
211215
onLaneAdd: PropTypes.func,
212216
onLaneDelete: PropTypes.func,
213217
onLaneClick: PropTypes.func,
218+
onLaneUpdate: PropTypes.func,
214219
laneSortFunction: PropTypes.func,
215220
draggable: PropTypes.bool,
216221
collapsibleLanes: PropTypes.bool,
@@ -241,6 +246,7 @@ BoardContainer.defaultProps = {
241246
onLaneAdd: () => {},
242247
onLaneDelete: () => {},
243248
onCardMoveAcrossLanes: () => {},
249+
onLaneUpdate: () => {},
244250
editable: false,
245251
canAddLanes: false,
246252
hideCardDeleteIcon: false,

src/controllers/Lane.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,18 @@ class Lane extends Component {
194194
const {id} = this.props
195195
this.props.actions.removeLane({laneId: id})
196196
this.props.onLaneDelete(id)
197+
}
198+
199+
updateTitle = (value) => {
200+
this.props.actions.updateLane({id: this.props.id, title: value})
201+
this.props.onLaneUpdate(this.props.id, {title: value })
197202
}
198203

199204
renderHeader = () => {
200205
const { components } = this.props
201206
const pickedProps = pick(
202207
this.props,
203-
['id','label','title','titleStyle','cards', 'labelStyle','t','inlineEditTitle','canAddLanes']
208+
['id','label','title','titleStyle','cards', 'labelStyle','t','editLaneTitle','canAddLanes']
204209
)
205210
return (
206211
<components.LaneHeader {...pickedProps} onDelete={this.removeLane} onDoubleClick={this.toggleLaneCollapsed} updateTitle={this.updateTitle}/>

src/helpers/LaneHelper.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@ const LaneHelper = {
4646
return update(state, {lanes: {$push: [newLane]}})
4747
},
4848

49+
updateLane: (state, updatedLane) => {
50+
const newLanes = state.lanes.map(lane => {
51+
if (updatedLane.id == lane.id ) {
52+
return { ...lane, ...updatedLane }
53+
} else {
54+
return lane
55+
}
56+
})
57+
return update(state, {lanes: {$set: newLanes}})
58+
},
59+
4960
removeCardFromLane: (state, {laneId, cardId}) => {
5061
const lanes = state.lanes.map(lane => {
5162
if (lane.id === laneId) {

src/styles/Base.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const GlobalStyle = createGlobalStyle`
5454
5555
.comPlainTextContentEditable {
5656
-webkit-user-modify: read-write-plaintext-only;
57+
cursor: text;
5758
}
5859
5960
.comPlainTextContentEditable--has-placeholder::before {

stories/EditableBoard.story.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,19 @@ storiesOf('Editable Board', module)
7070
},
7171
{info: 'Can hide the add card button on specific lanes'}
7272
)
73+
.add(
74+
'Inline Edit Lane Title',
75+
() => {
76+
return (
77+
<Board
78+
data={smallData}
79+
editable
80+
canAddLanes
81+
editLaneTitle
82+
onLaneUpdate={ (laneId, data) => debug(`onLaneUpdate: ${laneId} -> ${data.title}`)}
83+
onLaneAdd={t => debug('You added a line with title ' + t.title)}
84+
/>
85+
)
86+
},
87+
{info: 'Allow edit lane title'}
88+
)

0 commit comments

Comments
 (0)