[ARVADOS-WORKBENCH2] created: 1.2.0-638-g8762c56
Git user
git at public.curoverse.com
Sat Oct 13 11:37:41 EDT 2018
at 8762c56e9600fdc415d74f3d5db0e1c0ac700d9a (commit)
commit 8762c56e9600fdc415d74f3d5db0e1c0ac700d9a
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date: Fri Oct 12 14:05:07 2018 +0200
Create chips input
Feature #14229
Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
diff --git a/src/components/chips-input/chips-input.tsx b/src/components/chips-input/chips-input.tsx
new file mode 100644
index 0000000..c35db1b
--- /dev/null
+++ b/src/components/chips-input/chips-input.tsx
@@ -0,0 +1,85 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Chips } from '~/components/chips/chips';
+import { Input } from '@material-ui/core';
+
+interface ChipsInputProps<Value> {
+ values: Value[];
+ getLabel?: (value: Value) => string;
+ onChange: (value: Value[]) => void;
+ createNewValue: (value: string) => Value;
+}
+
+export class ChipsInput<Value> extends React.Component<ChipsInputProps<Value>> {
+
+ state = {
+ text: '',
+ };
+
+ filler = React.createRef<HTMLDivElement>();
+ timeout = -1;
+
+ setText = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this.setState({ text: event.target.value });
+ }
+
+ handleKeyPress = ({ key }: React.KeyboardEvent<HTMLInputElement>) => {
+ if (key === 'Enter') {
+ this.createNewValue();
+ } else if (key === 'Backspace') {
+ this.deleteLastValue();
+ }
+ }
+
+ createNewValue = () => {
+ if (this.state.text) {
+ const newValue = this.props.createNewValue(this.state.text);
+ this.setState({ text: '' });
+ this.props.onChange([...this.props.values, newValue]);
+ }
+ }
+
+ deleteLastValue = () => {
+ if (this.state.text.length === 0 && this.props.values.length > 0) {
+ this.props.onChange(this.props.values.slice(0, -1));
+ }
+ }
+
+ updateStyles = () => {
+ if(this.timeout){
+ clearTimeout(this.timeout);
+ }
+ this.timeout = setTimeout(() => this.forceUpdate());
+ }
+
+ render() {
+ this.updateStyles();
+ return <>
+ <div style={{ minHeight: '40px', zIndex: 1, position: 'relative' }}>
+ <Chips {...this.props} filler={<div ref={this.filler} />} />
+ </div>
+ <Input
+ value={this.state.text}
+ onChange={this.setText}
+ onKeyDown={this.handleKeyPress}
+ style={{ top: '-24px' }}
+ inputProps={{ style: this.getInputStyles(), }}
+ fullWidth />
+ </>;
+ }
+
+ getInputStyles = (): React.CSSProperties => ({
+ width: this.filler.current
+ ? this.filler.current.offsetWidth + 8
+ : '100%',
+ position: 'relative',
+ right: this.filler.current
+ ? `calc(${this.filler.current.offsetWidth}px - 100%)`
+ : 0,
+ top: '-5px',
+ zIndex: 1,
+ })
+}
\ No newline at end of file
diff --git a/src/components/chips/chips.tsx b/src/components/chips/chips.tsx
index c013080..c63b584 100644
--- a/src/components/chips/chips.tsx
+++ b/src/components/chips/chips.tsx
@@ -4,22 +4,21 @@
import * as React from 'react';
import { Chip, Grid } from '@material-ui/core';
-import { DragSource, DragSourceSpec, DragSourceCollector, ConnectDragSource, DragDropContextProvider, DropTarget, DropTargetSpec, DropTargetCollector, ConnectDropTarget } from 'react-dnd';
-import HTML5Backend from 'react-dnd-html5-backend';
+import { DragSource, DragSourceSpec, DragSourceCollector, ConnectDragSource, DropTarget, DropTargetSpec, DropTargetCollector, ConnectDropTarget } from 'react-dnd';
import { compose } from 'lodash/fp';
-interface ChipsFieldProps<Value> {
+interface ChipsProps<Value> {
values: Value[];
getLabel?: (value: Value) => string;
+ filler?: React.ReactNode;
onChange: (value: Value[]) => void;
}
-export class Chips<Value> extends React.Component<ChipsFieldProps<Value>> {
+export class Chips<Value> extends React.Component<ChipsProps<Value>> {
render() {
- const { values } = this.props;
- return <DragDropContextProvider backend={HTML5Backend}>
- <Grid container spacing={8}>
- {values.map(this.renderChip)}
- </Grid>
- </DragDropContextProvider>;
+ const { values, filler } = this.props;
+ return <Grid container spacing={8}>
+ {values.map(this.renderChip)}
+ {filler && <Grid item xs>{filler}</Grid>}
+ </Grid>;
}
renderChip = (value: Value, index: number) =>
@@ -32,13 +31,21 @@ export class Chips<Value> extends React.Component<ChipsFieldProps<Value>> {
dragSpec: DragSourceSpec<DraggableChipProps<Value>, { value: Value }> = {
beginDrag: ({ value }) => ({ value }),
endDrag: ({ value: dragValue }, monitor) => {
- const { value: dropValue } = monitor.getDropResult();
- const dragIndex = this.props.values.indexOf(dragValue);
- const dropIndex = this.props.values.indexOf(dropValue);
- const newValues = this.props.values.slice(0);
- newValues.splice(dragIndex, 1, dropValue);
- newValues.splice(dropIndex, 1, dragValue);
- this.props.onChange(newValues);
+ const result = monitor.getDropResult();
+ if (result) {
+ const { value: dropValue } = monitor.getDropResult();
+ const dragIndex = this.props.values.indexOf(dragValue);
+ const dropIndex = this.props.values.indexOf(dropValue);
+ const newValues = this.props.values.slice(0);
+ if (dragIndex < dropIndex) {
+ newValues.splice(dragIndex, 1);
+ newValues.splice(dropIndex - 1 || 0, 0, dragValue);
+ } else if (dragIndex > dropIndex) {
+ newValues.splice(dragIndex, 1);
+ newValues.splice(dropIndex, 0, dragValue);
+ }
+ this.props.onChange(newValues);
+ }
}
};
@@ -67,7 +74,11 @@ export class Chips<Value> extends React.Component<ChipsFieldProps<Value>> {
<Chip
color={isOver ? 'primary' : 'default'}
onDelete={this.deleteValue(value)}
- label={this.props.getLabel ? this.props.getLabel(value) : JSON.stringify(value)} />
+ label={this.props.getLabel ?
+ this.props.getLabel(value)
+ : typeof value === 'object'
+ ? JSON.stringify(value)
+ : value} />
</span>
)
);
diff --git a/src/index.tsx b/src/index.tsx
index 1d072d9..2b5c381 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -42,9 +42,10 @@ import { setUuidPrefix } from '~/store/workflow-panel/workflow-panel-actions';
import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set';
import { ContainerRequestState } from '~/models/container-request';
import { MountKind } from '~/models/mount-types';
-import { initProjectsTreePicker } from './store/tree-picker/tree-picker-actions';
import { setBuildInfo } from '~/store/app-info/app-info-actions';
import { getBuildInfo } from '~/common/app-info';
+import { DragDropContextProvider } from 'react-dnd';
+import HTML5Backend from 'react-dnd-html5-backend';
console.log(`Starting arvados [${getBuildInfo()}]`);
@@ -86,14 +87,16 @@ fetchConfig()
const App = () =>
<MuiThemeProvider theme={CustomTheme}>
- <Provider store={store}>
- <ConnectedRouter history={history}>
- <div>
- <Route path={Routes.TOKEN} component={TokenComponent} />
- <Route path={Routes.ROOT} component={MainPanelComponent} />
- </div>
- </ConnectedRouter>
- </Provider>
+ <DragDropContextProvider backend={HTML5Backend}>
+ <Provider store={store}>
+ <ConnectedRouter history={history}>
+ <div>
+ <Route path={Routes.TOKEN} component={TokenComponent} />
+ <Route path={Routes.ROOT} component={MainPanelComponent} />
+ </div>
+ </ConnectedRouter>
+ </Provider>
+ </DragDropContextProvider>
</MuiThemeProvider>;
ReactDOM.render(
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index f26d50f..02bf4c9 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -45,6 +45,8 @@ import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tre
import { SharedTreePicker } from '~/views-components/projects-tree-picker/shared-tree-picker';
import { FavoritesTreePicker } from '../../views-components/projects-tree-picker/favorites-tree-picker';
import { ProjectsTreePicker } from '~/views-components/projects-tree-picker/projects-tree-picker';
+import { Chips } from '~/components/chips/chips';
+import { ChipsInput } from '../../components/chips-input/chips-input';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
@@ -78,6 +80,22 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
type WorkbenchPanelProps = WithStyles<CssRules>;
+class TestInput extends React.Component {
+ state = {
+ values: ['asd']
+ };
+
+ handleChange = (values: string[]) => {
+ this.setState({ values });
+ }
+ render() {
+ return <ChipsInput
+ onChange={this.handleChange}
+ createNewValue={v => v}
+ values={this.state.values} />;
+ }
+}
+
export const WorkbenchPanel =
withStyles(styles)(({ classes }: WorkbenchPanelProps) =>
<Grid container item xs className={classes.root}>
@@ -92,6 +110,7 @@ export const WorkbenchPanel =
<MainContentBar />
</Grid>
<Grid item xs className={classes.content}>
+ <TestInput />
<Switch>
<Route path={Routes.PROJECTS} component={ProjectPanel} />
<Route path={Routes.COLLECTIONS} component={CollectionPanel} />
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list