Skip to content

Commit

Permalink
feat(app-board): add estimation component
Browse files Browse the repository at this point in the history
  • Loading branch information
rams23 committed Jan 20, 2021
1 parent 0c8f026 commit fdab96e
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import Box from '../Box';
import { Input } from '../TextInput/TextInput.styled';
import styled from 'styled-components';
/*
const estimationWrapperDefinition = keyframes`
0% {
top: -50px;
left: 0;
right: 0;
}
50% {
left: 0;
right: 0;
height: 40px;
width: 40px;
border-radius: 50%
}
75% {
left: 0;
top: -40px;
height: 40px;
width: 40px;
border-radius: 50%
}
100% {
left: 0;
top: -40px;
height: 32px;
width: 80px;
border-radius: 10px 20px 20px 10px;
}
`;
animation: ${estimationWrapperDefinition} linear 1s;
animation-delay: 3s;
animation-fill-mode: forwards;
*/

export const EstimationWrapper = styled(Box)`
position: absolute;
top: -50px;
left: 0;
right: 0;
`;

EstimationWrapper.displayName = 'EstimationWrapper';
/*
const estimationDefinition = keyframes`
0% {
}
5% {
right: 0;
top: 0;
bottom: 0;
height: 40px;
width: 40px;
}
50% {
right: 0;
top: 0;
bottom: 0;
height: 40px;
width: 40px;
border-radius: 50%
}
50% {
right: 0;
top: 0;
bottom: 0;
height: 40px;
width: 100%;
border-radius: 15px 0 0 15px;
}
75% {
right: 0;
top: 0;
bottom: 0;
height: 40px;
width: 100%;
border-radius: 20px;
}
100% {
right: 0;
top: 0;
bottom: 0;
height: 40px;
width: 100%;
border-radius: 20px;
}
animation: ${estimationDefinition} linear 3s;
animation-fill-mode: forwards;
`;
*/
export const ConfirmButton = styled.button`
border: none;
background: #96d5d2;
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
z-index: 1;
border-radius: 50%;
transition: all 0.3s;
cursor: pointer;
:hover {
width: 40px;
height: 40px;
right: 0;
top: 0;
}
:active,
:focus {
outline: none;
}
`;

ConfirmButton.displayName = 'ConfirmButton';

export const EstimationInput = styled(Input)`
background: #ffffff 0% 0% no-repeat padding-box;
box-shadow: 0px 3px 3px #d7d2cb80;
border-radius: 20px;
opacity: 1;
padding-left: 16px;
font-family: Montserrat;
:focus {
border: none;
}
::placeholder {
color: #d7d2cb;
opacity: 1; /* Firefox */
}
:focus + ${ConfirmButton} {
background: #096762;
}
`;

EstimationInput.displayName = 'EstimationInput';

export const EstimationInputContainer = styled.div`
border-radius: 20px;
position: relative;
overflow: hidden;
`;

EstimationInputContainer.displayName = 'EstimationInputContainer';
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { useCallback, useState } from 'react';
import { ConfirmButton, EstimationInput, EstimationInputContainer, EstimationWrapper } from './EstimationEditor.styled';

type Props = {
saveEstimation: (estimation: string) => void;
initialEstimation?: string;
};

/**
* Input that appears at the top of the card to edit time estimation
*/
const EstimationEditor: React.FC<Props> = ({ saveEstimation, initialEstimation }) => {
const [estimation, setEstimation] = useState(initialEstimation || '');

const onChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setEstimation(e.target.value);
}, []);

const onClick = useCallback(() => {
saveEstimation(estimation);
}, [estimation, saveEstimation]);

return (
<EstimationWrapper mb={2}>
<EstimationInputContainer>
<EstimationInput value={estimation} onChange={onChange} placeholder={'Write time estimation'} variant="clear" />
<ConfirmButton onClick={onClick} />
</EstimationInputContainer>
</EstimationWrapper>
);
};

EstimationEditor.displayName = 'EstimationEditor';

export default EstimationEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import EstimationEditor from './EstimationEditor';

export default EstimationEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import styled from 'styled-components';

