From 0147f0b74ea73048ce39d29485ca5078541dbb66 Mon Sep 17 00:00:00 2001 From: bart Date: Tue, 4 Jan 2022 23:01:25 +0100 Subject: [PATCH 1/7] add disabled for text area --- .eslintrc.js | 2 +- src/components/EntryList/EntryList.tsx | 85 ++++++++++--------- .../EntryList/ListItem/ListItem.tsx | 10 ++- src/config/palette.ts | 1 + .../components/TextArea/TextArea.styled.ts | 14 ++- src/shared/utils/context.ts | 21 ++++- src/shared/utils/helpers.ts | 3 +- 7 files changed, 86 insertions(+), 50 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index dda1fed..6162820 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,7 +37,7 @@ module.exports = { 'explicit-module-boundary-types': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', 'jsx-a11y/no-autofocus': 'off', - 'no-console': 2, + 'no-console': 1, }, settings: { react: { diff --git a/src/components/EntryList/EntryList.tsx b/src/components/EntryList/EntryList.tsx index 957edab..2f063a0 100644 --- a/src/components/EntryList/EntryList.tsx +++ b/src/components/EntryList/EntryList.tsx @@ -1,11 +1,13 @@ import { palette, SPACING_SMALL } from 'config' -import { ChangeEvent } from 'react' +import { ChangeEvent, useState } from 'react' import { BsArrowLeftCircle, BsArrowRightCircle, BsExclamationOctagon, } from 'react-icons/bs' +import { TimeEntry } from 'services' import { Button } from 'shared/components' +import { EditEntryContext } from 'shared/utils' import { ControlsWrapper, DateField, @@ -20,6 +22,7 @@ import TimeSumup from './TimeSumup' type ChangeEv = ChangeEvent const EntryList = () => { + const [editing, setEditing] = useState(null) const { entriesFromDay, labels, @@ -32,45 +35,47 @@ const EntryList = () => { const onDateChange = (e: ChangeEv) => setTargetDate(e.target.value) return ( - -
- Selected date: - - - - - - -
- - {entriesFromDay.map(item => ( - - ))} - {entriesFromDay.length === 0 && ( - -

No time entries

- -
- )} -
-
+ + +
+ Selected date: + + + + + + +
+ + {entriesFromDay.map(item => ( + + ))} + {entriesFromDay.length === 0 && ( + +

No time entries

+ +
+ )} +
+
+
) } diff --git a/src/components/EntryList/ListItem/ListItem.tsx b/src/components/EntryList/ListItem/ListItem.tsx index d3c87d2..963c496 100644 --- a/src/components/EntryList/ListItem/ListItem.tsx +++ b/src/components/EntryList/ListItem/ListItem.tsx @@ -4,7 +4,7 @@ import { BsFillPencilFill, BsFillTrashFill } from 'react-icons/bs' import { DB, TimeEntry } from 'services' import { Button, TextArea } from 'shared/components' import { Label } from 'shared/types' -import { EntryListContext } from 'shared/utils' +import { EditEntryContext, EntryListContext } from 'shared/utils' import { EntryTimeField, Labels } from '../../index' import { ActionsWrapper, Item } from './ListItem.style' import { calculateTimeEntry, getSelectedLabels } from './ListItem.utils' @@ -24,6 +24,7 @@ const ListItem = ({ id, }: ListItemProps) => { const { setUpdateEntryList } = useContext(EntryListContext) || {} + const { setEditing, editing } = useContext(EditEntryContext) || {} const deleteEntry = () => { db.deleteTimeEntry(id) setUpdateEntryList?.(true) @@ -36,6 +37,7 @@ const ListItem = ({ color="primary" height={75} width={400} + disabled={id !== editing} /> - diff --git a/src/config/palette.ts b/src/config/palette.ts index 05a500f..ca9bb16 100644 --- a/src/config/palette.ts +++ b/src/config/palette.ts @@ -13,6 +13,7 @@ export const palette = { accent: '#607D8B', }, divider: '#BDBDBD', + disabled: '#d3d3d3', shadows: { box0: '0 3px 3px rgb(0 0 0 / 11%), 0 3px 3px rgb(0 0 0 / 5%)', box1: '0 3px 5px rgb(0 0 0 / 20%), 0 3px 5px rgb(0 0 0 / 5%)', diff --git a/src/shared/components/TextArea/TextArea.styled.ts b/src/shared/components/TextArea/TextArea.styled.ts index 4eb628f..c938428 100644 --- a/src/shared/components/TextArea/TextArea.styled.ts +++ b/src/shared/components/TextArea/TextArea.styled.ts @@ -1,4 +1,4 @@ -import { palette } from 'config' +import { palette, transition } from 'config' import { darken, lighten } from 'polished' import { Color } from 'shared/types' import { getColor } from 'shared/utils' @@ -12,10 +12,11 @@ export interface TextAreaProps { error?: string width?: Size height?: Size + disabled?: boolean } export const TextArea = styled('textarea')( - ({ resizable, color, error, width, height }) => ({ + ({ resizable, color, error, width, height, disabled = false }) => ({ width: width ? width : 'inherit', height: height ? height : 'inherit', resize: resizable ? 'both' : 'none', @@ -27,5 +28,14 @@ export const TextArea = styled('textarea')( boxShadow: palette.shadows.box1, backgroundColor: lighten(0.4, getColor(color)), caretColor: darken(0.5, getColor(color)), + transition: `all ease-in-out ${transition.time.fast}ms`, + cursor: disabled ? 'not-allowed' : 'default', + + '&: hover': { + ...(disabled && { + backgroundColor: getColor(color, undefined, disabled), + borderColor: darken(0.2, getColor(color, undefined, disabled)), + }), + }, }) ) diff --git a/src/shared/utils/context.ts b/src/shared/utils/context.ts index 1ba51f9..5a70e83 100644 --- a/src/shared/utils/context.ts +++ b/src/shared/utils/context.ts @@ -1,11 +1,24 @@ import { createContext, Dispatch, SetStateAction } from 'react' +import { TimeEntry } from 'services' -export const EntryListContext = createContext<{ +interface EntryListContextProps { updateEntryList: boolean setUpdateEntryList: Dispatch> -} | null>(null) +} +export const EntryListContext = createContext( + null +) -export const LabelsContext = createContext<{ +interface LabelsContextProps { updateLabels: boolean setUpdateLabels: Dispatch> -} | null>(null) +} +export const LabelsContext = createContext(null) + +interface EditEntryContextProps { + editing: TimeEntry['id'] | null + setEditing: Dispatch> +} +export const EditEntryContext = createContext( + null +) diff --git a/src/shared/utils/helpers.ts b/src/shared/utils/helpers.ts index b357b7f..55a7195 100644 --- a/src/shared/utils/helpers.ts +++ b/src/shared/utils/helpers.ts @@ -9,8 +9,9 @@ export const getScaledMinutes = (minutes: number) => export const daysInMonth = (month: number, year: number) => new Date(year, month, 0).getDate() -export const getColor = (color: Color, error?: string) => { +export const getColor = (color: Color, error?: string, disabled?: boolean) => { if (error) return palette.error + if (disabled) return palette.disabled switch (color) { case 'primary': { From 5fea1bd6edc798e96cdee5149f58937cba568a9c Mon Sep 17 00:00:00 2001 From: bart Date: Thu, 6 Jan 2022 00:37:04 +0100 Subject: [PATCH 2/7] add disabled to labels --- .../EntryList/ListItem/ListItem.tsx | 4 ++- src/components/Labels/AddNewLabel.tsx | 3 ++- src/components/Labels/Labels.style.ts | 27 +++++++++++++++---- src/components/Labels/Labels.tsx | 16 ++++++++--- src/shared/components/TextArea/TextArea.tsx | 1 + 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/components/EntryList/ListItem/ListItem.tsx b/src/components/EntryList/ListItem/ListItem.tsx index 963c496..5a53f7a 100644 --- a/src/components/EntryList/ListItem/ListItem.tsx +++ b/src/components/EntryList/ListItem/ListItem.tsx @@ -29,6 +29,7 @@ const ListItem = ({ db.deleteTimeEntry(id) setUpdateEntryList?.(true) } + const disabled = id !== editing return ( @@ -37,11 +38,12 @@ const ListItem = ({ color="primary" height={75} width={400} - disabled={id !== editing} + disabled={disabled} />
diff --git a/src/components/Labels/AddNewLabel.tsx b/src/components/Labels/AddNewLabel.tsx index aca23bf..900ec4b 100644 --- a/src/components/Labels/AddNewLabel.tsx +++ b/src/components/Labels/AddNewLabel.tsx @@ -25,10 +25,11 @@ export const AddNewLabel = ({ onAdd }: AddNewLabelProps) => { setLabelName(e.target.value) const onKeyPress = (e: React.KeyboardEvent) => { + if (!onAdd) return if (e.charCode !== 13) return db.addNewLabel(labelName) setInitiated(false) - onAdd && onAdd(true) + onAdd(true) setLabelName('') } diff --git a/src/components/Labels/Labels.style.ts b/src/components/Labels/Labels.style.ts index 9b6e629..af8314d 100644 --- a/src/components/Labels/Labels.style.ts +++ b/src/components/Labels/Labels.style.ts @@ -9,6 +9,7 @@ import { transition, typography, } from 'config' +import { darken } from 'polished' import { LabelProps } from 'shared/types' import styled from 'styled-components' @@ -38,8 +39,24 @@ export const StyledLabel = styled('button')(({ active }) => ({ }, })) -export const LabelWrapper = styled('div')({ - display: 'flex', - width: LABEL_WRAPPER_WIDTH, - flexWrap: 'wrap', -}) +interface LabelWrapperProps { + disabled: boolean +} +export const LabelWrapper = styled('div')( + ({ disabled }) => ({ + display: 'flex', + width: LABEL_WRAPPER_WIDTH, + flexWrap: 'wrap', + + ...(disabled && { + '& button:hover': { + backgroundColor: palette.disabled, + borderColor: darken(0.2, palette.disabled), + cursor: 'not-allowed', + }, + '& button:last-of-type': { + display: 'none', + }, + }), + }) +) diff --git a/src/components/Labels/Labels.tsx b/src/components/Labels/Labels.tsx index 099106b..376ba28 100644 --- a/src/components/Labels/Labels.tsx +++ b/src/components/Labels/Labels.tsx @@ -8,9 +8,15 @@ interface LabelsProps { labels?: Label[] onClick?: (id: ID) => void selectedLabels?: ID[] + disabled?: boolean } -const Labels = ({ labels, onClick, selectedLabels }: LabelsProps) => { +const Labels = ({ + labels, + onClick, + selectedLabels, + disabled = false, +}: LabelsProps) => { const ctx = useContext(LabelsContext) const isLabelSelected = useCallback( @@ -18,18 +24,20 @@ const Labels = ({ labels, onClick, selectedLabels }: LabelsProps) => { [selectedLabels] ) + console.log('disabled', disabled) + return ( - + {labels?.map(({ id, name }) => ( onClick && onClick(id)} + onClick={() => !disabled && onClick?.(id)} active={isLabelSelected(id)} /> ))} - + ) } diff --git a/src/shared/components/TextArea/TextArea.tsx b/src/shared/components/TextArea/TextArea.tsx index 1a05914..a5c8515 100644 --- a/src/shared/components/TextArea/TextArea.tsx +++ b/src/shared/components/TextArea/TextArea.tsx @@ -11,6 +11,7 @@ export interface TextAreaProps extends BaseTextAreaProps { } const TextArea = (props: TextAreaProps) => { return ( + // TODO: add tooltip here based on a props <> {props.error} From 7a62321449607f942ef23e4cac846ada55102598 Mon Sep 17 00:00:00 2001 From: bart Date: Thu, 6 Jan 2022 00:53:46 +0100 Subject: [PATCH 3/7] add disabled to the inputs --- .../EntryList/ListItem/ListItem.tsx | 6 +++- .../EntryTimeField/EntryTimeField.style.ts | 28 +++++++++++++++++-- .../EntryTimeField/EntryTimeField.tsx | 7 +++-- src/shared/components/Input/index.ts | 1 + src/shared/components/index.ts | 1 + 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/components/EntryList/ListItem/ListItem.tsx b/src/components/EntryList/ListItem/ListItem.tsx index 5a53f7a..8f2211a 100644 --- a/src/components/EntryList/ListItem/ListItem.tsx +++ b/src/components/EntryList/ListItem/ListItem.tsx @@ -45,7 +45,11 @@ const ListItem = ({ selectedLabels={getSelectedLabels(labels, selectedLabels)} disabled={disabled} /> - +
Scaled time: {calculateTimeEntry(entryTimeHours, entryTimeMinutes)}
diff --git a/src/components/EntryTimeField/EntryTimeField.style.ts b/src/components/EntryTimeField/EntryTimeField.style.ts index 97b7d7f..63516d4 100644 --- a/src/components/EntryTimeField/EntryTimeField.style.ts +++ b/src/components/EntryTimeField/EntryTimeField.style.ts @@ -1,8 +1,17 @@ +import { + palette, + SPACING_REGULAR, + TIME_FIELD_MARGIN, + TIME_FIELD_WIDTH, +} from 'config' +import { darken } from 'polished' +import { + Input as BaseInput, + InputProps as BaseInputProps, +} from 'shared/components' import styled from 'styled-components' -import { SPACING_REGULAR, TIME_FIELD_MARGIN, TIME_FIELD_WIDTH } from 'config' -import { Input } from 'shared/components' -export const TimeField = styled(Input)({ +export const TimeField = styled(BaseInput)({ width: TIME_FIELD_WIDTH, margin: `0 ${TIME_FIELD_MARGIN}px`, }) @@ -12,3 +21,16 @@ export const Form = styled('form')({ justifyContent: 'space-between', gap: SPACING_REGULAR, }) + +interface InputProps extends BaseInputProps { + disabled?: boolean +} +export const Input = styled(BaseInput)(({ disabled }) => ({ + ...(disabled && { + cursor: 'not-allowed', + '&:hover': { + backgroundColor: palette.disabled, + borderColor: darken(0.2, palette.disabled), + }, + }), +})) diff --git a/src/components/EntryTimeField/EntryTimeField.tsx b/src/components/EntryTimeField/EntryTimeField.tsx index 46940f9..0ea44dc 100644 --- a/src/components/EntryTimeField/EntryTimeField.tsx +++ b/src/components/EntryTimeField/EntryTimeField.tsx @@ -1,14 +1,14 @@ -import { Input } from 'shared/components' import { setTimeFunc } from 'shared/types' import { HOURS_LIMIT, MINUTES_LIMIT } from '../../config' import { useEntryTimeField } from './EntryTime.utils' -import { Form } from './EntryTimeField.style' +import { Form, Input } from './EntryTimeField.style' interface EntryTimeFieldProps { hours: number minutes: number setHours?: setTimeFunc setMinutes?: setTimeFunc + disabled?: boolean } const EntryTimeField = ({ @@ -16,6 +16,7 @@ const EntryTimeField = ({ minutes, setHours, setMinutes, + disabled = false, }: EntryTimeFieldProps) => { const { error, handleChange } = useEntryTimeField() @@ -31,6 +32,7 @@ const EntryTimeField = ({ max={HOURS_LIMIT} error={error.hours} width={100} + disabled={disabled} /> handleChange(e.target.value, MINUTES_LIMIT, setMinutes)} error={error.minutes} width={100} + disabled={disabled} /> ) diff --git a/src/shared/components/Input/index.ts b/src/shared/components/Input/index.ts index aa97178..823777c 100644 --- a/src/shared/components/Input/index.ts +++ b/src/shared/components/Input/index.ts @@ -1 +1,2 @@ export { default } from './Input' +export type { InputProps } from './Input' diff --git a/src/shared/components/index.ts b/src/shared/components/index.ts index 6c3bece..f43999b 100644 --- a/src/shared/components/index.ts +++ b/src/shared/components/index.ts @@ -5,4 +5,5 @@ export { default as Modal } from './Modal' export { default as Collapse } from './Collapse' export { default as Button } from './Button' export { default as Input } from './Input' +export type { InputProps } from './Input' export { default as TextArea } from './TextArea' From 20cba21864925058dfe39e1168caa302d9fca001 Mon Sep 17 00:00:00 2001 From: bart Date: Thu, 6 Jan 2022 01:30:58 +0100 Subject: [PATCH 4/7] add done cion inside the button in order to save changes --- src/components/EntryList/ListItem/ListItem.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/EntryList/ListItem/ListItem.tsx b/src/components/EntryList/ListItem/ListItem.tsx index 8f2211a..07ed5fd 100644 --- a/src/components/EntryList/ListItem/ListItem.tsx +++ b/src/components/EntryList/ListItem/ListItem.tsx @@ -1,6 +1,6 @@ import { SPACING_SMALL } from 'config' import { useContext } from 'react' -import { BsFillPencilFill, BsFillTrashFill } from 'react-icons/bs' +import { BsCheckLg, BsFillPencilFill, BsFillTrashFill } from 'react-icons/bs' import { DB, TimeEntry } from 'services' import { Button, TextArea } from 'shared/components' import { Label } from 'shared/types' @@ -29,6 +29,10 @@ const ListItem = ({ db.deleteTimeEntry(id) setUpdateEntryList?.(true) } + const toggleEditing = () => { + setEditing?.(id) + if (editing === id) setEditing?.(null) + } const disabled = id !== editing return ( @@ -65,9 +69,9 @@ const ListItem = ({ From 7f8d6d39d4edeb18e524f17fc561517272a1beb2 Mon Sep 17 00:00:00 2001 From: bart Date: Sat, 8 Jan 2022 01:26:48 +0100 Subject: [PATCH 5/7] add tooltip component --- src/components/Labels/Labels.tsx | 28 ++++++++------ src/config/transition.ts | 1 + .../components/Tooltip/Tooltip.style.ts | 34 +++++++++++++++++ src/shared/components/Tooltip/Tooltip.tsx | 38 +++++++++++++++++++ src/shared/components/Tooltip/index.ts | 1 + src/shared/types/props.ts | 5 +++ 6 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 src/shared/components/Tooltip/Tooltip.style.ts create mode 100644 src/shared/components/Tooltip/Tooltip.tsx create mode 100644 src/shared/components/Tooltip/index.ts diff --git a/src/components/Labels/Labels.tsx b/src/components/Labels/Labels.tsx index 376ba28..7931387 100644 --- a/src/components/Labels/Labels.tsx +++ b/src/components/Labels/Labels.tsx @@ -1,4 +1,5 @@ import { memo, useCallback, useContext } from 'react' +import Tooltip from 'shared/components/Tooltip' import { ID, Label } from 'shared/types' import { LabelsContext } from 'shared/utils' import { AddNewLabel, LabelItem } from './AddNewLabel' @@ -27,18 +28,21 @@ const Labels = ({ console.log('disabled', disabled) return ( - - {labels?.map(({ id, name }) => ( - !disabled && onClick?.(id)} - active={isLabelSelected(id)} - /> - ))} - - + + + {labels?.map(({ id, name }) => ( + !disabled && onClick?.(id)} + active={isLabelSelected(id)} + /> + ))} + + + + ) } diff --git a/src/config/transition.ts b/src/config/transition.ts index ee268d5..a18cdf6 100644 --- a/src/config/transition.ts +++ b/src/config/transition.ts @@ -3,5 +3,6 @@ export const transition = { instant: 100, fast: 200, medium: 400, + long: 1000, }, } diff --git a/src/shared/components/Tooltip/Tooltip.style.ts b/src/shared/components/Tooltip/Tooltip.style.ts new file mode 100644 index 0000000..96927d5 --- /dev/null +++ b/src/shared/components/Tooltip/Tooltip.style.ts @@ -0,0 +1,34 @@ +import { + DEFAULT_BORDER_RADIUS, + palette, + SPACING_MID, + SPACING_SMALL, + transition, +} from 'config' +import { Position } from 'shared/types' +import styled from 'styled-components' + +export const TooltipWrapper = styled('div')({ + '&:hover span': { + opacity: 1, + }, +}) + +interface BaseTooltipProps { + position: Position +} +export const BaseTooltip = styled('span')(({ position }) => ({ + position: 'fixed', + left: 0, + top: 0, + opacity: 0, + transform: `translate(${position.x}px, ${position.y}px)`, + transition: `opacity ease-in-out ${transition.time.long}ms, transform linear ${transition.time.instant}ms`, + backgroundColor: palette.text.secondary, + color: palette.text.text, + padding: `${SPACING_SMALL}px ${SPACING_MID}px ${SPACING_MID}px`, + borderRadius: DEFAULT_BORDER_RADIUS, + margin: SPACING_SMALL, + clipPath: + 'polygon(0% 0%, 100% 0%, 100% 70%, 56% 70%, 50% 100%, 40% 70%, 0 70%)', +})) diff --git a/src/shared/components/Tooltip/Tooltip.tsx b/src/shared/components/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..eb0888d --- /dev/null +++ b/src/shared/components/Tooltip/Tooltip.tsx @@ -0,0 +1,38 @@ +import { MouseEvent, PropsWithChildren, useRef, useState } from 'react' +import { useThrottle } from 'rooks' +import { BaseTooltip, TooltipWrapper } from './Tooltip.style' + +const MOUSE_OFFSET = 15 + +type MouseMoveEvent = MouseEvent + +interface TooltipProps { + text?: string +} +const Tooltip = ({ children, text }: PropsWithChildren) => { + const wrapperRef = useRef(null) + const tooltipRef = useRef(null) + const [position, setPosition] = useState({ x: 0, y: 0 }) + const [throttledSetPosition] = useThrottle(setPosition, 35) + + const onMouseMoveHandler = (e: MouseMoveEvent) => { + throttledSetPosition({ + x: + e.clientX - + (tooltipRef.current?.offsetWidth || 0) / 2 - + MOUSE_OFFSET / 2, + y: e.clientY - (tooltipRef.current?.offsetHeight || 0) - MOUSE_OFFSET, + }) + } + + return ( + + + {text} + + {children} + + ) +} + +export default Tooltip diff --git a/src/shared/components/Tooltip/index.ts b/src/shared/components/Tooltip/index.ts new file mode 100644 index 0000000..2f430db --- /dev/null +++ b/src/shared/components/Tooltip/index.ts @@ -0,0 +1 @@ +export { default } from './Tooltip' diff --git a/src/shared/types/props.ts b/src/shared/types/props.ts index 5da802c..9719884 100644 --- a/src/shared/types/props.ts +++ b/src/shared/types/props.ts @@ -14,3 +14,8 @@ export interface TimeEntryErrors { timeEntryDescription?: string timeEntry?: string } + +export interface Position { + x: T + y: T +} From e03d5ce3b653c6cf4f00f481cd2d25bfd08f239c Mon Sep 17 00:00:00 2001 From: bart Date: Sat, 8 Jan 2022 01:43:57 +0100 Subject: [PATCH 6/7] add tooltip to disabled elements --- .../AddEntryButton/AddEntryButton.tsx | 2 +- .../EntryList/ListItem/ListItem.tsx | 45 +++++++++++-------- src/components/Labels/Labels.tsx | 29 +++++------- src/config/content.ts | 1 + src/config/index.ts | 1 + src/shared/components/Button/Button.style.ts | 30 ------------- src/shared/components/Button/Button.tsx | 38 +++------------- .../components/Tooltip/Tooltip.style.ts | 2 + src/shared/components/Tooltip/Tooltip.tsx | 16 +++++-- src/shared/components/index.ts | 1 + 10 files changed, 63 insertions(+), 102 deletions(-) create mode 100644 src/config/content.ts diff --git a/src/components/AddEntryButton/AddEntryButton.tsx b/src/components/AddEntryButton/AddEntryButton.tsx index d877cc3..be5259b 100644 --- a/src/components/AddEntryButton/AddEntryButton.tsx +++ b/src/components/AddEntryButton/AddEntryButton.tsx @@ -14,7 +14,7 @@ const AddEntryButton = ({ onClick, errors }: AddEntryButtonProps) => {