Skip to content

Instantly share code, notes, and snippets.

@Phineas
Created July 26, 2020 21:10
Show Gist options
  • Save Phineas/dd3707b297b9a997256e0ad58159ec73 to your computer and use it in GitHub Desktop.
Save Phineas/dd3707b297b9a997256e0ad58159ec73 to your computer and use it in GitHub Desktop.
import React, { useState, useEffect, useCallback } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import twemojiEMData from 'emoji-mart/data/twitter.json';
import { AtSignIcon, SmileIcon } from '../Icons';
import { HivenReduxState, HivenHouseMember } from '../../interfaces/common';
import UserIcon from '../UserIcon';
const Container = styled.div<any>`
position: absolute;
bottom: 65px;
min-height: 45px;
width: calc(100% - 30px);
align-self: center;
border-radius: 5px;
background-color: ${(props) => props.theme.layoutLighter};
transform: ${(props) =>
props.active ? 'translateY(0px)' : 'translateY(15px)'};
box-shadow: 0px 0px 48px rgba(0, 0, 0, 0.04), 0px 8px 24px rgba(0, 0, 0, 0.08);
transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
opacity: ${(props) => (props.active ? 100 : 0)};
z-index: 1;
overflow: auto;
cursor: pointer;
pointer-events: ${(props) => !props.active && 'none'};
`;
const RowWrap = styled.div<any>`
display: flex;
align-items: center;
flex-direction: row;
height: 45px;
padding-left: 15px;
padding-right: 15px;
background-color: ${(props) => props.selected && props.theme.layoutLightest};
img {
width: 25px;
height: 25px;
margin-right: 15px;
}
`;
const HelperRow = styled(RowWrap)`
color: ${(props) => props.theme.textLighter};
svg {
color: ${(props) => props.theme.textLight};
margin-right: 15px;
}
`;
const StyledUserIcon = styled(UserIcon)`
width: 25px;
height: 25px;
border-radius: 50%;
margin-right: 15px;
`;
const UserInfo = styled.span`
color: ${(props) => props.theme.textLighter};
b {
font-weight: 600;
color: ${(props) => props.theme.textLightest};
}
`;
interface ResourceAutocompleteProps {
active: boolean;
roomId: string;
resourceType: string | null;
searchParams: string | null;
callback: (completeText: string) => void;
}
interface ReduxProps {
house_members: any;
}
const EMOJI_NAMES = Object.keys(twemojiEMData.emojis);
const ResourceAutocomplete = ({
active,
roomId,
resourceType,
searchParams,
house_members,
callback,
}: ResourceAutocompleteProps & ReduxProps) => {
const [items, setItems] = useState<any[]>([]);
const [index, setIndex] = useState(0);
const autocomplete = useCallback(
(text: string) => {
callback(text);
setItems([]);
},
[callback]
);
useEffect(() => {
if (!searchParams || searchParams.length < 1) {
if (items.length) setItems([]);
return;
}
switch (resourceType) {
case 'user': {
if (!house_members.length) return;
setItems(
house_members
.filter(
(m: HivenHouseMember) =>
m.user.name
.toLowerCase()
.startsWith(searchParams.toLowerCase()) ||
m.user.username
.toLowerCase()
.startsWith(searchParams.toLowerCase())
)
.slice(0, 8)
);
break;
}
case 'emoji': {
setItems(
EMOJI_NAMES.filter((e) => e.startsWith(searchParams.toLowerCase()))
.slice(0, 8)
.map((en) => {
// @ts-ignore
return { ...twemojiEMData.emojis[en], name: en };
})
.filter((e) => e.b || e.c)
);
break;
}
default:
break;
}
setIndex(0);
}, [resourceType, roomId, house_members, searchParams, items.length]);
const handleKeydown = useCallback(
(e) => {
if (!active || !resourceType) return;
switch (e.code) {
case 'ArrowDown': {
e.preventDefault();
setIndex((current: number) =>
current >= items.length - 1 ? 0 : current + 1
);
break;
}
case 'ArrowUp': {
e.preventDefault();
setIndex((current: number) =>
current >= items.length - 1 ? 0 : current <= 0 ? 0 : current - 1
);
break;
}
case 'Enter':
case 'Tab': {
if (searchParams ? !searchParams.length || !items.length : true)
return;
e.preventDefault();
if (!items.length || !items[index]) return;
autocomplete(
resourceType === 'user'
? items[index].user.username
: items[index].name
);
break;
}
default:
break;
}
},
[items, index, searchParams, setIndex, active, autocomplete, resourceType]
);
useEffect(() => {
window.addEventListener('keydown', handleKeydown);
return () => {
window.removeEventListener('keydown', handleKeydown);
};
}, [handleKeydown]);
return (
<Container
active={
active && (searchParams ? searchParams.length && items.length : true)
}
>
{active && resourceType ? (
searchParams && searchParams.length && items.length ? (
resourceType === 'user' ? (
items.map((m: HivenHouseMember, i: number) => (
<RowWrap
onClick={() => autocomplete(items[index].user.username)}
onMouseEnter={() => setIndex(i)}
selected={i === index}
>
<StyledUserIcon {...m.user} />
<UserInfo>
<b>{m.user.name}</b> @{m.user.username}
</UserInfo>
</RowWrap>
))
) : (
items.map((m: any, i: number) => (
<RowWrap
onClick={() => autocomplete(m.name)}
onMouseEnter={() => setIndex(i)}
selected={i === index}
>
<img
alt=""
// @ts-ignore
src={`https://twemoji.maxcdn.com/2/svg/${(
m.c || m.b
).toLowerCase()}.svg`}
/>
{`:${m.name}:`}
</RowWrap>
))
)
) : resourceType === 'user' ? (
<HelperRow>
<AtSignIcon height={20} />
Start typing to search users to mention in this house
</HelperRow>
) : (
<HelperRow>
<SmileIcon height={20} />
Start typing to search emojis
</HelperRow>
)
) : null}
</Container>
);
};
const mapStateToProps = (
{ rooms, houses }: HivenReduxState,
{ roomId }: any
) => {
return {
house_members:
rooms[roomId] && rooms[roomId].house_id
? houses[rooms[roomId].house_id].members
: [],
};
};
export default connect(mapStateToProps)(ResourceAutocomplete);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment