[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