-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
361 additions
and
37 deletions.
There are no files selected for viewing
173 changes: 173 additions & 0 deletions
173
src/components/Header/components/RepositoryStep/Body.jsx
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,173 @@ | ||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | ||
import slice from '@/redux/modules/repositories'; | ||
import Highlight from "@/shared/components/Highlight"; | ||
import LoadingOverlay from "@/shared/components/LoadingOverlay"; | ||
import { ScrollBarMixin } from "@/shared/components/ScrollBar"; | ||
import { useUIProperty } from "@/shared/hooks"; | ||
import { | ||
Avatar, ListItem as ListItemOrigin, | ||
ListItemAvatar, ListSubheader, TextField, | ||
} from "@material-ui/core"; | ||
import List from "@material-ui/core/List"; | ||
import ListItemText from "@material-ui/core/ListItemText"; | ||
import debounce from 'lodash.debounce'; | ||
import SourceRepositoryIcon from "mdi-react/SourceRepositoryIcon"; | ||
import { useDispatch, useSelector } from "react-redux"; | ||
import { useDebounce } from "react-use"; | ||
import { FixedSizeList } from 'react-window'; | ||
import styled from "styled-components"; | ||
import Secondary from "./Secondary"; | ||
|
||
const Container = styled.div` | ||
min-height: 100px; | ||
display: flex; | ||
flex-direction: column; | ||
padding: 10px; | ||
box-sizing: border-box; | ||
width: 100%; | ||
`; | ||
|
||
const ListItems = styled(FixedSizeList)` | ||
height: 300px !important; | ||
${ScrollBarMixin} | ||
`; | ||
|
||
const ListItem = styled(ListItemOrigin)` | ||
cursor: pointer; | ||
transition: background 0.3s; | ||
&:hover { | ||
background: rgba(255, 255, 255, 0.1); | ||
} | ||
&:active { | ||
background: rgba(255, 255, 255, 0.2); | ||
} | ||
`; | ||
|
||
const NotData = styled(({ className }) => ( | ||
<div className={className}> | ||
<div>Repositories not found</div> | ||
</div> | ||
))` | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
`; | ||
|
||
const bySearch = (search) => (item) => { | ||
return item?.name?.includes(search); | ||
}; | ||
|
||
const Body = () => { | ||
const dispatch = useDispatch(); | ||
const inputRef = useRef(); | ||
const [search, setSearch] = useState(''); | ||
const { isFetching, items } = useSelector(slice.selectors.getState); | ||
const [bodyOpen, setBodyOpen] = useUIProperty('bodyOpen'); | ||
const [filtered, setFiltered] = useState(items); | ||
|
||
const changeSearch = useMemo( | ||
() => debounce( | ||
(value) => setSearch(value), | ||
300, | ||
), | ||
[], | ||
); | ||
|
||
const onChange = useCallback( | ||
(event) => { | ||
changeSearch(event.target.value); | ||
}, | ||
[changeSearch], | ||
); | ||
|
||
const onClick = useCallback( | ||
(item) => () => { | ||
dispatch(slice.actions.setRepository(item)); | ||
setBodyOpen(false); | ||
}, | ||
[setBodyOpen, dispatch], | ||
); | ||
|
||
const ListHeader = useMemo( | ||
() => ( | ||
<ListSubheader component="div"> | ||
Repositories: {filtered.length || 0} of {items.length || 0} | ||
</ListSubheader> | ||
), | ||
[filtered.length, items.length], | ||
); | ||
|
||
const Item = useCallback( | ||
({ index, style }) => { | ||
const item = filtered[index]; | ||
|
||
return ( | ||
<ListItem | ||
alignItems="center" | ||
key={item.name} | ||
onClick={onClick(item)} | ||
style={style} | ||
> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<SourceRepositoryIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={<Highlight search={search} text={item.name} />} | ||
secondary={<Secondary item={item} />} | ||
/> | ||
</ListItem> | ||
); | ||
}, | ||
[onClick, search, filtered], | ||
); | ||
|
||
useEffect( | ||
() => { | ||
setFiltered(search ? items.filter(bySearch(search)) : items); | ||
}, | ||
[items, search, dispatch], | ||
); | ||
|
||
useDebounce( | ||
() => { | ||
if (inputRef.current && bodyOpen) { | ||
inputRef.current.querySelector('input').focus(); | ||
} | ||
}, | ||
100, | ||
[bodyOpen], | ||
); | ||
|
||
return ( | ||
<Container> | ||
<TextField | ||
label="Repository" | ||
placeholder="Type repository name" | ||
onChange={onChange} | ||
ref={inputRef} | ||
/> | ||
<LoadingOverlay loading={isFetching}> | ||
{/*<ListContainer>*/} | ||
<List | ||
dense | ||
subheader={ListHeader} | ||
> | ||
{!filtered.length && <NotData />} | ||
<ListItems | ||
itemCount={filtered.length} | ||
itemSize={76} | ||
height={300} | ||
> | ||
{Item} | ||
</ListItems> | ||
</List> | ||
{/*</ListContainer>*/} | ||
</LoadingOverlay> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default Body; |
38 changes: 38 additions & 0 deletions
38
src/components/Header/components/RepositoryStep/Header.jsx
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,38 @@ | ||
import React from 'react'; | ||
import slice from '@/redux/modules/repositories'; | ||
import SourceRepositoryIcon from "mdi-react/SourceRepositoryIcon"; | ||
import { useSelector } from "react-redux"; | ||
import styled from "styled-components"; | ||
import Container from '../shared/HeaderContainer'; | ||
|
||
const InfoContainer = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
margin-left: 5px; | ||
`; | ||
|
||
const Title = styled.div` | ||
font-weight: bold; | ||
display: flex; | ||
flex-wrap: nowrap; | ||
white-space: nowrap; | ||
`; | ||
|
||
const Header = (props) => { | ||
const { selected } = useSelector(slice.selectors.getState); | ||
const { name } = selected || {}; | ||
|
||
return ( | ||
<Container {...props}> | ||
<SourceRepositoryIcon /> | ||
<InfoContainer> | ||
{!selected && <div>Choice a repository</div>} | ||
{selected && ( | ||
<Title>{name}</Title> | ||
)} | ||
</InfoContainer> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default Header; |
100 changes: 100 additions & 0 deletions
100
src/components/Header/components/RepositoryStep/Secondary.jsx
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,100 @@ | ||
import React from "react"; | ||
import GithubEmoji from "@/shared/components/GithubEmoje"; | ||
import capitalize from "@material-ui/core/utils/capitalize"; | ||
import AlertCircleOutlineIcon from "mdi-react/AlertCircleOutlineIcon"; | ||
import CodeTagsIcon from "mdi-react/CodeTagsIcon"; | ||
import EyeOutlineIcon from "mdi-react/EyeOutlineIcon"; | ||
import SourceForkIcon from "mdi-react/SourceForkIcon"; | ||
import StarIcon from "mdi-react/StarIcon"; | ||
import PropTypes from 'prop-types'; | ||
import styled from "styled-components"; | ||
|
||
const SecondaryContainer = styled.span` | ||
display: flex; | ||
flex-direction: column; | ||
`; | ||
|
||
const Description = styled.span` | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
`; | ||
|
||
const Properties = styled.span` | ||
display: flex; | ||
align-items: center; | ||
font-size: 0.8em; | ||
`; | ||
|
||
const Property = styled.span` | ||
display: flex; | ||
align-items: center; | ||
margin-left: 5px; | ||
padding-right: 5px; | ||
border-right: 1px solid rgba(255, 255, 255, 0.2); | ||
&:first-child { | ||
margin-left: 0; | ||
} | ||
&:last-child { | ||
border-right: 0; | ||
padding-right: 0; | ||
} | ||
color: #fff; | ||
`; | ||
|
||
const PropertyValue = styled.span` | ||
margin-left: 5px; | ||
`; | ||
|
||
const Secondary = ({ item }) => ( | ||
<SecondaryContainer> | ||
{item.description && ( | ||
<Description title={item.description}> | ||
<GithubEmoji text={item.description} /> | ||
</Description> | ||
)} | ||
<Properties> | ||
{item.language && ( | ||
<Property title="Forks"> | ||
<CodeTagsIcon size={16} /> | ||
<PropertyValue> | ||
{capitalize(item.language)} | ||
</PropertyValue> | ||
</Property> | ||
)} | ||
<Property title="Forks"> | ||
<SourceForkIcon size={16} /> | ||
<PropertyValue> | ||
{item.forks} | ||
</PropertyValue> | ||
</Property> | ||
<Property title="Stars"> | ||
<StarIcon size={16} /> | ||
<PropertyValue> | ||
{item.stargazers_count} | ||
</PropertyValue> | ||
</Property> | ||
<Property title="Watchers"> | ||
<EyeOutlineIcon size={16} /> | ||
<PropertyValue> | ||
{item.watchers} | ||
</PropertyValue> | ||
</Property> | ||
<Property title="Opened issues"> | ||
<AlertCircleOutlineIcon size={16} /> | ||
<PropertyValue> | ||
{item.open_issues} | ||
</PropertyValue> | ||
</Property> | ||
</Properties> | ||
</SecondaryContainer> | ||
); | ||
|
||
Secondary.propTypes = { | ||
item: PropTypes.shape().isRequired, | ||
}; | ||
|
||
export default Secondary; |
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
Oops, something went wrong.