From 854f90f4a32c96aea9be702fc69bc311ea876212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Tue, 30 Jan 2018 12:16:22 +0100 Subject: [PATCH 1/2] Improved SelectAccount component --- package.json | 5 +- src/components/SelectAccount.js | 39 --------------- src/components/SelectAccount/index.js | 63 +++++++++++++++++++++++++ src/components/SelectAccount/stories.js | 49 +++++++++++++++++++ src/components/base/Icon/index.js | 1 + src/components/base/Select/index.js | 39 ++++++++++++--- src/styles/global.js | 2 +- static/i18n/en/translation.yml | 3 ++ static/i18n/fr/translation.yml | 3 ++ yarn.lock | 14 ++++-- 10 files changed, 167 insertions(+), 51 deletions(-) delete mode 100644 src/components/SelectAccount.js create mode 100644 src/components/SelectAccount/index.js create mode 100644 src/components/SelectAccount/stories.js diff --git a/package.json b/package.json index f27c85f6..aae04fb4 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "redux-thunk": "^2.2.0", "shortid": "^2.2.8", "source-map-support": "^0.5.3", - "styled-components": "^3.1.2", + "styled-components": "^3.1.4", "styled-system": "^1.1.1" }, "devDependencies": { @@ -98,8 +98,9 @@ "babel-preset-flow": "^6.23.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", + "chance": "^1.0.13", "concurrently": "^3.5.1", - "dotenv": "^4.0.0", + "dotenv": "^5.0.0", "electron": "1.7.11", "electron-builder": "^19.55.2", "electron-devtools-installer": "^2.2.3", diff --git a/src/components/SelectAccount.js b/src/components/SelectAccount.js deleted file mode 100644 index 08957cb5..00000000 --- a/src/components/SelectAccount.js +++ /dev/null @@ -1,39 +0,0 @@ -// @flow - -import React from 'react' -import { connect } from 'react-redux' - -import type { MapStateToProps } from 'react-redux' - -import { getAccounts } from 'reducers/accounts' -import Select from 'components/base/Select' - -import type { Account } from 'types/common' - -const mapStateToProps: MapStateToProps<*, *, *> = state => ({ - accounts: Object.entries(getAccounts(state)).map(([, account]: [string, any]) => account), -}) - -type Props = { - accounts: Array, - onChange: () => Account | void, - value: Account | null, -} - -const SelectAccount = ({ accounts, value, onChange }: Props) => ( - value && a.id === value.id)} + renderSelected={renderItem} + renderItem={renderItem} + keyProp="id" + items={accounts} + placeholder={t('SelectAccount.placeholder')} + onChange={onChange} + /> +) + +SelectAccount.defaultProps = { + onChange: noop, + value: undefined, +} + +export default compose(connect(mapStateToProps), translate())(SelectAccount) diff --git a/src/components/SelectAccount/stories.js b/src/components/SelectAccount/stories.js new file mode 100644 index 00000000..fe379933 --- /dev/null +++ b/src/components/SelectAccount/stories.js @@ -0,0 +1,49 @@ +// @flow + +import React, { PureComponent } from 'react' +import { storiesOf } from '@storybook/react' +import Chance from 'chance' + +import { SelectAccount } from 'components/SelectAccount' + +const chance = new Chance() +const stories = storiesOf('SelectAccount', module) + +const accounts = [...Array(20)].map(() => ({ + id: chance.string(), + name: chance.name(), + type: 'BTC', + data: { + address: chance.string(), + balance: chance.floating({ min: 0, max: 20 }), + currentIndex: chance.integer({ min: 0, max: 20 }), + transactions: [], + }, +})) + +type State = { + value: any, +} + +class Wrapper extends PureComponent { + state = { + value: '', + } + + handleChange = item => this.setState({ value: item }) + + render() { + const { render } = this.props + const { value } = this.state + + return render({ onChange: this.handleChange, value }) + } +} + +stories.add('basic', () => ( + ( + k} /> + )} + /> +)) diff --git a/src/components/base/Icon/index.js b/src/components/base/Icon/index.js index 3735c4ce..2ef9d956 100644 --- a/src/components/base/Icon/index.js +++ b/src/components/base/Icon/index.js @@ -8,6 +8,7 @@ import FontAwesomeIcon from '@fortawesome/react-fontawesome' const Container = styled.span` ${fontSize}; ${color}; + position: relative; ` export default ({ name, ...props }: { name: string }) => ( diff --git a/src/components/base/Select/index.js b/src/components/base/Select/index.js index 50bf06a6..4cc4668e 100644 --- a/src/components/base/Select/index.js +++ b/src/components/base/Select/index.js @@ -8,9 +8,10 @@ import { space } from 'styled-system' import type { Element } from 'react' import Box from 'components/base/Box' -import Text from 'components/base/Text' +import Icon from 'components/base/Icon' import Input from 'components/base/Input' import Search from 'components/base/Search' +import Text from 'components/base/Text' import Triangles from './Triangles' @@ -49,6 +50,7 @@ const TriggerBtn = styled(Box).attrs({ ` const Item = styled(Box).attrs({ + align: 'center', p: 2, })` background: ${p => (p.highlighted ? p.theme.colors.cream : p.theme.colors.white)}; @@ -89,13 +91,31 @@ const FloatingTriangles = styled(Box).attrs({ padding-right: 1px; ` +const IconSelected = styled(Box).attrs({ + bg: 'blue', + color: 'white', + align: 'center', + justify: 'center', +})` + border-radius: 50%; + height: 15px; + font-size: 5px; + width: 15px; + opacity: ${p => (p.selected ? 1 : 0)}; + + // add top for center icon + > * { + top: 1px; + } +` + class Select extends PureComponent { static defaultProps = { itemToString: (item: Object) => item && item.name, keyProp: undefined, } - renderItems = (items: Array, downshiftProps: Object) => { + renderItems = (items: Array, selectedItem: any, downshiftProps: Object) => { const { renderItem, keyProp } = this.props const { getItemProps, highlightedIndex } = downshiftProps @@ -104,8 +124,15 @@ class Select extends PureComponent { {items.length ? ( items.map((item, i) => ( - - {renderItem ? renderItem(item) : {item.name_highlight || item.name}} + + + {renderItem ? renderItem(item) : {item.name_highlight || item.name}} + + + + + + )) @@ -176,10 +203,10 @@ class Select extends PureComponent { fuseOptions={fuseOptions} highlight={highlight} renderHighlight={renderHighlight} - render={items => this.renderItems(items, downshiftProps)} + render={items => this.renderItems(items, selectedItem, downshiftProps)} /> ) : ( - this.renderItems(items, downshiftProps) + this.renderItems(items, selectedItem, downshiftProps) ))} )} diff --git a/src/styles/global.js b/src/styles/global.js index 1144a3b4..2442895c 100644 --- a/src/styles/global.js +++ b/src/styles/global.js @@ -44,7 +44,7 @@ injectGlobal` em { font-style: italic; } - + ::-webkit-scrollbar { background-color: rgba(0, 0, 0, 0); width: 6px; diff --git a/static/i18n/en/translation.yml b/static/i18n/en/translation.yml index 6beace79..8d8eda6e 100644 --- a/static/i18n/en/translation.yml +++ b/static/i18n/en/translation.yml @@ -40,3 +40,6 @@ settings: display: language: Language + +SelectAccount: + placeholder: Select a account diff --git a/static/i18n/fr/translation.yml b/static/i18n/fr/translation.yml index b46f4439..5a4039ff 100644 --- a/static/i18n/fr/translation.yml +++ b/static/i18n/fr/translation.yml @@ -40,3 +40,6 @@ settings: display: language: Langage + +SelectAccount: + placeholder: Sélectionner un compte diff --git a/yarn.lock b/yarn.lock index 615fc4a2..acbd9d29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2219,6 +2219,10 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chance@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/chance/-/chance-1.0.13.tgz#666bec2db42b3084456a3e4f4c28a82db5ccb7e6" + chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" @@ -3138,6 +3142,10 @@ dotenv@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" +dotenv@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.0.tgz#0206eb5b336639bf377618a2a304ff00c6a1fddb" + downshift@^1.26.1: version "1.26.1" resolved "https://registry.yarnpkg.com/downshift/-/downshift-1.26.1.tgz#ae45a016f211d02f8000584d0b466142fde2dd6b" @@ -8535,9 +8543,9 @@ style-loader@^0.19.0, style-loader@^0.19.1: loader-utils "^1.0.2" schema-utils "^0.3.0" -styled-components@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.1.2.tgz#0769655335eb6800dc5f6691425f6f7fe1801e32" +styled-components@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-3.1.4.tgz#1bdc1409c9bacafee3510c573d23b73039b0d875" dependencies: buffer "^5.0.3" css-to-react-native "^2.0.3" From 6b29736975037b10593309362b36373f1786ccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=ABck=20V=C3=A9zien?= Date: Tue, 30 Jan 2018 17:52:34 +0100 Subject: [PATCH 2/2] Clean custom scrollbar --- package.json | 2 + src/components/SelectAccount/index.js | 16 +-- src/components/SideBar/index.js | 3 +- src/components/Wrapper.js | 3 +- src/components/base/GrowScroll/index.js | 91 ++++++++++++++ src/components/base/GrowScroll/stories.js | 13 ++ src/components/base/Modal/index.js | 1 + src/components/base/Select/index.js | 143 ++++++++++++++-------- src/styles/global.js | 23 ++-- yarn.lock | 18 ++- 10 files changed, 243 insertions(+), 70 deletions(-) create mode 100644 src/components/base/GrowScroll/index.js create mode 100644 src/components/base/GrowScroll/stories.js diff --git a/package.json b/package.json index aae04fb4..ff1b4d03 100644 --- a/package.json +++ b/package.json @@ -75,10 +75,12 @@ "react-router": "^4.2.0", "react-router-dom": "^4.2.2", "react-router-redux": "5.0.0-alpha.9", + "react-smooth-scrollbar": "^8.0.6", "redux": "^3.7.2", "redux-actions": "^2.2.1", "redux-thunk": "^2.2.0", "shortid": "^2.2.8", + "smooth-scrollbar": "^8.2.5", "source-map-support": "^0.5.3", "styled-components": "^3.1.4", "styled-system": "^1.1.1" diff --git a/src/components/SelectAccount/index.js b/src/components/SelectAccount/index.js index 67185905..f0032c7d 100644 --- a/src/components/SelectAccount/index.js +++ b/src/components/SelectAccount/index.js @@ -21,13 +21,6 @@ const mapStateToProps: MapStateToProps<*, *, *> = state => ({ accounts: Object.entries(getAccounts(state)).map(([, account]: [string, any]) => account), }) -type Props = { - accounts: Array, - onChange?: () => Account | void, - value?: Account | null, - t: T, -} - const renderItem = item => ( @@ -43,7 +36,14 @@ const renderItem = item => ( ) -export const SelectAccount = ({ accounts, value, onChange, t }: Props) => ( +type Props = { + accounts: Array, + onChange?: () => Account | void, + value?: Account | null, + t: T, +} + +export const SelectAccount = ({ accounts, onChange, value, t }: Props) => ( - - - - - ) : ( - - - {selectedItem && renderSelected ? ( - renderSelected(selectedItem) - ) : ( - {placeholder} - )} + }) => { + if (!isOpen) { + this._scrollToSelectedItem = true + } + + return ( + (this._useKeyboard = true)} + onKeyUp={() => (this._useKeyboard = false)} + > + {searchable ? ( + + + + + - - - )} - {isOpen && - (searchable ? ( - this.renderItems(items, selectedItem, downshiftProps)} - /> ) : ( - this.renderItems(items, selectedItem, downshiftProps) - ))} - - )} + + + {selectedItem && renderSelected ? ( + renderSelected(selectedItem) + ) : ( + {placeholder} + )} + + + + )} + {isOpen && + (searchable ? ( + this.renderItems(items, selectedItem, downshiftProps)} + /> + ) : ( + this.renderItems(items, selectedItem, downshiftProps) + ))} + + ) + }} /> ) } diff --git a/src/styles/global.js b/src/styles/global.js index 2442895c..929a570b 100644 --- a/src/styles/global.js +++ b/src/styles/global.js @@ -45,17 +45,22 @@ injectGlobal` font-style: italic; } - ::-webkit-scrollbar { - background-color: rgba(0, 0, 0, 0); - width: 6px; + .scrollbar-thumb-y { + width: 5px !important; } - ::-webkit-scrollbar:hover { - background-color: rgba(0, 0, 0, 0.09); + .scrollbar-thumb-x { + height: 5px !important; } - ::-webkit-scrollbar-thumb:vertical { - background: rgba(0, 0, 0, 0.5); + .scrollbar-track { + background: transparent !important; + transition: opacity 0.2s ease-in-out !important; } - ::-webkit-scrollbar-thumb:vertical:active { - background: rgba(0, 0, 0, 0.61); + .scrollbar-track-y { + right: 2px !important; + width: 5px !important; + } + .scrollbar-track-x { + bottom: 2px !important; + height: 5px !important; } ` diff --git a/yarn.lock b/yarn.lock index acbd9d29..8bdda5fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2606,7 +2606,7 @@ core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" -core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.5.3: +core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.5.1, core-js@^2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" @@ -7443,6 +7443,10 @@ react-router@^4.2.0: prop-types "^15.5.4" warning "^3.0.0" +react-smooth-scrollbar@^8.0.6: + version "8.0.6" + resolved "https://registry.yarnpkg.com/react-smooth-scrollbar/-/react-smooth-scrollbar-8.0.6.tgz#179072e6a547b3af589ea303c50fd86366275edc" + react-split-pane@^0.1.74: version "0.1.74" resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.74.tgz#cf79fc98b51ab0763fdc778749b810a102b036ca" @@ -8184,6 +8188,14 @@ slice-ansi@1.0.0: dependencies: readable-stream "~1.0.31" +smooth-scrollbar@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/smooth-scrollbar/-/smooth-scrollbar-8.2.5.tgz#676a2595b1aad97fe0835d2425e403b0d9c70eb3" + dependencies: + core-js "^2.5.1" + lodash-es "^4.17.4" + tslib "^1.7.1" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -8826,6 +8838,10 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +tslib@^1.7.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"