21720: removed calc values from csx where they didn't work
[arvados.git] / services / workbench2 / src / components / chips / chips.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from 'react';
6 import { CustomStyleRulesCallback } from 'common/custom-theme';
7 import { Chip, Grid } from '@mui/material';
8 import withStyles from '@mui/styles/withStyles';
9 import {
10     DragSource,
11     DragSourceSpec,
12     DragSourceCollector,
13     ConnectDragSource,
14     DropTarget,
15     DropTargetSpec,
16     DropTargetCollector,
17     ConnectDropTarget
18 } from 'react-dnd';
19 import { compose } from 'lodash/fp';
20 import { WithStyles } from '@mui/styles';
21 interface ChipsProps<Value> {
22     values: Value[];
23     getLabel?: (value: Value) => string;
24     filler?: React.ReactNode;
25     deletable?: boolean;
26     orderable?: boolean;
27     onChange: (value: Value[]) => void;
28     clickable?: boolean;
29 }
30
31 type CssRules = 'root';
32
33 const styles: CustomStyleRulesCallback<CssRules> = ({ spacing }) => ({
34     root: {
35         margin: `0px ${spacing(0.5)}`,
36     },
37 });
38 export const Chips = withStyles(styles)(
39     class Chips<Value> extends React.Component<ChipsProps<Value> & WithStyles<CssRules>> {
40         render() {
41             const { values, filler } = this.props;
42             return <Grid container spacing={1} className={this.props.classes.root}>
43                 {values && values.map(this.renderChip)}
44                 {filler && <Grid item xs>{filler}</Grid>}
45             </Grid>;
46         }
47
48         renderChip = (value: Value, index: number) => {
49             const { deletable, getLabel } = this.props;
50             return <Grid item key={index}>
51                 <Chip onDelete={deletable ? this.deleteValue(value) : undefined}
52                     label={getLabel !== undefined ? getLabel(value) : value} />
53             </Grid>
54         }
55
56         type = 'chip';
57
58         dragSpec: DragSourceSpec<DraggableChipProps<Value>, { value: Value }> = {
59             beginDrag: ({ value }) => ({ value }),
60             endDrag: ({ value: dragValue }, monitor) => {
61                 const result = monitor.getDropResult();
62                 if (result) {
63                     const { value: dropValue } = monitor.getDropResult();
64                     const dragIndex = this.props.values.indexOf(dragValue);
65                     const dropIndex = this.props.values.indexOf(dropValue);
66                     const newValues = this.props.values.slice(0);
67                     if (dragIndex < dropIndex) {
68                         newValues.splice(dragIndex, 1);
69                         newValues.splice(dropIndex - 1 || 0, 0, dragValue);
70                     } else if (dragIndex > dropIndex) {
71                         newValues.splice(dragIndex, 1);
72                         newValues.splice(dropIndex, 0, dragValue);
73                     }
74                     this.props.onChange(newValues);
75                 }
76             }
77         };
78
79         dragCollector: DragSourceCollector<{}> = connect => ({
80             connectDragSource: connect.dragSource(),
81         })
82
83         dropSpec: DropTargetSpec<DraggableChipProps<Value>> = {
84             drop: ({ value }) => ({ value }),
85         };
86
87         dropCollector: DropTargetCollector<{}> = (connect, monitor) => ({
88             connectDropTarget: connect.dropTarget(),
89             isOver: monitor.isOver(),
90         })
91         chip = compose(
92             DragSource(this.type, this.dragSpec, this.dragCollector),
93             DropTarget(this.type, this.dropSpec, this.dropCollector),
94         )(
95             ({ connectDragSource, connectDropTarget, isOver, value }: DraggableChipProps<Value> & CollectedProps) => {
96                 const connect = compose(
97                     connectDragSource,
98                     connectDropTarget,
99                 );
100
101                 const chip =
102                     <span>
103                         <Chip
104                             color={isOver ? 'primary' : 'default'}
105                             onDelete={this.props.deletable
106                                 ? this.deleteValue(value)
107                                 : undefined}
108                             clickable={this.props.clickable}
109                             label={this.props.getLabel ?
110                                 this.props.getLabel(value)
111                                 : typeof value === 'object'
112                                     ? JSON.stringify(value)
113                                     : value} />
114                     </span>;
115
116                 return this.props.orderable
117                     ? connect(chip)
118                     : chip;
119             }
120         );
121
122         deleteValue = (value: Value) => () => {
123             const { values } = this.props;
124             const index = values.indexOf(value);
125             const newValues = values.slice(0);
126             newValues.splice(index, 1);
127             this.props.onChange(newValues);
128         }
129     });
130
131 interface CollectedProps {
132     connectDragSource: ConnectDragSource;
133     connectDropTarget: ConnectDropTarget;
134
135     isOver: boolean;
136 }
137
138 interface DraggableChipProps<Value> {
139     value: Value;
140 }