-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tokens: Local identities autocomplete (#862)
* Hooks: add helper utils * AutoComplete: add component * LocalIdentitiesAutoComplete: add component * AssignVotePanelContent: use local identities autocomplete * AutoComplete: trigger onChange when closing * LocalIdentitiesAutoComplete: handle receiving two types of data in change handler * Autocomplete: add styles and props for setting inner styles * LocalIdentitiesAutoComplete: update styles for autocomplete buttons * IconMagnifyingGlass: add component * AutoComplete: add magnifying glass icon * AutoComplete: add animated transition when opening list * LocalIdentitiesAutoComplete: fix sending data to onChange handler * IconMagnifyingGlass: add react memo * AutoComplete: add react memo and useCallback * AutoComplete: add prop types * LocalIdentitiesAutoComplete: update to a more simulated api * LocalIdentitiesAutoComplete: update item and selected to functions * LocalIdentitiesAutoComplete: use callback on item and selected functions * LocalIdentityBadge: update effect on default value to simulate final api * LocalIdentityBadge: remove unused prop * LocalIdentityBadge: fix error on default value * LocalIdentityBadge: add prop types * LocalIdentityBadge: add react memo * LocalIdentityBadge: add useCallback to handlers * Autocomplete: remove selected functionality from component * LocalIdentityBadge: add selected functionality to component * Autocomplete: remove unused vars * LocalIdentityBadge: add useCallback to handlers and remove unused vars * LocalIdentityBadge: add onChange with selected name when clicking on selected option * Integrate local identities autocomplete with updated api (#872) * Use api to search identities * LocalIdentitiesAutoComplete: remove mock data and integrate with aragon api * LocalIdentitiesAutoComplete: fix deps on hooks * AutoComplete: add default prop and fix lint error * AutoCompleteSelected: add component that abstracts selected functionality * LocalIdentitiesAutoComplete: refactor to use AutoCompleteSelected * AssignVotePanelContent: set height for container * Correctly setup react.memo and react.forwardref * AutoComplete: remove unused dep * AutoCompleteSelected: add reference to props * Hooks: add hook to cycle through items using arrow keys * AutoComplete: add hook to cycle through items using arrow keys * Update aragon api and api react versions * useArrowKeysFocus: update var names * AutoComplete: update react memo and forwardref usage * AutoCompleteSelected: update react memo and forwardref usage * Finance: local identities autocomplete (#877) * Hooks: add hook helpers * AutoComplete: add component * AutoCompleteSelected: add component * IconMagnifyingGlass: add component * LocalIdentitiesAutoComplete: add component * Withdrawal: update recipient text input for autocomplete input * AutoComplete: remove unused dep * AutoCompleteSelected: add reference to props * Hooks: add hook to cycle through items using arrow keys * AutoComplete: add hook to cycle through items using arrow keys * useArrowKeysFocus: update var names * AutoComplete: update hook var names * Update aragon api and api react versions * useArrowKeysFocus: update var names * AutoComplete: update react memo and forwardref usage * AutoCompleteSelected: update react memo and forwardref usage
- Loading branch information
Showing
14 changed files
with
1,150 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
apps/finance/app/src/components/AutoComplete/AutoComplete.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import React, { useState, useRef, useCallback } from 'react' | ||
import PropTypes from 'prop-types' | ||
import styled from 'styled-components' | ||
import { Transition, animated } from 'react-spring' | ||
import { ButtonBase, TextInput, springs, theme, unselectable } from '@aragon/ui' | ||
import { useClickOutside, useOnBlur, useArrowKeysFocus } from '../../hooks' | ||
import IconMagnifyingGlass from './IconMagnifyingGlass' | ||
|
||
const { accent, contentBackground, contentBorder, textPrimary } = theme | ||
const identity = x => x | ||
|
||
function AutoComplete({ | ||
forwardedRef, | ||
itemButtonStyles = '', | ||
items, | ||
onSelect, | ||
onChange, | ||
renderItem = identity, | ||
required, | ||
value, | ||
wide, | ||
}) { | ||
const ref = forwardedRef | ||
const [opened, setOpened] = useState(true) | ||
const wrapRef = useRef() | ||
|
||
const handleClose = useCallback(() => setOpened(false)) | ||
const handleFocus = useCallback(() => setOpened(true)) | ||
const handleSelect = useCallback( | ||
item => e => { | ||
e.preventDefault() | ||
onSelect(item) | ||
}, | ||
[onSelect] | ||
) | ||
const handleChange = useCallback(({ target: { value } }) => onChange(value), [ | ||
onChange, | ||
]) | ||
|
||
const { containerRef, handleContainerBlur } = useArrowKeysFocus( | ||
'.autocomplete-items' | ||
) | ||
const { handleBlur } = useOnBlur(handleClose, wrapRef) | ||
useClickOutside(handleClose, wrapRef) | ||
|
||
return ( | ||
<div css="position: relative" ref={wrapRef} onBlur={handleBlur}> | ||
<TextInput | ||
css={` | ||
caret-color: ${accent}; | ||
padding-right: 35px; | ||
`} | ||
ref={ref} | ||
wide={wide} | ||
required={required} | ||
onChange={handleChange} | ||
onFocus={handleFocus} | ||
value={value} | ||
/> | ||
<div | ||
css={` | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
height: 40px; | ||
width: 35px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
`} | ||
> | ||
<IconMagnifyingGlass css="color: #a8b3c8" /> | ||
</div> | ||
<Transition | ||
config={springs.swift} | ||
items={opened && !!items.length} | ||
from={{ scale: 0.98, opacity: 0 }} | ||
enter={{ scale: 1, opacity: 1 }} | ||
leave={{ scale: 1, opacity: 0 }} | ||
native | ||
> | ||
{show => | ||
show && | ||
(({ scale, opacity }) => ( | ||
<Items | ||
ref={containerRef} | ||
onBlur={handleContainerBlur} | ||
role="listbox" | ||
style={{ | ||
opacity, | ||
transform: scale.interpolate(t => `scale3d(${t},${t},1)`), | ||
}} | ||
> | ||
{items.map(item => ( | ||
<Item role="option" key={item.key}> | ||
<ButtonBase | ||
className="autocomplete-items" | ||
onClick={handleSelect(item)} | ||
css={` | ||
width: 100%; | ||
${itemButtonStyles}; | ||
`} | ||
> | ||
{renderItem(item, value)} | ||
</ButtonBase> | ||
</Item> | ||
))} | ||
</Items> | ||
)) | ||
} | ||
</Transition> | ||
</div> | ||
) | ||
} | ||
|
||
AutoComplete.propTypes = { | ||
forwardedRef: PropTypes.object, | ||
itemButtonStyles: PropTypes.string, | ||
items: PropTypes.array.isRequired, | ||
onSelect: PropTypes.func.isRequired, | ||
onChange: PropTypes.func.isRequired, | ||
renderItem: PropTypes.func, | ||
required: PropTypes.bool, | ||
value: PropTypes.string, | ||
wide: PropTypes.bool, | ||
} | ||
|
||
const Item = styled.li` | ||
${unselectable()}; | ||
overflow: hidden; | ||
cursor: pointer; | ||
` | ||
|
||
const Items = styled(animated.ul)` | ||
position: absolute; | ||
z-index: 2; | ||
top: 100%; | ||
width: 100%; | ||
padding: 8px 0; | ||
color: ${textPrimary}; | ||
background: ${contentBackground}; | ||
border: 1px solid ${contentBorder}; | ||
box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.06); | ||
border-radius: 3px; | ||
padding: 0; | ||
margin: 0; | ||
list-style: none; | ||
& ${Item}:first-child { | ||
border-top-left-radius: 3px; | ||
border-top-right-radius: 3px; | ||
} | ||
& ${Item}:last-child { | ||
border-bottom-left-radius: 3px; | ||
border-bottom-right-radius: 3px; | ||
} | ||
` | ||
|
||
const AutoCompleteMemo = React.memo(AutoComplete) | ||
|
||
export default React.forwardRef((props, ref) => ( | ||
<AutoCompleteMemo {...props} forwardedRef={ref} /> | ||
)) |
113 changes: 113 additions & 0 deletions
113
apps/finance/app/src/components/AutoComplete/AutoCompleteSelected.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React, { useRef, useCallback } from 'react' | ||
import PropTypes from 'prop-types' | ||
import { ButtonBase, theme } from '@aragon/ui' | ||
import AutoComplete from './AutoComplete' | ||
|
||
const identity = x => x | ||
const noop = () => null | ||
|
||
function AutoCompleteSelected({ | ||
forwardedRef, | ||
itemButtonStyles, | ||
items, | ||
onChange, | ||
onSelect, // user clicks on an item in the list and thus, selects it | ||
onSelectedClick = noop, // when item is selected and user clicks on it, opens up the input for typing | ||
renderItem, | ||
required, | ||
renderSelected = identity, | ||
selected, | ||
selectedButtonStyles = '', | ||
value, | ||
wide, | ||
}) { | ||
const ref = forwardedRef | ||
const selectedRef = useRef() | ||
|
||
const handleSelect = useCallback( | ||
selected => { | ||
onSelect(selected) | ||
setTimeout(() => { | ||
selectedRef.current.focus() | ||
}, 0) | ||
}, | ||
[onChange] | ||
) | ||
const handleSelectedClick = useCallback( | ||
() => { | ||
onSelectedClick() | ||
setTimeout(() => { | ||
ref.current.select() | ||
ref.current.focus() | ||
}, 0) | ||
}, | ||
[ref, selected, onChange] | ||
) | ||
|
||
if (selected) { | ||
return ( | ||
<ButtonBase | ||
onClick={handleSelectedClick} | ||
ref={selectedRef} | ||
css={` | ||
height: 40px; | ||
width: 100%; | ||
background: #fff; | ||
cursor: pointer; | ||
border: 1px solid ${theme.contentBorder}; | ||
border-radius: 3px; | ||
${selectedButtonStyles}; | ||
`} | ||
> | ||
{renderSelected(selected)} | ||
</ButtonBase> | ||
) | ||
} | ||
|
||
return ( | ||
<AutoComplete | ||
itemButtonStyles={` | ||
border-left: 3px solid transparent; | ||
cursor: pointer; | ||
border-radius: 0; | ||
&:hover, | ||
&:focus { | ||
outline: 2px solid ${theme.accent}; | ||
background: #f9fafc; | ||
border-left: 3px solid ${theme.accent} | ||
} | ||
`} | ||
items={items} | ||
onChange={onChange} | ||
onSelect={handleSelect} | ||
ref={ref} | ||
renderItem={renderItem} | ||
required={required} | ||
value={value} | ||
wide={wide} | ||
/> | ||
) | ||
} | ||
|
||
AutoCompleteSelected.propTypes = { | ||
forwardedRef: PropTypes.object, | ||
itemButtonStyles: PropTypes.string, | ||
items: PropTypes.array.isRequired, | ||
onChange: PropTypes.func.isRequired, | ||
onSelect: PropTypes.func.isRequired, | ||
onSelectedClick: PropTypes.func, | ||
renderItem: PropTypes.func, | ||
renderSelected: PropTypes.func, | ||
required: PropTypes.bool, | ||
selected: PropTypes.object, | ||
selectedButtonStyles: PropTypes.string, | ||
value: PropTypes.string, | ||
wide: PropTypes.bool, | ||
} | ||
|
||
const AutoCompleteSelectedMemo = React.memo(AutoCompleteSelected) | ||
|
||
export default React.forwardRef((props, ref) => ( | ||
<AutoCompleteSelectedMemo {...props} forwardedRef={ref} /> | ||
)) |
14 changes: 14 additions & 0 deletions
14
apps/finance/app/src/components/AutoComplete/IconMagnifyingGlass.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react' | ||
|
||
const IconMagnifyingGlass = React.memo(props => { | ||
return ( | ||
<svg width={16} height={16} fill="none" {...props}> | ||
<path | ||
d="M15.757 14.573l-3.944-3.96a6.307 6.307 0 0 0 1.57-4.153C13.382 2.898 10.38 0 6.69 0 3.001 0 0 2.898 0 6.46s3.002 6.46 6.691 6.46a6.784 6.784 0 0 0 3.834-1.169l3.974 3.99c.166.167.39.259.629.259a.885.885 0 0 0 .605-.235.823.823 0 0 0 .024-1.192zM6.69 1.685c2.727 0 4.946 2.142 4.946 4.775 0 2.633-2.219 4.775-4.946 4.775S1.746 9.093 1.746 6.46c0-2.633 2.218-4.775 4.945-4.775z" | ||
fill="currentColor" | ||
/> | ||
</svg> | ||
) | ||
}) | ||
|
||
export default IconMagnifyingGlass |
Oops, something went wrong.