Skip to content

Commit 9d85981

Browse files
aaronschwartztannerlinsley
authored andcommitted
Resize columns (TanStack#170)
* Add column resizing for non pivot columns. * Fixing resizing UI issues and mobile functionality. * Remove calling onChange during resize events so that server example doesn't refetch data every time a column resizes.
1 parent b779a98 commit 9d85981

11 files changed

+262
-83
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,21 @@ These are all of the available props (and their default values) for the main `<R
153153
const id = filter.pivotId || filter.id
154154
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
155155
},
156+
resizable: true,
157+
defaultResizing: [],
156158

157159
// Controlled State Overrides (see Fully Controlled Component section)
158160
page: undefined,
159161
pageSize: undefined,
160-
sorting: undefined
162+
sorting: undefined,
161163

162164
// Controlled State Callbacks
163165
onExpandSubComponent: undefined,
164166
onPageChange: undefined,
165167
onPageSizeChange: undefined,
166168
onSortingChange: undefined,
167169
onFilteringChange: undefined,
170+
onResize: undefined,
168171

169172
// Pivoting
170173
pivotBy: undefined,
@@ -209,6 +212,7 @@ These are all of the available props (and their default values) for the main `<R
209212
getPaginationProps: () => ({}),
210213
getLoadingProps: () => ({}),
211214
getNoDataProps: () => ({}),
215+
getResizerProps: () => ({}),
212216

213217
// Global Column Defaults
214218
column: {
@@ -449,6 +453,8 @@ Every single built-in component's props can be dynamically extended using any on
449453
getTdProps={fn}
450454
getPaginationProps={fn}
451455
getLoadingProps={fn}
456+
getNoDataProps: {fn},
457+
getResizerProps: {fn}
452458
/>
453459
```
454460

@@ -635,6 +641,7 @@ Here are the props and their corresponding callbacks that control the state of t
635641
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
636642
onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
637643
onFilteringChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFilterChange handler by the filterRender option.
644+
onResize={(column, event, isTouch) => {...}} // Called when a user clicks on a resizing component (the right edge of a column header)
638645
/>
639646
```
640647

@@ -711,6 +718,7 @@ Object.assign(ReactTableDefaults, {
711718
NextComponent: undefined,
712719
LoadingComponent: component,
713720
NoDataComponent: component,
721+
ResizerComponent: component
714722
})
715723

716724
// Or change per instance

docs/iframe.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<body>
1717
<div id="root"></div>
1818
<div id="error-display"></div>
19-
<script src="static/preview.2b73b4112ed711a12fa8.bundle.js"></script>
19+
<script src="static/preview.0cd0bb2bf09f3220bea7.bundle.js"></script>
2020
</body>
2121
</html>
2222

docs/static/preview.0cd0bb2bf09f3220bea7.bundle.js

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/static/preview.0cd0bb2bf09f3220bea7.bundle.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/static/preview.2b73b4112ed711a12fa8.bundle.js

Lines changed: 0 additions & 37 deletions
This file was deleted.

docs/static/preview.2b73b4112ed711a12fa8.bundle.js.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/defaultProps.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export default {
2727
const id = filter.pivotId || filter.id
2828
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
2929
},
30+
resizable: true,
31+
defaultResizing: [],
3032

3133
// Controlled State Overrides
3234
// page: undefined,
@@ -39,6 +41,7 @@ export default {
3941
onPageSizeChange: undefined,
4042
onSortingChange: undefined,
4143
onFilteringChange: undefined,
44+
onResize: undefined,
4245

4346
// Pivoting
4447
pivotBy: undefined,
@@ -82,6 +85,7 @@ export default {
8285
getPaginationProps: emptyObj,
8386
getLoadingProps: emptyObj,
8487
getNoDataProps: emptyObj,
88+
getResizerProps: emptyObj,
8589

8690
// Global Column Defaults
8791
column: {
@@ -170,5 +174,6 @@ export default {
170174
</div>
171175
</div>
172176
),
173-
NoDataComponent: _.makeTemplateComponent('rt-noData')
177+
NoDataComponent: _.makeTemplateComponent('rt-noData'),
178+
ResizerComponent: _.makeTemplateComponent('rt-resizer')
174179
}

src/index.js

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,21 @@ export default React.createClass({
3939
getPaginationProps,
4040
getLoadingProps,
4141
getNoDataProps,
42+
getResizerProps,
4243
showPagination,
4344
expanderColumnWidth,
4445
manual,
4546
loadingText,
4647
noDataText,
4748
showFilters,
49+
resizable,
4850
// State
4951
loading,
5052
pageSize,
5153
page,
5254
sorting,
5355
filtering,
56+
resizing,
5457
pages,
5558
// Pivoting State
5659
pivotValKey,
@@ -71,6 +74,7 @@ export default React.createClass({
7174
LoadingComponent,
7275
SubComponent,
7376
NoDataComponent,
77+
ResizerComponent,
7478
// Data model
7579
resolvedData,
7680
allVisibleColumns,
@@ -106,7 +110,10 @@ export default React.createClass({
106110
const canPrevious = page > 0
107111
const canNext = page + 1 < pages
108112

109-
const rowMinWidth = _.sum(allVisibleColumns.map(d => _.getFirstDefined(d.width, d.minWidth)))
113+
const rowMinWidth = _.sum(allVisibleColumns.map(d => {
114+
const resized = resizing.find(x => x.id === d.id) || {}
115+
return _.getFirstDefined(resized.value, d.width, d.minWidth)
116+
}))
110117

111118
let rowIndex = -1
112119

@@ -149,9 +156,18 @@ export default React.createClass({
149156
}
150157

151158
const makeHeaderGroup = (column, i) => {
152-
const flex = _.sum(column.columns.map(d => d.width ? 0 : d.minWidth))
153-
const width = _.sum(column.columns.map(d => _.getFirstDefined(d.width, d.minWidth)))
154-
const maxWidth = _.sum(column.columns.map(d => _.getFirstDefined(d.width, d.maxWidth)))
159+
const flex = _.sum(column.columns.map(d => {
160+
const resized = resizing.find(x => x.id === d.id) || {}
161+
return d.width || resized.value ? 0 : d.minWidth
162+
}))
163+
const width = _.sum(column.columns.map(d => {
164+
const resized = resizing.find(x => x.id === d.id) || {}
165+
return _.getFirstDefined(resized.value, d.width, d.minWidth)
166+
}))
167+
const maxWidth = _.sum(column.columns.map(d => {
168+
const resized = resizing.find(x => x.id === d.id) || {}
169+
return _.getFirstDefined(resized.value, d.width, d.maxWidth)
170+
}))
155171
const theadGroupThProps = _.splitProps(getTheadGroupThProps(finalState, undefined, column, this))
156172
const columnHeaderProps = _.splitProps(column.getHeaderProps(finalState, undefined, column, this))
157173

@@ -255,10 +271,11 @@ export default React.createClass({
255271
}
256272

257273
const makeHeader = (column, i) => {
274+
const resized = resizing.find(x => x.id === column.id) || {}
258275
const sort = sorting.find(d => d.id === column.id)
259276
const show = typeof column.show === 'function' ? column.show() : column.show
260-
const width = _.getFirstDefined(column.width, column.minWidth)
261-
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
277+
const width = _.getFirstDefined(resized.value, column.width, column.minWidth)
278+
const maxWidth = _.getFirstDefined(resized.value, column.width, column.maxWidth)
262279
const theadThProps = _.splitProps(getTheadThProps(finalState, undefined, column, this))
263280
const columnHeaderProps = _.splitProps(column.getHeaderProps(finalState, undefined, column, this))
264281

@@ -279,6 +296,14 @@ export default React.createClass({
279296
...columnHeaderProps.rest
280297
}
281298

299+
const resizer = resizable ? (
300+
<ResizerComponent
301+
onMouseDown={e => this.resizeColumnStart(column, e, false)}
302+
onTouchStart={e => this.resizeColumnStart(column, e, true)}
303+
{...resizerProps}
304+
/>
305+
) : null
306+
282307
if (column.expander) {
283308
if (column.pivotColumns) {
284309
const pivotSort = sorting.find(d => d.id === column.id)
@@ -287,6 +312,7 @@ export default React.createClass({
287312
key={i}
288313
className={classnames(
289314
'rt-pivot-header',
315+
'rt-resizable-header',
290316
column.sortable && '-cursor-pointer',
291317
classes,
292318
pivotSort ? (pivotSort.desc ? '-sort-desc' : '-sort-asc') : ''
@@ -302,19 +328,22 @@ export default React.createClass({
302328
}}
303329
{...rest}
304330
>
305-
{column.pivotColumns.map((pivotColumn, i) => {
306-
return (
307-
<span key={pivotColumn.id}>
308-
{_.normalizeComponent(pivotColumn.header, {
309-
data: sortedData,
310-
column: column
311-
})}
312-
{i < column.pivotColumns.length - 1 && (
313-
<ExpanderComponent />
314-
)}
315-
</span>
316-
)
317-
})}
331+
<div className='rt-resizable-header-content'>
332+
{column.pivotColumns.map((pivotColumn, i) => {
333+
return (
334+
<span key={pivotColumn.id}>
335+
{_.normalizeComponent(pivotColumn.header, {
336+
data: sortedData,
337+
column: column
338+
})}
339+
{i < column.pivotColumns.length - 1 && (
340+
<ExpanderComponent />
341+
)}
342+
</span>
343+
)
344+
})}
345+
</div>
346+
{resizer}
318347
</ThComponent>
319348
)
320349
}
@@ -340,6 +369,7 @@ export default React.createClass({
340369
key={i}
341370
className={classnames(
342371
classes,
372+
'rt-resizable-header',
343373
sort ? (sort.desc ? '-sort-desc' : '-sort-asc') : '',
344374
column.sortable && '-cursor-pointer',
345375
!show && '-hidden',
@@ -355,10 +385,13 @@ export default React.createClass({
355385
}}
356386
{...rest}
357387
>
358-
{_.normalizeComponent(column.header, {
359-
data: sortedData,
360-
column: column
361-
})}
388+
<div className='rt-resizable-header-content'>
389+
{_.normalizeComponent(column.header, {
390+
data: sortedData,
391+
column: column
392+
})}
393+
</div>
394+
{resizer}
362395
</ThComponent>
363396
)
364397
}
@@ -387,8 +420,9 @@ export default React.createClass({
387420
}
388421

389422
const makeFilter = (column, i) => {
390-
const width = _.getFirstDefined(column.width, column.minWidth)
391-
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
423+
const resized = resizing.find(x => x.id === column.id) || {}
424+
const width = _.getFirstDefined(resized.value, column.width, column.minWidth)
425+
const maxWidth = _.getFirstDefined(resized.value, column.width, column.maxWidth)
392426
const theadFilterThProps = _.splitProps(getTheadFilterThProps(finalState, undefined, column, this))
393427
const columnHeaderProps = _.splitProps(column.getHeaderProps(finalState, undefined, column, this))
394428

@@ -530,9 +564,10 @@ export default React.createClass({
530564
{...trProps.rest}
531565
>
532566
{allVisibleColumns.map((column, i2) => {
567+
const resized = resizing.find(x => x.id === column.id) || {}
533568
const show = typeof column.show === 'function' ? column.show() : column.show
534-
const width = _.getFirstDefined(column.width, column.minWidth)
535-
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
569+
const width = _.getFirstDefined(resized.value, column.width, column.minWidth)
570+
const maxWidth = _.getFirstDefined(resized.value, column.width, column.maxWidth)
536571
const tdProps = _.splitProps(getTdProps(finalState, rowInfo, column, this))
537572
const columnProps = _.splitProps(column.getProps(finalState, rowInfo, column, this))
538573

@@ -681,9 +716,10 @@ export default React.createClass({
681716
style={trProps.style || {}}
682717
>
683718
{allVisibleColumns.map((column, i2) => {
719+
const resized = resizing.find(x => x.id === column.id) || {}
684720
const show = typeof column.show === 'function' ? column.show() : column.show
685-
const width = _.getFirstDefined(column.width, column.minWidth)
686-
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
721+
const width = _.getFirstDefined(resized.value, column.width, column.minWidth)
722+
const maxWidth = _.getFirstDefined(resized.value, column.width, column.maxWidth)
687723
const tdProps = _.splitProps(getTdProps(finalState, undefined, column, this))
688724
const columnProps = _.splitProps(column.getProps(finalState, undefined, column, this))
689725

@@ -743,9 +779,10 @@ export default React.createClass({
743779
{...tFootTrProps.rest}
744780
>
745781
{allVisibleColumns.map((column, i2) => {
782+
const resized = resizing.find(x => x.id === column.id) || {}
746783
const show = typeof column.show === 'function' ? column.show() : column.show
747-
const width = _.getFirstDefined(column.width, column.minWidth)
748-
const maxWidth = _.getFirstDefined(column.width, column.maxWidth)
784+
const width = _.getFirstDefined(resized.value, column.width, column.minWidth)
785+
const maxWidth = _.getFirstDefined(resized.value, column.width, column.maxWidth)
749786
const tFootTdProps = _.splitProps(getTfootTdProps(finalState, undefined, undefined, this))
750787
const columnProps = _.splitProps(column.getProps(finalState, undefined, column, this))
751788
const columnFooterProps = _.splitProps(column.getFooterProps(finalState, undefined, column, this))
@@ -841,6 +878,7 @@ export default React.createClass({
841878
const paginationProps = _.splitProps(getPaginationProps(finalState, undefined, undefined, this))
842879
const loadingProps = getLoadingProps(finalState, undefined, undefined, this)
843880
const noDataProps = getNoDataProps(finalState, undefined, undefined, this)
881+
const resizerProps = getResizerProps(finalState, undefined, undefined, this)
844882

845883
const makeTable = () => (
846884
<div

src/index.styl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ $expandSize = 7px
1818
.rt-thead
1919
display: flex
2020
flex-direction: column
21+
-webkit-user-select: none;
22+
-moz-user-select: none;
23+
-ms-user-select: none;
24+
user-select: none;
25+
2126
&.-headerGroups
2227
background: alpha(black, .03)
2328
border-bottom: 1px solid alpha(black, .05)
@@ -36,6 +41,9 @@ $expandSize = 7px
3641

3742
.rt-th
3843
.rt-td
44+
padding: 5px 5px
45+
line-height: normal
46+
position: relative
3947
border-right: 1px solid alpha(black, .05)
4048
transition box-shadow .3s $easeOutBack
4149
box-shadow:inset 0 0 0 0 transparent
@@ -47,6 +55,16 @@ $expandSize = 7px
4755
cursor: pointer
4856
&:last-child
4957
border-right: 0
58+
59+
.rt-resizable-header
60+
overflow: visible
61+
&:last-child
62+
overflow: hidden
63+
64+
.rt-resizable-header-content
65+
overflow: hidden
66+
text-overflow: ellipsis
67+
5068
.rt-tbody
5169
display: flex
5270
flex-direction: column
@@ -105,6 +123,17 @@ $expandSize = 7px
105123
cursor: pointer
106124
&.-open:after
107125
transform: translate(-50%, -50%) rotate(0deg)
126+
127+
.rt-resizer
128+
display: inline-block
129+
position: absolute
130+
width: 36px
131+
top: 0
132+
bottom: 0
133+
right: -18px
134+
cursor: col-resize
135+
z-index: 10
136+
108137
.rt-tfoot
109138
display: flex
110139
flex-direction: column

src/lifecycle.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export default {
1212
pageSize: this.props.defaultPageSize || 10,
1313
sorting: this.props.defaultSorting,
1414
expandedRows: {},
15-
filtering: this.props.defaultFiltering
15+
filtering: this.props.defaultFiltering,
16+
resizing: this.props.defaultResizing,
17+
currentlyResizing: undefined,
18+
skipNextSort: false
1619
}
1720
},
1821

0 commit comments

Comments
 (0)