From bd0b88608141125addb50710998170c966ca8604 Mon Sep 17 00:00:00 2001 From: Abhishek Thory <76877003+abhithory@users.noreply.github.com> Date: Tue, 24 Oct 2023 19:54:25 +0530 Subject: [PATCH] 1259/add compact view in opportunities (#2182) * icons added * recoil family state added for checking compact view in each card * recoil state added for toggle button. Wether compact view show or not * menu item modifed for right side content * compact view toggle added in dropdown options * dropdown width increased because compact view text was overflowing * compact view added in boardcard * new animation added for in and out * compact view enabled state added * old state deleted * sizes added in toggle component * removed extra added code form navigation * toggle size added in menuitem toggle * MenuItemToggle added instead of MenuItemNavigate * Compact view improved --- .../companies/components/CompanyBoardCard.tsx | 117 ++++++++++++++---- front/src/modules/ui/display/icon/index.ts | 1 + .../modules/ui/input/components/Toggle.tsx | 48 ++++--- .../board/components/BoardOptionsDropdown.tsx | 1 + .../BoardOptionsDropdownContent.tsx | 16 +++ .../board/states/isCardInCompactViewState.ts | 6 + .../board/states/isCompactViewEnabledState.ts | 6 + .../menu-item/components/MenuItemToggle.tsx | 19 ++- .../components/AnimatedEaseInOut.tsx | 45 +++++++ 9 files changed, 216 insertions(+), 43 deletions(-) create mode 100644 front/src/modules/ui/layout/board/states/isCardInCompactViewState.ts create mode 100644 front/src/modules/ui/layout/board/states/isCompactViewEnabledState.ts create mode 100644 front/src/modules/ui/utilities/animation/components/AnimatedEaseInOut.tsx diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx index f5f721375..6686279a9 100644 --- a/front/src/modules/companies/components/CompanyBoardCard.tsx +++ b/front/src/modules/companies/components/CompanyBoardCard.tsx @@ -6,11 +6,15 @@ import { FieldContext } from '@/ui/data/field/contexts/FieldContext'; import { InlineCell } from '@/ui/data/inline-cell/components/InlineCell'; import { InlineCellHotkeyScope } from '@/ui/data/inline-cell/types/InlineCellHotkeyScope'; import { EntityChipVariant } from '@/ui/display/chip/components/EntityChip'; +import { IconEye } from '@/ui/display/icon/index'; import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox'; import { BoardCardIdContext } from '@/ui/layout/board/contexts/BoardCardIdContext'; import { useBoardContext } from '@/ui/layout/board/hooks/useBoardContext'; import { useCurrentCardSelected } from '@/ui/layout/board/hooks/useCurrentCardSelected'; +import { isCardInCompactViewState } from '@/ui/layout/board/states/isCardInCompactViewState'; +import { isCompactViewEnabledState } from '@/ui/layout/board/states/isCompactViewEnabledState'; import { visibleBoardCardFieldsScopedSelector } from '@/ui/layout/board/states/selectors/visibleBoardCardFieldsScopedSelector'; +import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql'; import { getLogoUrlFromDomainName } from '~/utils'; @@ -38,12 +42,21 @@ const StyledBoardCard = styled.div<{ selected: boolean }>` cursor: pointer; .checkbox-container { + transition: all ease-in-out 160ms; opacity: ${({ selected }) => (selected ? 1 : 0)}; } &:hover .checkbox-container { opacity: 1; } + + .compact-icon-container { + transition: all ease-in-out 160ms; + opacity: 0; + } + &:hover .compact-icon-container { + opacity: 1; + } `; const StyledBoardCardWrapper = styled.div` @@ -51,16 +64,21 @@ const StyledBoardCardWrapper = styled.div` width: 100%; `; -const StyledBoardCardHeader = styled.div` +const StyledBoardCardHeader = styled.div<{ + showCompactView: boolean; +}>` align-items: center; display: flex; flex-direction: row; font-weight: ${({ theme }) => theme.font.weight.medium}; height: 24px; - padding-bottom: ${({ theme }) => theme.spacing(1)}; + padding-bottom: ${({ theme, showCompactView }) => + theme.spacing(showCompactView ? 0 : 1)}; padding-left: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(2)}; padding-top: ${({ theme }) => theme.spacing(2)}; + transition: padding ease-in-out 160ms; + img { height: ${({ theme }) => theme.icon.size.md}px; margin-right: ${({ theme }) => theme.spacing(2)}; @@ -99,6 +117,27 @@ const StyledFieldContainer = styled.div` width: 100%; `; +const StyledCompactIconContainer = styled.div` + align-items: center; + display: flex; + justify-content: center; +`; + +const StyledIconEye = styled(IconEye)` + color: ${({ theme }) => theme.font.color.tertiary}; + height: 24px; + padding-bottom: ${({ theme }) => theme.spacing(0.2)}; + padding-left: ${({ theme }) => theme.spacing(0.5)}; + padding-right: ${({ theme }) => theme.spacing(0.5)}; + + padding-top: ${({ theme }) => theme.spacing(0.2)}; + + &:hover { + background-color: ${({ theme }) => theme.background.transparent.medium}; + border-radius: ${({ theme }) => theme.border.radius.sm}; + } +`; + export const CompanyBoardCard = () => { const { BoardRecoilScopeContext } = useBoardContext(); @@ -109,6 +148,15 @@ export const CompanyBoardCard = () => { const [companyProgress] = useRecoilState( companyProgressesFamilyState(boardCardId ?? ''), ); + + const [isCompactViewEnabled] = useRecoilState(isCompactViewEnabledState); + + const [isCardInCompactView, setIsCardInCompactView] = useRecoilState( + isCardInCompactViewState(boardCardId ?? ''), + ); + + const showCompactView = isCompactViewEnabled && isCardInCompactView; + const { pipelineProgress, company } = companyProgress ?? {}; const visibleBoardCardFields = useRecoilScopedValue( @@ -135,19 +183,34 @@ export const CompanyBoardCard = () => { ); + const OnMouseLeaveBoard = () => { + setIsCardInCompactView(true); + }; + return ( setCurrentCardSelected(!currentCardSelected)} > - + + {showCompactView && ( + + { + e.stopPropagation(); + setIsCardInCompactView(false); + }} + /> + + )} { - {visibleBoardCardFields.map((viewField) => ( - - - - - - ))} + + {visibleBoardCardFields.map((viewField) => ( + + + + + + ))} + diff --git a/front/src/modules/ui/display/icon/index.ts b/front/src/modules/ui/display/icon/index.ts index ea4d21684..e50dd47f4 100644 --- a/front/src/modules/ui/display/icon/index.ts +++ b/front/src/modules/ui/display/icon/index.ts @@ -11,6 +11,7 @@ export { IconArrowRight, IconArrowUp, IconArrowUpRight, + IconBaselineDensitySmall, IconBell, IconBox, IconBrandGithub, diff --git a/front/src/modules/ui/input/components/Toggle.tsx b/front/src/modules/ui/input/components/Toggle.tsx index 9eedf0788..61345232e 100644 --- a/front/src/modules/ui/input/components/Toggle.tsx +++ b/front/src/modules/ui/input/components/Toggle.tsx @@ -1,10 +1,13 @@ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import styled from '@emotion/styled'; import { motion } from 'framer-motion'; +export type ToggleSize = 'small' | 'medium'; + type ContainerProps = { isOn: boolean; color?: string; + toggleSize: ToggleSize; }; const StyledContainer = styled.div` @@ -14,32 +17,40 @@ const StyledContainer = styled.div` border-radius: 10px; cursor: pointer; display: flex; - height: 20px; + height: ${({ toggleSize }) => (toggleSize === 'small' ? 16 : 20)}px; transition: background-color 0.3s ease; - width: 32px; + width: ${({ toggleSize }) => (toggleSize === 'small' ? 24 : 32)}px; `; -const StyledCircle = styled(motion.div)` +const StyledCircle = styled(motion.div)<{ + toggleSize: ToggleSize; +}>` background-color: ${({ theme }) => theme.background.primary}; border-radius: 50%; - height: 16px; - width: 16px; + height: ${({ toggleSize }) => (toggleSize === 'small' ? 12 : 16)}px; + width: ${({ toggleSize }) => (toggleSize === 'small' ? 12 : 16)}px; `; -const circleVariants = { - on: { x: 14 }, - off: { x: 2 }, -}; - export type ToggleProps = { value?: boolean; onChange?: (value: boolean) => void; color?: string; + toggleSize?: ToggleSize; }; -export const Toggle = ({ value, onChange, color }: ToggleProps) => { +export const Toggle = ({ + value, + onChange, + color, + toggleSize = 'medium', +}: ToggleProps) => { const [isOn, setIsOn] = useState(value ?? false); + const circleVariants = { + on: { x: toggleSize === 'small' ? 10 : 14 }, + off: { x: 2 }, + }; + const handleChange = () => { setIsOn(!isOn); @@ -56,8 +67,17 @@ export const Toggle = ({ value, onChange, color }: ToggleProps) => { }, [value]); return ( - - + + ); }; diff --git a/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx b/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx index cba4bad8c..e1aa2dc91 100644 --- a/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx +++ b/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx @@ -35,6 +35,7 @@ export const BoardOptionsDropdown = ({ } dropdownHotkeyScope={customHotkeyScope} onClickOutside={resetViewEditMode} + dropdownMenuWidth={170} /> ); diff --git a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx index 5bf25e65e..bef92c89b 100644 --- a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx +++ b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx @@ -15,6 +15,7 @@ import { currentViewScopedSelector } from '@/ui/data/view-bar/states/selectors/c import { viewsByIdScopedSelector } from '@/ui/data/view-bar/states/selectors/viewsByIdScopedSelector'; import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState'; import { + IconBaselineDensitySmall, IconChevronLeft, IconLayoutKanban, IconPlus, @@ -28,6 +29,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate'; +import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; import { ThemeColor } from '@/ui/theme/constants/colors'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; @@ -37,6 +39,7 @@ import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilSco import { useBoardCardFields } from '../hooks/useBoardCardFields'; import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState'; import { boardColumnsState } from '../states/boardColumnsState'; +import { isCompactViewEnabledState } from '../states/isCompactViewEnabledState'; import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState'; import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector'; import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector'; @@ -72,6 +75,9 @@ export const BoardOptionsDropdownContent = ({ >(); const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState); + const [isCompactViewEnabled, setIsCompactViewEnabled] = useRecoilState( + isCompactViewEnabledState, + ); const hiddenBoardCardFields = useRecoilScopedValue( hiddenBoardCardFieldsScopedSelector, @@ -194,6 +200,16 @@ export const BoardOptionsDropdownContent = ({ text="Stages" /> + + + + )} {currentMenu === 'stages' && ( diff --git a/front/src/modules/ui/layout/board/states/isCardInCompactViewState.ts b/front/src/modules/ui/layout/board/states/isCardInCompactViewState.ts new file mode 100644 index 000000000..58542815a --- /dev/null +++ b/front/src/modules/ui/layout/board/states/isCardInCompactViewState.ts @@ -0,0 +1,6 @@ +import { atomFamily } from 'recoil'; + +export const isCardInCompactViewState = atomFamily({ + key: 'isCardInCompactViewState', + default: true, +}); diff --git a/front/src/modules/ui/layout/board/states/isCompactViewEnabledState.ts b/front/src/modules/ui/layout/board/states/isCompactViewEnabledState.ts new file mode 100644 index 000000000..7fb7793c9 --- /dev/null +++ b/front/src/modules/ui/layout/board/states/isCompactViewEnabledState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const isCompactViewEnabledState = atom({ + key: 'isCompactViewEnabledState', + default: false, +}); diff --git a/front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx b/front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx index cdb9323c3..70ed9dfb6 100644 --- a/front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx +++ b/front/src/modules/ui/navigation/menu-item/components/MenuItemToggle.tsx @@ -1,15 +1,19 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent'; -import { Toggle } from '@/ui/input/components/Toggle'; +import { Toggle, ToggleSize } from '@/ui/input/components/Toggle'; import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent'; -import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase'; +import { + StyledMenuItemBase, + StyledMenuItemRightContent, +} from '../internals/components/StyledMenuItemBase'; type MenuItemToggleProps = { LeftIcon?: IconComponent; toggled: boolean; text: string; - className: string; + className?: string; onToggleChange?: (toggled: boolean) => void; + toggleSize?: ToggleSize; }; export const MenuItemToggle = ({ @@ -18,6 +22,7 @@ export const MenuItemToggle = ({ toggled, className, onToggleChange, + toggleSize, }: MenuItemToggleProps) => { const handleOnClick = () => { onToggleChange?.(!toggled); @@ -26,7 +31,13 @@ export const MenuItemToggle = ({ return ( - + + + ); }; diff --git a/front/src/modules/ui/utilities/animation/components/AnimatedEaseInOut.tsx b/front/src/modules/ui/utilities/animation/components/AnimatedEaseInOut.tsx new file mode 100644 index 000000000..e712a0816 --- /dev/null +++ b/front/src/modules/ui/utilities/animation/components/AnimatedEaseInOut.tsx @@ -0,0 +1,45 @@ +import { useTheme } from '@emotion/react'; +import { AnimatePresence, motion } from 'framer-motion'; + +import { AnimationDuration } from '@/ui/theme/constants/animation'; + +type AnimatedEaseInOutProps = { + isOpen: boolean; + children: React.ReactNode; + duration?: AnimationDuration; + marginBottom?: string; + marginTop?: string; +}; + +export const AnimatedEaseInOut = ({ + children, + isOpen, + marginBottom, + marginTop, + duration = 'normal', +}: AnimatedEaseInOutProps) => { + const theme = useTheme(); + + return ( + + {isOpen && ( + + {children} + + )} + + ); +};