Advertisement
Guest User

Untitled

a guest
Apr 23rd, 2019
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.92 KB | None | 0 0
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import styled from 'styled-components';
  4. import { withApollo } from 'react-apollo';
  5.  
  6. import debounce from 'lodash/debounce';
  7.  
  8. import InputLoader from 'ui/atoms/loaders/input-loader/InputLoader';
  9. import { textCss } from 'ui/atoms/texts/base/style';
  10. import CustomScrollbar, { ScrollThumb } from 'ui/atoms/containers/custom-scrollbar/CustomScrollbar';
  11. import Overlay from 'ui/molecules/search-overlay/SearchOverlay';
  12. import SearchItem from 'ui/components/items/search-item/SearchItem';
  13. import ErrorText from 'ui/atoms/texts/error-text/ErrorText';
  14. import SearchResultsHeading from 'ui/molecules/search-results-heading/SearchResultsHeading';
  15. import SearchInput from 'ui/molecules/search-input/SearchInput';
  16. import TabList from 'ui/organisms/tab-list/TabList';
  17. import LangContext from 'ui/context/lang/LangContext';
  18.  
  19. import SearchOverlayContext from 'app/structure/search-overlay/SearchOverlayContext';
  20. import { openModal } from 'app/structure/modal/utils';
  21. import View from 'app/structure/view/View';
  22.  
  23. import searchQuery from 'graphql/queries/search';
  24.  
  25. const ContainerSearch = styled.div`
  26. position: relative;
  27. `;
  28.  
  29. const TotalResults = styled.span`
  30. ${textCss};
  31. margin-top: 15px;
  32. display: block;
  33. `;
  34. const StyledOverlay = styled(Overlay)`
  35. position: fixed;
  36. width: 100%;
  37. height: 100%;
  38. z-index: 50;
  39. `;
  40.  
  41. const ResultContainer = styled.div`
  42. display: flex;
  43. flex-direction: column;
  44. flex: 1;
  45. height: 633px;
  46.  
  47. ${ScrollThumb} {
  48. right: 9px;
  49. top: 13px;
  50. };
  51. `;
  52.  
  53. const Heading = styled(SearchResultsHeading)`
  54. padding: 0 15px;
  55. `;
  56.  
  57. const DataContainer = styled.div`
  58. background: ${({ theme }) => theme.colors.lightgray};
  59. padding: 15px;
  60. border-radius: 15px;
  61. `;
  62.  
  63. const Tabs = styled(TabList)`
  64. margin-top: 5px;
  65. margin-bottom: 40px;
  66. .Tab:last-child {
  67. }
  68. `;
  69.  
  70. const Item = styled(SearchItem)`
  71. margin-bottom: 15px;
  72. `;
  73.  
  74. const AllResultsContainer = styled.div`
  75. display: flex;
  76. `;
  77.  
  78. const Loader = styled(InputLoader)`
  79. &::after {
  80. position: absolute;
  81. bottom: 1px;
  82. }
  83. `;
  84.  
  85. export const getSearchInputQuery = () => {
  86. return searchQuery([
  87. { 'datatypes': ['key', 'value'] },
  88. { 'results': [
  89. 'iddatatype',
  90. 'title',
  91. 'summary',
  92. 'thumb',
  93. {
  94. 'status': [
  95. 'title',
  96. 'uik',
  97. 'color'
  98. ]
  99. }
  100. ] }
  101. ]);
  102. };
  103.  
  104. export const debounceSearchDelay = 1000;
  105.  
  106.  
  107. /**
  108. *
  109. * Search Input[SearchInput](http://styleguide.yoomap.tech/#!/SearchInput)
  110. *
  111. * @exports: __ getSearchInputQuery() __
  112. */
  113. class SearchOverlay extends React.Component {
  114. static displayName = 'SearchOverlay';
  115.  
  116. static propTypes = {
  117. /** Client React */
  118. client: PropTypes.object,
  119. /** Number of [ui/molecules/Tab](#!/Tab) selected */
  120. activeTabCount: PropTypes.number,
  121. /** Min characters for search */
  122. minCaracters: PropTypes.number
  123. };
  124.  
  125. static contextType = SearchOverlayContext;
  126.  
  127. state = {
  128. search: '',
  129. searchdata: '',
  130. index: 0,
  131. isLoading: false,
  132. error: false,
  133. isEmpty: false
  134. };
  135.  
  136. static defaultProps = {
  137. activeTabCount: 3,
  138. minCaracters: 3
  139. };
  140.  
  141. getData = debounce((search) => {
  142. const { client, minCaracters } = this.props;
  143.  
  144.  
  145. if (search.length >= minCaracters) {
  146. this.setState({ isLoading: true });
  147.  
  148. client.query({
  149. query: getSearchInputQuery(),
  150. variables: { searchString: search }
  151. })
  152. .then(res => {
  153. this.setState({ isLoading: false, error: false });
  154. this.setSearchData(res);
  155. });
  156. } else {
  157. this.setState({ error: true });
  158. }
  159. }, debounceSearchDelay);
  160.  
  161. handleTabClick = obj => {
  162. const { searchdata } = this.state;
  163. // Get index of the clicked obj in newResults array
  164. const index = searchdata.findIndex(result => result.datatype === obj.id);
  165.  
  166. this.setState({ index });
  167. };
  168.  
  169. handleClear = () => {
  170. this.setState({ search: '', searchdata: [], isEmpty: false });
  171. }
  172.  
  173. setQuery = (e) => {
  174. const { minCaracters } = this.props;
  175. const search = e.currentTarget.value;
  176. const doSearch = search.length >= minCaracters;
  177.  
  178. const state = doSearch
  179. ? { search }
  180. : { search, searchdata: [] };
  181.  
  182. this.setState(state);
  183.  
  184. if (!search) {
  185. this.setState({ error: false, isEmpty: true });
  186. } else {
  187. this.getData(search);
  188. }
  189. }
  190.  
  191. getMinMax = () => {
  192. let min;
  193. let max;
  194.  
  195. const { index, searchdata } = this.state;
  196. const { activeTabCount } = this.props;
  197.  
  198. max = index + (activeTabCount - 1);
  199. if (max > this.state.searchdata.length - 1) {
  200. max = searchdata.length - 1;
  201. }
  202.  
  203. min = min < 0 ? 0 : max - 2;
  204.  
  205. return { min, max };
  206. }
  207.  
  208. renderTabList = (min, max) => {
  209. const { searchdata } = this.state;
  210.  
  211. const tabs = searchdata.map((result, i) => {
  212. return {
  213. id: result.datatype,
  214. text: result.datatype,
  215. active: i >= min && i <= max,
  216. count: result.count
  217. };
  218. });
  219.  
  220. return <Tabs tabs={tabs} onTabClick={this.handleTabClick} />;
  221. };
  222.  
  223. resultsCount = () => {
  224. const { searchdata } = this.state;
  225.  
  226. if (searchdata.length < 1) {
  227. return null;
  228. }
  229. return searchdata.reduce((count, result) => result.items.length + count, 0);
  230. };
  231.  
  232. // TODO: Temporary results JSON
  233. renderResultsItems = (min, max) => {
  234. const { searchdata } = this.state;
  235. return (
  236. <AllResultsContainer>
  237. {searchdata.slice(min, max + 1).map(
  238. (result, index) =>
  239. (
  240. <ResultContainer key={index}>
  241. <Heading count={result.count}>
  242. {result.datatype}
  243. </Heading>
  244. <CustomScrollbar>
  245. <DataContainer>
  246. {result.items.map((item, index) => (
  247. <Item
  248. onClick={() => this.openModal(item.iddatatype)}
  249. key={index}
  250. title={item.title}
  251. image={item.thumb}
  252. description='Lorem ipsum its a test'
  253. status={item.status.title}
  254. statusColor={item.status.color}
  255. />
  256. ))}
  257. </DataContainer>
  258. </CustomScrollbar>
  259. </ResultContainer>
  260. )
  261. )}
  262. </AllResultsContainer>
  263. );
  264. };
  265.  
  266. setSearchData = ({ data }) => {
  267. if (!(data && data.search && data.search.datatypes)) {
  268. return null;
  269. }
  270.  
  271. const { datatypes, results } = data.search;
  272.  
  273. const res = results.reduce(function(obj, result) {
  274. const key = result.iddatatype;
  275. if (!obj[key]) {
  276. obj[key] = [];
  277. }
  278.  
  279. obj[key].push(result);
  280. return obj;
  281. }, {});
  282.  
  283. const searchdata = datatypes.map(({ key, value }, index) => ({
  284. datatype: value,
  285. count: res[key] ? res[key].length : 0,
  286. items: res[key]
  287. }));
  288.  
  289. this.setState({ searchdata });
  290. };
  291.  
  292. renderError = (contextTypes) => {
  293. return (
  294. <ErrorText>
  295. {contextTypes.lang.errors.minCaracter}
  296. </ErrorText>
  297. );
  298. }
  299.  
  300. renderTotalResults = (contextTypes) => {
  301. const { error, searchdata } = this.state;
  302.  
  303. return (
  304. <TotalResults>
  305. {error && this.renderError(contextTypes)}
  306. {searchdata.length > 0 &&
  307. this.resultsCount() + ' ' + contextTypes.lang.common.results
  308. }
  309. </TotalResults>
  310. );
  311. }
  312.  
  313. renderSearchResults = () => {
  314. const { min, max } = this.getMinMax();
  315. const { searchdata } = this.state;
  316.  
  317. return (
  318. <React.Fragment>
  319. <LangContext.Consumer>
  320. {this.renderTotalResults}
  321. </LangContext.Consumer>
  322. {searchdata.length > 0 &&
  323. <React.Fragment>
  324. {this.renderTabList(min, max)}
  325. {this.renderResultsItems(min, max)}
  326. </React.Fragment>
  327. }
  328. </React.Fragment>
  329. );
  330. }
  331.  
  332. openModal(id) {
  333. openModal({
  334. id,
  335. title: 'nop',
  336. render: () => <View id={id} />
  337. });
  338. }
  339.  
  340. renderOverlay = ({ hideOverlay }) => {
  341. const { isEmpty, isLoading, search, error } = this.state;
  342. return (
  343. <StyledOverlay onClose={hideOverlay}>
  344. <ContainerSearch>
  345. <SearchInput
  346. error={error || error}
  347. isEmpty={isEmpty || isEmpty}
  348. value={search}
  349. onClear={this.handleClear}
  350. onChange={this.setQuery}
  351. />
  352. {isLoading && <Loader error={error || error} />}
  353. </ContainerSearch>
  354. {this.renderSearchResults()}
  355. </StyledOverlay>
  356. );
  357. };
  358.  
  359. render() {
  360. return (
  361. <SearchOverlayContext.Consumer>
  362. {this.renderOverlay}
  363. </SearchOverlayContext.Consumer>
  364. );
  365. }
  366. }
  367.  
  368. export default withApollo(SearchOverlay);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement