Skip to content

Commit 2450b1a

Browse files
authored
[docs] add inline editing example (#65)
1 parent 7de717c commit 2450b1a

File tree

6 files changed

+309
-4
lines changed

6 files changed

+309
-4
lines changed

docs/inline-editing.md

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,132 @@
11
# Inline Editing
22

3-
WIP
3+
`Inline Editing` is a very common feature in a table, but it's highly coupled with specific ui libraries, so it won't be a part of `BaseTable` itself.
4+
5+
## The Problem
6+
7+
`Inline Editing` would be a bit tricky in `BaseTable` because of it's using the `virtualization` technology to render the rows, so there are `overflow: hidden` for the table and rows and cells, so if your editing content is larger then the cell area, the content would be cut, you genius would find that you could override the `overflow: hidden` via style to prevent the content to be cut, but **PLEASE DON"T DO THAT**, as there would be always problems with this solution, e.g. what would happens if it's in the last row?
8+
9+
## How
10+
11+
The suggested solution is using something like `Portal` to render the editing content out of the cell, then it won't be constrained in the cell. As `Portal` needs a container to attach the target to, most of the custom renderers provide a param `container` to be used in this case, the `container` is table itself.
12+
13+
Internally we are using the `Overlay` component from [react-overlays](https://github.com/react-bootstrap/react-overlays) to do that, `react-overlays` is based on [Popper.js](https://github.com/FezVrasta/popper.js) which provides excellent positioning mechanism.
14+
15+
If you are using fixed mode with frozen columns, there will be a problem with the `Popper.js`. As the default `boundariesElement` for `preventOverflow` is `scrollParent`, but there would be three tables internal tables to mimic the frozen feature, and those tables are all scrollable, then the positioning could be not expected, you could change the `boundariesElement` to `viewport` or the `container` to fix that.
16+
17+
## API Design
18+
19+
`Column.editable: object | fun({ cellData, column, columnIndex, rowData, rowIndex })`
20+
21+
You can use `callOrReturn` exported from this package to get the result, the result could be a `boolean` to indicate the specific cell is editable or not, or an object which include the options like `{ disabled, ...editorProps }`, the result would be used in your custom `TableCell` component.
22+
23+
## Recipe
24+
25+
_The following is really a rough one, will improve it later_
26+
27+
```jsx
28+
// import { Overlay } from 'react-overlays'
29+
const { Overlay } = ReactOverlays
30+
31+
const CellContainer = styled.div`
32+
display: flex;
33+
flex: 1 0 100%;
34+
align-items: center;
35+
height: 100%;
36+
overflow: hidden;
37+
margin: 0 -5px;
38+
padding: 5px;
39+
border: 1px dashed transparent;
40+
`
41+
42+
const GlobalStyle = createGlobalStyle`
43+
.BaseTable__row:hover,
44+
.BaseTable__row--hover {
45+
${CellContainer} {
46+
border: 1px dashed #ccc;
47+
}
48+
}
49+
`
50+
51+
const Select = styled.select`
52+
width: 100%;
53+
height: 30px;
54+
margin-top: 10px;
55+
`
56+
57+
class EditableCell extends React.PureComponent {
58+
state = {
59+
value: this.props.cellData,
60+
editing: false,
61+
}
62+
63+
setTargetRef = ref => (this.targetRef = ref)
64+
65+
getTargetRef = () => this.targetRef
66+
67+
handleClick = () => this.setState({ editing: true })
68+
69+
handleHide = () => this.setState({ editing: false })
70+
71+
handleChange = e =>
72+
this.setState({
73+
value: e.target.value,
74+
editing: false,
75+
})
76+
77+
render() {
78+
const { container, rowIndex, columnIndex } = this.props
79+
const { value, editing } = this.state
80+
81+
return (
82+
<CellContainer ref={this.setTargetRef} onClick={this.handleClick}>
83+
{!editing && value}
84+
{editing && this.targetRef && (
85+
<Overlay
86+
show
87+
flip
88+
rootClose
89+
container={container}
90+
target={this.getTargetRef}
91+
onHide={this.handleHide}
92+
>
93+
{({ props, placement }) => (
94+
<div
95+
{...props}
96+
style={{
97+
...props.style,
98+
width: this.targetRef.offsetWidth,
99+
top:
100+
placement === 'top'
101+
? this.targetRef.offsetHeight
102+
: -this.targetRef.offsetHeight,
103+
}}
104+
>
105+
<Select value={value} onChange={this.handleChange}>
106+
<option value="grapefruit">Grapefruit</option>
107+
<option value="lime">Lime</option>
108+
<option value="coconut">Coconut</option>
109+
<option value="mango">Mango</option>
110+
</Select>
111+
</div>
112+
)}
113+
</Overlay>
114+
)}
115+
</CellContainer>
116+
)
117+
}
118+
}
119+
120+
const columns = generateColumns(5)
121+
const data = generateData(columns, 100)
122+
123+
columns[0].cellRenderer = EditableCell
124+
columns[0].width = 300
125+
126+
export default () => (
127+
<>
128+
<GlobalStyle />
129+
<Table fixed columns={columns} data={data} />
130+
</>
131+
)
132+
```

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"react-helmet": "^5.2.0",
2929
"react-inspector": "^2.3.1",
3030
"react-live-runner": "^0.7.3",
31+
"react-overlays": "^1.2.0",
3132
"react-sortable-hoc": "^1.9.1",
3233
"rehype-react": "^3.1.0",
3334
"sanitize.css": "^10.0.0",

website/siteConfig.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,5 +196,9 @@ module.exports = {
196196
title: 'Tag Name',
197197
path: '/examples/tag-name',
198198
},
199+
{
200+
title: 'Inline Editing',
201+
path: '/examples/inline-editing',
202+
},
199203
],
200204
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// import { Overlay } from 'react-overlays'
2+
const { Overlay } = ReactOverlays
3+
4+
const CellContainer = styled.div`
5+
display: flex;
6+
flex: 1 0 100%;
7+
align-items: center;
8+
height: 100%;
9+
overflow: hidden;
10+
margin: 0 -5px;
11+
padding: 5px;
12+
border: 1px dashed transparent;
13+
`
14+
15+
const GlobalStyle = createGlobalStyle`
16+
.BaseTable__row:hover,
17+
.BaseTable__row--hover {
18+
${CellContainer} {
19+
border: 1px dashed #ccc;
20+
}
21+
}
22+
`
23+
24+
const Select = styled.select`
25+
width: 100%;
26+
height: 30px;
27+
margin-top: 10px;
28+
`
29+
30+
class EditableCell extends React.PureComponent {
31+
state = {
32+
value: this.props.cellData,
33+
editing: false,
34+
}
35+
36+
setTargetRef = ref => (this.targetRef = ref)
37+
38+
getTargetRef = () => this.targetRef
39+
40+
handleClick = () => this.setState({ editing: true })
41+
42+
handleHide = () => this.setState({ editing: false })
43+
44+
handleChange = e =>
45+
this.setState({
46+
value: e.target.value,
47+
editing: false,
48+
})
49+
50+
render() {
51+
const { container, rowIndex, columnIndex } = this.props
52+
const { value, editing } = this.state
53+
54+
return (
55+
<CellContainer ref={this.setTargetRef} onClick={this.handleClick}>
56+
{!editing && value}
57+
{editing && this.targetRef && (
58+
<Overlay
59+
show
60+
flip
61+
rootClose
62+
container={container}
63+
target={this.getTargetRef}
64+
onHide={this.handleHide}
65+
>
66+
{({ props, placement }) => (
67+
<div
68+
{...props}
69+
style={{
70+
...props.style,
71+
width: this.targetRef.offsetWidth,
72+
top:
73+
placement === 'top'
74+
? this.targetRef.offsetHeight
75+
: -this.targetRef.offsetHeight,
76+
}}
77+
>
78+
<Select value={value} onChange={this.handleChange}>
79+
<option value="grapefruit">Grapefruit</option>
80+
<option value="lime">Lime</option>
81+
<option value="coconut">Coconut</option>
82+
<option value="mango">Mango</option>
83+
</Select>
84+
</div>
85+
)}
86+
</Overlay>
87+
)}
88+
</CellContainer>
89+
)
90+
}
91+
}
92+
93+
const columns = generateColumns(5)
94+
const data = generateData(columns, 100)
95+
96+
columns[0].cellRenderer = EditableCell
97+
columns[0].width = 300
98+
99+
export default () => (
100+
<>
101+
<GlobalStyle />
102+
<Table fixed columns={columns} data={data} />
103+
</>
104+
)

website/src/utils/baseScope.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react'
22
import ReactDOM from 'react-dom'
33
import styled, { css, keyframes, createGlobalStyle } from 'styled-components'
44
import * as ReactSortableHoc from 'react-sortable-hoc'
5+
import * as ReactOverlays from 'react-overlays'
56

67
import BaseTable, {
78
Column,
@@ -55,6 +56,7 @@ export default {
5556
createGlobalStyle,
5657

5758
ReactSortableHoc,
59+
ReactOverlays,
5860

5961
BaseTable,
6062
Column,

0 commit comments

Comments
 (0)