index.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import React, { Component, ReactEventHandler } from "react";
  2. import {
  3. fetchIfNeeded as fetch,
  4. receiveRecipes as receive,
  5. selectRecipe as select,
  6. } from "containers/Recipes/List/actions";
  7. import { connect } from "react-redux";
  8. import { Grid, Row, Cell } from "@material/react-layout-grid";
  9. import Button from "components/Button";
  10. import Card from 'components/Card';
  11. import RecipeCard from 'components/RecipeCard';
  12. import Recipe, { DBRecipe } from 'types/recipes';
  13. import Navbar from 'components/Navbar';
  14. import { Link, RouteComponentProps } from 'react-router-dom';
  15. import Input from 'components/Input';
  16. import { throttle } from 'throttle-debounce';
  17. import './styles.scss';
  18. interface RecipeListProps extends RouteComponentProps {
  19. data: DBRecipe[],
  20. isFetching: boolean,
  21. selectedRecipe: any | undefined,
  22. fetchRecipes: (query: string) => undefined,
  23. receiveRecipes: (recipes: DBRecipe[]) => undefined,
  24. selectRecipe: (recipe?: DBRecipe) => undefined,
  25. };
  26. class RecipeList extends Component<RecipeListProps, {phoneDisplay: boolean, search: string}> {
  27. constructor(props: RecipeListProps) {
  28. super(props);
  29. this.state = {
  30. phoneDisplay: window.innerWidth < 840,
  31. search: ''
  32. }
  33. }
  34. componentDidMount() {
  35. const { fetchRecipes } = this.props;
  36. fetchRecipes('');
  37. }
  38. autocompleteSearch = throttle(500, (query: string) => {
  39. this.props.fetchRecipes(query)
  40. })
  41. changeQuery = (query: string) => {
  42. this.setState({
  43. search: query
  44. }, () => {
  45. this.autocompleteSearch(this.state.search)
  46. })
  47. }
  48. componentDidUpdate(prevProps: any) {
  49. const { data, receiveRecipes, isFetching } = this.props;
  50. if (isFetching === false && data.length !== prevProps.data.length) {
  51. receiveRecipes(data);
  52. }
  53. }
  54. cleanRecipeSelection() {
  55. this.props.selectRecipe();
  56. }
  57. handleRecipeSelection(recipe: DBRecipe) {
  58. return (event: React.MouseEvent) => {
  59. if (this.state.phoneDisplay) {
  60. this.props.history.push('/recipes/view/' + recipe._id)
  61. this.cleanRecipeSelection();
  62. } else {
  63. this.props.selectRecipe(recipe)
  64. }
  65. };
  66. }
  67. handleEditRecipe = (id = '') => (event: React.MouseEvent) => {
  68. this.props.history.push('/recipes/edit/' + id)
  69. this.cleanRecipeSelection();
  70. }
  71. handler = (event: React.MouseEvent) => {
  72. console.log('click', event);
  73. }
  74. render() {
  75. const {data, selectedRecipe, selectRecipe} = this.props;
  76. const actions = (id = '') => [{
  77. label: 'edit',
  78. handler: this.handleEditRecipe(id),
  79. }, {
  80. label: 'shopping',
  81. handler: this.handler
  82. }];
  83. const navbarActions = [{
  84. label: 'create recipe',
  85. onClick: () => {}
  86. }];
  87. const icons = (id = '') => [{
  88. icon: 'open_in_new',
  89. handler: () => this.props.history.push('/recipes/view/' + id)
  90. }]
  91. return(
  92. <div className='cbk-recipes-list'>
  93. <Navbar
  94. title="Recipes"
  95. >
  96. <Input
  97. value={this.state.search}
  98. label='search'
  99. onChange={(e) => this.changeQuery(e.currentTarget.value)}
  100. button={{
  101. icon: 'clear',
  102. onClick: () => this.changeQuery('')
  103. }}
  104. className='cbk-recipes-list__search'
  105. />
  106. <Link to='/recipes/create'>
  107. <Button unelevated>
  108. Create Recipe
  109. </Button>
  110. </Link>
  111. </Navbar>
  112. <Grid>
  113. <Row>
  114. <Cell columns={6} phoneColumns={4} tabletColumns={8}>
  115. {
  116. data.map((recipe, i) => {
  117. return (
  118. <Card
  119. key={i}
  120. title={recipe.name}
  121. onClick={this.handleRecipeSelection(recipe)}
  122. summary={recipe.summary}
  123. actions={actions(recipe._id)}
  124. icons={icons(recipe._id)}
  125. />
  126. )
  127. })
  128. }
  129. </Cell>
  130. <Cell columns={6} phoneColumns={4} tabletColumns={8}>
  131. { selectedRecipe !== undefined &&
  132. <RecipeCard
  133. recipe={selectedRecipe}
  134. />
  135. }
  136. </Cell>
  137. </Row>
  138. </Grid>
  139. </div>
  140. )
  141. }
  142. }
  143. const mapStateToProps = ({ recipes }: any, ownProps: any) => {
  144. return recipes.list;
  145. }
  146. const mapDispatchToProps = (dispatch: any) => {
  147. return {
  148. fetchRecipes: (query: string) => {
  149. dispatch(fetch(query))
  150. },
  151. receiveRecipes: (recipes: DBRecipe[]) => {
  152. dispatch(receive(recipes))
  153. },
  154. selectRecipe: (recipe: DBRecipe) => {
  155. dispatch(select(recipe))
  156. }
  157. }
  158. }
  159. export default connect(
  160. mapStateToProps,
  161. mapDispatchToProps
  162. )(RecipeList);