[ARVADOS-WORKBENCH2] updated: 1.2.0-744-g6aaf655
Git user
git at public.curoverse.com
Sat Oct 27 14:18:43 EDT 2018
Summary of changes:
src/components/autocomplete/autocomplete.tsx | 181 +++++++++++++++++++++
.../sharing-dialog/people-select.tsx | 51 ++++++
.../sharing-dialog/permission-select.tsx | 7 +-
.../sharing-invitation-form-component.tsx | 18 +-
4 files changed, 238 insertions(+), 19 deletions(-)
create mode 100644 src/components/autocomplete/autocomplete.tsx
create mode 100644 src/views-components/sharing-dialog/people-select.tsx
via 6aaf65540506d590fa826b08166c050b924a99ff (commit)
from bd9e873a8c5995ed74bf70114e7fb90ab14d1c1e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
commit 6aaf65540506d590fa826b08166c050b924a99ff
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date: Sat Oct 27 20:18:29 2018 +0200
Extract autocomplete, people-select
Feature #14365
Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
diff --git a/src/components/autocomplete/autocomplete.tsx b/src/components/autocomplete/autocomplete.tsx
new file mode 100644
index 0000000..e2f3547
--- /dev/null
+++ b/src/components/autocomplete/autocomplete.tsx
@@ -0,0 +1,181 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Input as MuiInput, Chip as MuiChip, Popper as MuiPopper, Paper, FormControl, InputLabel, StyleRulesCallback, withStyles, RootRef, Menu, MenuItem, ListItemText, ListItem, List } from '@material-ui/core';
+import { PopperProps } from '@material-ui/core/Popper';
+import { WithStyles } from '@material-ui/core/styles';
+import { noop } from 'lodash';
+
+export interface AutocompleteProps<Item, Suggestion> {
+ label?: string;
+ value: string;
+ items: Item[];
+ suggestions?: Suggestion[];
+ onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
+ onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
+ onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
+ onCreate?: () => void;
+ onDelete?: (item: Item, index: number) => void;
+ onSelect?: (suggestion: Suggestion) => void;
+ renderChipValue?: (item: Item) => string;
+ renderSuggestion?: (suggestion: Suggestion) => React.ReactNode;
+}
+
+export interface AutocompleteState {
+ suggestionsOpen: boolean;
+}
+export class Autocomplete<Value, Suggestion> extends React.Component<AutocompleteProps<Value, Suggestion>, AutocompleteState> {
+
+ state = {
+ suggestionsOpen: false,
+ };
+
+ containerRef = React.createRef<HTMLDivElement>();
+ inputRef = React.createRef<HTMLInputElement>();
+ render() {
+ return (
+ <RootRef rootRef={this.containerRef}>
+ <FormControl fullWidth>
+ {this.renderLabel()}
+ {this.renderInput()}
+ {this.renderSuggestions()}
+ </FormControl>
+ </RootRef>
+ );
+ }
+
+ renderLabel() {
+ const { label } = this.props;
+ return label && <InputLabel>{label}</InputLabel>;
+ }
+
+ renderInput() {
+ return <Input
+ inputRef={this.inputRef}
+ value={this.props.value}
+ startAdornment={this.renderChips()}
+ onFocus={this.handleFocus}
+ onBlur={this.handleBlur}
+ onChange={this.props.onChange}
+ onKeyPress={this.handleKeyPress}
+ />;
+ }
+
+ handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
+ const { onFocus = noop } = this.props;
+ this.setState({ suggestionsOpen: true });
+ onFocus(event);
+ }
+
+ handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {
+ const { onBlur = noop } = this.props;
+ this.setState({ suggestionsOpen: true });
+ onBlur(event);
+ }
+
+ handleKeyPress = ({ key }: React.KeyboardEvent<HTMLInputElement>) => {
+ const { onCreate = noop } = this.props;
+ if (key === 'Enter') {
+ onCreate();
+ }
+ }
+
+ renderChips() {
+ const { items, onDelete } = this.props;
+ return items.map(
+ (item, index) =>
+ <Chip
+ label={this.renderChipValue(item)}
+ key={index}
+ onDelete={() => onDelete ? onDelete(item, index) : undefined} />
+ );
+ }
+
+ renderChipValue(value: Value) {
+ const { renderChipValue } = this.props;
+ return renderChipValue ? renderChipValue(value) : JSON.stringify(value);
+ }
+
+ renderSuggestions() {
+ const { suggestions } = this.props;
+ return suggestions && suggestions.length > 0
+ ? <Popper
+ open={this.state.suggestionsOpen}
+ anchorEl={this.containerRef.current}>
+ <Paper>
+ <List dense style={{ width: this.getSuggestionsWidth() }}>
+ {suggestions.map(
+ (suggestion, index) =>
+ <ListItem button key={index} onClick={this.handleSelect(suggestion)}>
+ {this.renderSuggestion(suggestion)}
+ </ListItem>
+ )}
+ </List>
+ </Paper>
+ </Popper>
+ : null;
+ }
+
+ handleSelect(suggestion: Suggestion) {
+ return () => {
+ const { onSelect = noop } = this.props;
+ const { current } = this.inputRef;
+ if (current) {
+ current.focus();
+ }
+ onSelect(suggestion);
+ };
+ }
+
+ renderSuggestion(suggestion: Suggestion) {
+ const { renderSuggestion } = this.props;
+ return renderSuggestion
+ ? renderSuggestion(suggestion)
+ : <ListItemText>{JSON.stringify(suggestion)}</ListItemText>;
+ }
+
+ getSuggestionsWidth() {
+ return this.containerRef.current ? this.containerRef.current.offsetWidth : 'auto';
+ }
+}
+
+type ChipClasses = 'root';
+
+const chipStyles: StyleRulesCallback<ChipClasses> = theme => ({
+ root: {
+ marginRight: theme.spacing.unit / 4,
+ height: theme.spacing.unit * 3,
+ }
+});
+
+const Chip = withStyles(chipStyles)(MuiChip);
+
+type PopperClasses = 'root';
+
+const popperStyles: StyleRulesCallback<ChipClasses> = theme => ({
+ root: {
+ zIndex: theme.zIndex.modal,
+ }
+});
+
+const Popper = withStyles(popperStyles)(
+ ({ classes, ...props }: PopperProps & WithStyles<PopperClasses>) =>
+ <MuiPopper {...props} className={classes.root} />
+);
+
+type InputClasses = 'root';
+
+const inputStyles: StyleRulesCallback<InputClasses> = () => ({
+ root: {
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ input: {
+ minWidth: '20%',
+ flex: 1,
+ },
+});
+
+const Input = withStyles(inputStyles)(MuiInput);
diff --git a/src/views-components/sharing-dialog/people-select.tsx b/src/views-components/sharing-dialog/people-select.tsx
new file mode 100644
index 0000000..2e24560
--- /dev/null
+++ b/src/views-components/sharing-dialog/people-select.tsx
@@ -0,0 +1,51 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Autocomplete } from '~/components/autocomplete/autocomplete';
+
+
+export interface Person {
+ name: string;
+}
+export interface PeopleSelectProps {
+ suggestedPeople: Person[];
+}
+
+export interface PeopleSelectState {
+ value: string;
+ items: Person[];
+ suggestions: string[];
+}
+export class PeopleSelect extends React.Component<PeopleSelectProps, PeopleSelectState> {
+
+ state = {
+ value: '',
+ items: [{ name: 'Michal Klobukowski' }],
+ suggestions: ['Michal Klobukowski', 'Mateusz Ollik']
+ };
+
+ render() {
+ return (
+ <Autocomplete
+ label='Invite people'
+ value={this.state.value}
+ items={this.state.items}
+ suggestions={this.getSuggestions()}
+ renderChipValue={item => item.name}
+ onChange={this.handleChange} />
+ );
+ }
+
+ getSuggestions() {
+ const { value, suggestions } = this.state;
+ return value
+ ? suggestions.filter(suggestion => suggestion.includes(value))
+ : [];
+ }
+
+ handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+ this.setState({ value: event.target.value });
+ }
+}
diff --git a/src/views-components/sharing-dialog/permission-select.tsx b/src/views-components/sharing-dialog/permission-select.tsx
index 582f3bc..a2d0298 100644
--- a/src/views-components/sharing-dialog/permission-select.tsx
+++ b/src/views-components/sharing-dialog/permission-select.tsx
@@ -44,15 +44,12 @@ export const PermissionSelect = withStyles(PermissionSelectStyles)(
const renderPermissionItem = (value: string) =>
<PermissionItem {...{ value }} />;
-type PermissionItemClasses = 'value' | 'icon';
+type PermissionItemClasses = 'value';
const permissionItemStyles: StyleRulesCallback<PermissionItemClasses> = theme => ({
value: {
marginLeft: theme.spacing.unit,
},
- icon: {
- margin: `-${theme.spacing.unit / 2}px 0`,
- }
});
const PermissionItem = withStyles(permissionItemStyles)(
@@ -60,7 +57,7 @@ const PermissionItem = withStyles(permissionItemStyles)(
const Icon = getIcon(value);
return (
<Grid container alignItems='center'>
- <Icon className={classes.icon} />
+ <Icon />
<span className={classes.value}>
{value}
</span>
diff --git a/src/views-components/sharing-dialog/sharing-invitation-form-component.tsx b/src/views-components/sharing-dialog/sharing-invitation-form-component.tsx
index 9efbb1b..f1eb177 100644
--- a/src/views-components/sharing-dialog/sharing-invitation-form-component.tsx
+++ b/src/views-components/sharing-dialog/sharing-invitation-form-component.tsx
@@ -4,10 +4,12 @@
import * as React from 'react';
import { Field, WrappedFieldProps } from 'redux-form';
-import { Grid, Input, FormControl, FormHelperText, FormLabel, InputLabel } from '@material-ui/core';
+import { Grid, Input, FormControl, FormHelperText, FormLabel, InputLabel, Chip } from '@material-ui/core';
import { ChipsInput } from '~/components/chips-input/chips-input';
import { identity } from 'lodash';
import { PermissionSelect } from './permission-select';
+import { PeopleSelect } from './people-select';
+import ChipInput from 'material-ui-chip-input';
export default () =>
<Grid container spacing={8}>
@@ -26,19 +28,7 @@ const InvitedPeopleField = () =>
const InvitedPeopleFieldComponent = (props: WrappedFieldProps) =>
- <FormControl fullWidth>
- <FormLabel>
- Invite people
- </FormLabel>
- <ChipsInput
- {...props.input}
- value={['Test User']}
- createNewValue={identity}
- inputComponent={Input} />
- <FormHelperText>
- Helper text
- </FormHelperText>
- </FormControl>;
+ <PeopleSelect suggestedPeople={[]} />;
const PermissionSelectField = () =>
<Field
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list