Skip to content

Commit 2ee1ec1

Browse files
shivgarg5676edgarmueller
authored andcommitted
Add up/down buttons to material array layout (eclipsesource#1384)
Add move up/down buttons for moving elements within the material based array layout renderer. The buttons are not shown by default, but need to be set via an option `showSortButtons`.
1 parent e443ce9 commit 2ee1ec1

File tree

9 files changed

+593
-107
lines changed

9 files changed

+593
-107
lines changed

packages/core/src/util/array.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const move = (array: Array<any>, index: number, delta: number) => {
2+
const newIndex: number = index + delta;
3+
if (newIndex < 0 || newIndex >= array.length) return; //Already at the top or bottom.
4+
const indexes: number[] = [index, newIndex].sort((a, b) => a - b); //Sort the indixes
5+
array.splice(indexes[0], 2, array[indexes[1]], array[indexes[0]]);
6+
};
7+
8+
const moveUp = (array: Array<any>, toMove: number) => {
9+
move(array, toMove, -1);
10+
};
11+
12+
const moveDown = (array: Array<any>, toMove: number) => {
13+
move(array, toMove, 1);
14+
};
15+
16+
export { moveUp, moveDown };

packages/core/src/util/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,4 @@ export * from './ids';
143143
export * from './validator';
144144
export * from './combinators';
145145
export * from './uischema';
146+
export * from './array';

packages/core/src/util/renderer.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ import {
4747
isEnabled,
4848
isVisible,
4949
Resolve,
50-
resolveSubSchemas
50+
resolveSubSchemas,
51+
moveUp,
52+
moveDown
5153
} from '../util';
5254
import has from 'lodash/has';
5355
import { update } from '../actions';
@@ -498,6 +500,8 @@ export const mapStateToArrayControlProps = (
498500
export interface DispatchPropsOfArrayControl {
499501
addItem(path: string, value: any): () => void;
500502
removeItems?(path: string, toDelete: number[]): () => void;
503+
moveUp?(path: string, toMove: number): () => void;
504+
moveDown?(path: string, toMove: number): () => void;
501505
}
502506

503507
/**
@@ -531,6 +535,22 @@ export const mapDispatchToArrayControlProps = (
531535
return array;
532536
})
533537
);
538+
},
539+
moveUp: (path, toMove: number) => () => {
540+
dispatch(
541+
update(path, array => {
542+
moveUp(array, toMove);
543+
return array;
544+
})
545+
);
546+
},
547+
moveDown: (path, toMove: number) => () => {
548+
dispatch(
549+
update(path, array => {
550+
moveDown(array, toMove);
551+
return array;
552+
})
553+
);
534554
}
535555
});
536556

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { moveUp, moveDown } from '../../src/util/';
2+
import test from 'ava';
3+
let array: Array<number> = [];
4+
test.beforeEach(() => {
5+
array = [1, 2, 3, 4, 5];
6+
});
7+
8+
test('Move up should move item up by one index', t => {
9+
moveUp(array, 2);
10+
t.deepEqual(array, [1, 3, 2, 4, 5]);
11+
});
12+
13+
test('Move up should not change array if item to move is the first item ', t => {
14+
moveUp(array, 0);
15+
t.deepEqual(array, [1, 2, 3, 4, 5]);
16+
});
17+
18+
test('Move down should move item down by one index', t => {
19+
moveDown(array, 2);
20+
t.deepEqual(array, [1, 2, 4, 3, 5]);
21+
});
22+
23+
test('Move down should not change array if item to move is the last item ', t => {
24+
moveDown(array, 4);
25+
t.deepEqual(array, [1, 2, 3, 4, 5]);
26+
});

packages/examples/src/arrays-with-detail.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export const uischema = {
5757
type: 'Control',
5858
scope: '#/properties/comments',
5959
options: {
60+
showSortButtons: true,
6061
detail: {
6162
type: 'VerticalLayout',
6263
elements: [

packages/material/src/complex/MaterialTableControl.tsx

Lines changed: 102 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import union from 'lodash/union';
2727
import { DispatchCell } from '@jsonforms/react';
2828
import startCase from 'lodash/startCase';
2929
import range from 'lodash/range';
30-
import React from 'react';
30+
import React, { Fragment } from 'react';
3131
import { connect } from 'react-redux';
3232
import {
3333
FormHelperText,
@@ -37,7 +37,8 @@ import {
3737
TableCell,
3838
TableHead,
3939
TableRow,
40-
Typography
40+
Typography,
41+
Grid
4142
} from '@material-ui/core';
4243
import {
4344
ArrayLayoutProps,
@@ -51,13 +52,22 @@ import {
5152
} from '@jsonforms/core';
5253
import IconButton from '@material-ui/core/IconButton';
5354
import DeleteIcon from '@material-ui/icons/Delete';
55+
import ArrowDownward from '@material-ui/icons/ArrowDownward';
56+
import ArrowUpward from '@material-ui/icons/ArrowUpward';
57+
5458
import { WithDeleteDialogSupport } from './DeleteDialog';
5559
import NoBorderTableCell from './NoBorderTableCell';
5660
import TableToolbar from './TableToolbar';
57-
5861
// we want a cell that doesn't automatically span
5962
const styles = {
6063
fixedCell: {
64+
width: '150px',
65+
height: '50px',
66+
paddingLeft: 0,
67+
paddingRight: 0,
68+
textAlign: 'center'
69+
},
70+
fixedCellSmall: {
6171
width: '50px',
6272
height: '50px',
6373
paddingLeft: 0,
@@ -114,7 +124,7 @@ export interface EmptyTableProps {
114124
const EmptyTable = ({ numColumns }: EmptyTableProps) => (
115125
<TableRow>
116126
<NoBorderTableCell colSpan={numColumns}>
117-
<Typography align='center'>No data</Typography>
127+
<Typography align="center">No data</Typography>
118128
</NoBorderTableCell>
119129
</TableRow>
120130
);
@@ -141,14 +151,11 @@ const mapStateToNonEmptyCellProps = (
141151
state: JsonFormsState,
142152
ownProps: OwnPropsOfNonEmptyCell
143153
): NonEmptyCellProps => {
144-
145-
const path = ownProps.rowPath + (ownProps.schema.type === 'object' ? '.' + ownProps.propName : '');
154+
const path =
155+
ownProps.rowPath +
156+
(ownProps.schema.type === 'object' ? '.' + ownProps.propName : '');
146157
const errors = formatErrorMessage(
147-
union(
148-
getErrorAt(path, ownProps.schema)(state).map(
149-
error => error.message
150-
)
151-
)
158+
union(getErrorAt(path, ownProps.schema)(state).map(error => error.message))
152159
);
153160
return {
154161
rowPath: ownProps.rowPath,
@@ -185,12 +192,12 @@ class NonEmptyCellInner extends React.Component<NonEmptyCellProps, any> {
185192
path={path}
186193
/>
187194
) : (
188-
<DispatchCell
189-
schema={schema}
190-
uischema={controlWithoutLabel('#')}
191-
path={path}
192-
/>
193-
)}
195+
<DispatchCell
196+
schema={schema}
197+
uischema={controlWithoutLabel('#')}
198+
path={path}
199+
/>
200+
)}
194201
<FormHelperText error={!isValid}>{!isValid && errors}</FormHelperText>
195202
</NoBorderTableCell>
196203
);
@@ -202,60 +209,113 @@ interface NonEmptyRowProps {
202209
childPath: string;
203210
schema: JsonSchema;
204211
rowIndex: number;
212+
moveUp: () => void;
213+
moveDown: () => void;
214+
enableUp: boolean;
215+
enableDown: boolean;
216+
showSortButtons: boolean;
205217
}
206218

207-
const NonEmptyRow = React.memo(({
208-
childPath,
209-
schema,
210-
rowIndex,
211-
openDeleteDialog
212-
}: NonEmptyRowProps & WithDeleteDialogSupport) => (
213-
<TableRow key={childPath} hover>
214-
{generateCells(NonEmptyCell, schema, childPath)}
215-
<NoBorderTableCell style={styles.fixedCell}>
216-
<div style={{ display: 'flex', justifyContent: 'center' }}>
217-
<IconButton
218-
aria-label={`Delete`}
219-
onClick={() => openDeleteDialog(childPath, rowIndex)}
220-
>
221-
<DeleteIcon />
222-
</IconButton>
223-
</div>
224-
</NoBorderTableCell>
225-
</TableRow>
226-
));
219+
const NonEmptyRow = React.memo(
220+
({
221+
childPath,
222+
schema,
223+
rowIndex,
224+
openDeleteDialog,
225+
moveUp,
226+
moveDown,
227+
enableUp,
228+
enableDown,
229+
showSortButtons
230+
}: NonEmptyRowProps & WithDeleteDialogSupport) => {
231+
return (
232+
<TableRow key={childPath} hover>
233+
{generateCells(NonEmptyCell, schema, childPath)}
234+
<NoBorderTableCell
235+
style={showSortButtons ? styles.fixedCell : styles.fixedCellSmall}
236+
>
237+
<Grid container direction="row" justify="center" alignItems="center">
238+
{showSortButtons ? (
239+
<Fragment>
240+
<Grid item>
241+
<IconButton
242+
aria-label={`Move up`}
243+
onClick={moveUp}
244+
disabled={!enableUp}
245+
>
246+
<ArrowUpward />
247+
</IconButton>
248+
</Grid>
249+
<Grid item>
250+
<IconButton
251+
aria-label={`Move down`}
252+
onClick={moveDown}
253+
disabled={!enableDown}
254+
>
255+
<ArrowDownward />
256+
</IconButton>
257+
</Grid>
258+
</Fragment>
259+
) : (
260+
''
261+
)}
262+
263+
<Grid item>
264+
<IconButton
265+
aria-label={`Delete`}
266+
onClick={() => openDeleteDialog(childPath, rowIndex)}
267+
>
268+
<DeleteIcon />
269+
</IconButton>
270+
</Grid>
271+
</Grid>
272+
</NoBorderTableCell>
273+
</TableRow>
274+
);
275+
}
276+
);
227277
interface TableRowsProp {
228278
data: number;
229279
path: string;
230280
schema: JsonSchema;
281+
moveUp?(path: string, toMove: number): () => void;
282+
moveDown?(path: string, toMove: number): () => void;
283+
uischema: ControlElement;
231284
}
232285
const TableRows = ({
233286
data,
234287
path,
235288
schema,
236-
openDeleteDialog
289+
openDeleteDialog,
290+
moveUp,
291+
moveDown,
292+
uischema
237293
}: TableRowsProp & WithDeleteDialogSupport) => {
238294
const isEmptyTable = data === 0;
239-
240295
if (isEmptyTable) {
241296
return <EmptyTable numColumns={getValidColumnProps(schema).length + 1} />;
242297
}
243-
244298
return (
245299
<React.Fragment>
246300
{range(data).map((index: number) => {
247301
const childPath = Paths.compose(
248302
path,
249303
`${index}`
250304
);
251-
252305
return (
253306
<NonEmptyRow
254307
key={childPath}
255308
childPath={childPath}
256309
rowIndex={index}
257310
schema={schema}
258311
openDeleteDialog={openDeleteDialog}
312+
moveUp={moveUp(path, index)}
313+
moveDown={moveDown(path, index)}
314+
enableUp={index !== 0}
315+
enableDown={index !== data - 1}
316+
showSortButtons={
317+
uischema.options && uischema.options.showSortButtons
318+
}
259319
/>
260320
);
261321
})}
@@ -266,7 +326,7 @@ const TableRows = ({
266326
export class MaterialTableControl extends React.Component<
267327
ArrayLayoutProps & WithDeleteDialogSupport,
268328
any
269-
> {
329+
> {
270330
addItem = (path: string, value: any) => this.props.addItem(path, value);
271331
render() {
272332
const {

0 commit comments

Comments
 (0)