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}
+
+ )}
+
+ );
+};