1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import * as React from 'react';
6 import { Chip, Grid } from '@material-ui/core';
7 import { DragSource, DragSourceSpec, DragSourceCollector, ConnectDragSource, DragDropContextProvider, DropTarget, DropTargetSpec, DropTargetCollector, ConnectDropTarget } from 'react-dnd';
8 import HTML5Backend from 'react-dnd-html5-backend';
9 import { compose } from 'lodash/fp';
10 interface ChipsFieldProps<Value> {
12 getLabel?: (value: Value) => string;
13 onChange: (value: Value[]) => void;
15 export class Chips<Value> extends React.Component<ChipsFieldProps<Value>> {
17 const { values } = this.props;
18 return <DragDropContextProvider backend={HTML5Backend}>
19 <Grid container spacing={8}>
20 {values.map(this.renderChip)}
22 </DragDropContextProvider>;
25 renderChip = (value: Value, index: number) =>
26 <Grid item key={index}>
27 <this.chip {...{ value }} />
32 dragSpec: DragSourceSpec<DraggableChipProps<Value>, { value: Value }> = {
33 beginDrag: ({ value }) => ({ value }),
34 endDrag: ({ value: dragValue }, monitor) => {
35 const { value: dropValue } = monitor.getDropResult();
36 const dragIndex = this.props.values.indexOf(dragValue);
37 const dropIndex = this.props.values.indexOf(dropValue);
38 const newValues = this.props.values.slice(0);
39 newValues.splice(dragIndex, 1, dropValue);
40 newValues.splice(dropIndex, 1, dragValue);
41 this.props.onChange(newValues);
45 dragCollector: DragSourceCollector<{}> = connect => ({
46 connectDragSource: connect.dragSource(),
49 dropSpec: DropTargetSpec<DraggableChipProps<Value>> = {
50 drop: ({ value }) => ({ value }),
53 dropCollector: DropTargetCollector<{}> = (connect, monitor) => ({
54 connectDropTarget: connect.dropTarget(),
55 isOver: monitor.isOver(),
58 DragSource(this.type, this.dragSpec, this.dragCollector),
59 DropTarget(this.type, this.dropSpec, this.dropCollector),
61 ({ connectDragSource, connectDropTarget, isOver, value }: DraggableChipProps<Value> & CollectedProps) =>
68 color={isOver ? 'primary' : 'default'}
69 onDelete={this.deleteValue(value)}
70 label={this.props.getLabel ? this.props.getLabel(value) : JSON.stringify(value)} />
75 deleteValue = (value: Value) => () => {
76 const { values } = this.props;
77 const index = values.indexOf(value);
78 const newValues = values.slice(0);
79 newValues.splice(index, 1);
80 this.props.onChange(newValues);
84 interface CollectedProps {
85 connectDragSource: ConnectDragSource;
86 connectDropTarget: ConnectDropTarget;
91 interface DraggableChipProps<Value> {