export const EstimationCardContent = styled.div`
width: 80px;
height: 32px;
background: #096762;
border-radius: 10px 20px 20px 10px;
opacity: 1;
position: absolute;
top: 12px;
left: -4px;
line-height: 32px;
color: white;
padding-left: 16px;
z-index: 10001;
`;

EstimationCardContent.displayName = 'EstimationCardContent';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { EstimationCardContent } from './EstimationInCard.styled';

type Props = {
estimation: string;
};

/**
* Component to show estimation inside the card
*/
const EstimationInCard: React.FC<Props> = ({ estimation }) => {
return <EstimationCardContent>{estimation}</EstimationCardContent>;
};

EstimationInCard.displayName = 'EstimationInCard';

export default EstimationInCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import EstimationInCard from './EstimationInCard';

export default EstimationInCard;
4 changes: 4 additions & 0 deletions packages/game-app/src/_shared/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import useDialog from './Dialog/useDialog';
import Dialog from './Dialog';
import ErrorMessage from './ErrorMessage';
import { Input } from './TextInput/TextInput.styled';
import EstimationEditor from './EstimationEditor';
import EstimationInCard from './EstimationInCard';

export {
TextInput,
Expand All @@ -36,4 +38,6 @@ export {
Dialog,
Input,
ErrorMessage,
EstimationEditor,
EstimationInCard,
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from 'react';
import React, { useCallback, useState } from 'react';
import { useDraggable } from '@dnd-kit/core';
import { useSelector } from 'react-redux';
import { selectors } from '../../slice';
import { useDispatch, useSelector } from 'react-redux';
import { actions, selectors } from '../../slice';
import ConnectedCard from '../ConnectedCard';
import { CardWrapper } from './DraggableCard.styled';
import useDoubleClick from './useDoubleClick';
import { EstimationEditor, EstimationInCard } from '@pipeline/components';

type Props = {
id: string;
Expand All @@ -18,7 +20,18 @@ const DraggableCard: React.FC<Props> = ({ id, bigger }) => {
id,
});

const position = useSelector(selectors.getCardPosition(id));
const { position, estimation } = useSelector(selectors.getCardAdditionalInfo(id));
const [estimationOpen, setEstimationOpen] = useState(false);

const dispatch = useDispatch();

const saveEstimation = useCallback(
(estimation: string) => {
dispatch(actions.setEstimation({ estimation, cardId: id }));
setEstimationOpen(false);
},
[dispatch, id],
);

const style =
position?.x || position?.y
Expand All @@ -29,10 +42,36 @@ const DraggableCard: React.FC<Props> = ({ id, bigger }) => {
}
: {};

const handler = useCallback(() => {
setEstimationOpen(true);
}, []);

const fire = useDoubleClick(handler);

const dragOnPointer = listeners?.onPointerDown;

const onPointerDown = useCallback(
(e: React.PointerEvent) => {
fire();
dragOnPointer?.(e);
},
[fire, dragOnPointer],
);

return (
<CardWrapper style={style} ref={setNodeRef} {...listeners} {...attributes} isDragging={isDragging}>
<ConnectedCard bigger={bigger} id={id} />
</CardWrapper>
<div style={style}>
{estimationOpen && <EstimationEditor saveEstimation={saveEstimation} initialEstimation={estimation} />}
{!estimationOpen && estimation && <EstimationInCard estimation={estimation} />}
<CardWrapper
ref={setNodeRef}
{...listeners}
onPointerDown={onPointerDown}
{...attributes}
isDragging={isDragging}
>
<ConnectedCard bigger={bigger} id={id} />
</CardWrapper>
</div>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useCallback, useEffect, useRef } from 'react';

export default function useDoubleClick(handler: () => void) {
const intervalRef = useRef(0);
const counterRef = useRef(0);
const handlerRef = useRef(handler);

useEffect(() => {
handlerRef.current = handler;
}, [handler]);

const fire = useCallback(() => {
clearTimeout(intervalRef.current);
if (counterRef.current === 0) {
counterRef.current = intervalRef.current + 1;

intervalRef.current = setTimeout(() => {
counterRef.current = 0;
}, 200) as any;
} else {
counterRef.current = 0;
handlerRef.current();
}
}, []);

return fire;
}
Loading

0 comments on commit fdab96e

Please sign in to comment.