1761 objects settings add a cover image (#2096)

* add image

* overflow hidden

* add close button

* add animation to cover image

* use cookie to store user preference

* refactor to have a reusable component called AnimatedFadeOut

* corrected close button position

* modified according to comments
This commit is contained in:
bosiraphael
2023-10-18 13:02:44 +02:00
committed by GitHub
parent a1a2309140
commit f95c9d3df8
6 changed files with 168 additions and 53 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -0,0 +1,58 @@
import { useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconX } from '@/ui/display/icon';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { AnimatedFadeOut } from '@/ui/utilities/animation/components/AnimatedFadeOut';
import { cookieStorage } from '~/utils/cookie-storage';
import CoverImage from '../assets/build-your-business-logic.jpg';
const StyledCoverImageContainer = styled.div`
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
overflow: hidden;
position: relative;
width: 100%;
`;
const StyledCoverImage = styled.img`
height: 100%;
object-fit: cover;
width: 100%;
`;
const StyledLighIconButton = styled(LightIconButton)`
position: absolute;
right: ${({ theme }) => theme.spacing(1)};
top: ${({ theme }) => theme.spacing(1)};
`;
export const SettingsObjectCoverImage = () => {
const theme = useTheme();
const [cookieState, setCookieState] = useState(
cookieStorage.getItem('settings-object-cover-image'),
);
return (
<AnimatedFadeOut
isOpen={cookieState !== 'closed'}
marginBottom={theme.spacing(8)}
>
<StyledCoverImageContainer>
<StyledCoverImage src={CoverImage} alt="Build your business logic" />
<StyledLighIconButton
Icon={IconX}
accent="tertiary"
size="small"
onClick={() => {
cookieStorage.setItem('settings-object-cover-image', 'closed');
setCookieState('closed');
}}
/>
</StyledCoverImageContainer>
</AnimatedFadeOut>
);
};

View File

@ -5,3 +5,5 @@ export const animation = {
normal: 0.3,
},
};
export type AnimationDuration = 'instant' | 'fast' | 'normal';

View File

@ -1,19 +1,26 @@
import { useTheme } from '@emotion/react';
import { motion } from 'framer-motion';
import { AnimationDuration } from '@/ui/theme/constants/animation';
type AnimatedEaseInProps = Omit<
React.ComponentProps<typeof motion.div>,
'initial' | 'animated' | 'transition'
> & {
duration?: number;
duration?: AnimationDuration;
};
export const AnimatedEaseIn = ({
children,
duration = 0.3,
duration = 'normal',
}: AnimatedEaseInProps) => {
const theme = useTheme();
const initial = { opacity: 0 };
const animate = { opacity: 1 };
const transition = { ease: 'linear', duration };
const transition = {
ease: 'linear',
duration: theme.animation.duration[duration],
};
return (
<motion.div initial={initial} animate={animate} transition={transition}>

View File

@ -0,0 +1,42 @@
import { useTheme } from '@emotion/react';
import { AnimatePresence, motion } from 'framer-motion';
import { AnimationDuration } from '@/ui/theme/constants/animation';
type AnimatedFadeOutProps = {
isOpen: boolean;
children: React.ReactNode;
duration?: AnimationDuration;
marginBottom?: string;
marginTop?: string;
};
export const AnimatedFadeOut = ({
isOpen,
children,
duration = 'normal',
marginBottom,
marginTop,
}: AnimatedFadeOutProps) => {
const theme = useTheme();
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{
opacity: 1,
marginBottom: marginBottom ?? 0,
marginTop: marginTop ?? 0,
}}
exit={{ opacity: 0, height: 0, marginBottom: 0, marginTop: 0 }}
transition={{
duration: theme.animation.duration[duration],
ease: 'easeOut',
}}
>
{children}
</motion.div>
)}
</AnimatePresence>
);
};

View File

@ -8,6 +8,7 @@ import {
activeObjectItems,
disabledObjectItems,
} from '@/settings/data-model/constants/mockObjects';
import { SettingsObjectCoverImage } from '@/settings/data-model/objects/SettingsObjectCoverImage';
import {
IconChevronRight,
IconDotsVertical,
@ -76,52 +77,28 @@ export const SettingsObjects = () => {
}}
/>
</SettingsHeaderContainer>
<Section>
<H2Title title="Existing objects" />
<Table>
<StyledTableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Type</TableHeader>
<TableHeader align="right">Fields</TableHeader>
<TableHeader align="right">Instances</TableHeader>
<TableHeader></TableHeader>
</StyledTableRow>
<TableSection title="Active">
{activeObjectItems.map((objectItem) => (
<StyledTableRow
key={objectItem.name}
onClick={() =>
navigate(
`/settings/objects/${objectItem.name.toLowerCase()}`,
)
}
>
<StyledNameTableCell>
<objectItem.Icon size={theme.icon.size.md} />
{objectItem.name}
</StyledNameTableCell>
<TableCell>
{objectItem.type === 'standard' ? (
<StyledTag color="blue" text="Standard" />
) : (
<StyledTag color="orange" text="Custom" />
)}
</TableCell>
<TableCell align="right">{objectItem.fields}</TableCell>
<TableCell align="right">{objectItem.instances}</TableCell>
<StyledIconTableCell>
<StyledIconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconTableCell>
</StyledTableRow>
))}
</TableSection>
{!!disabledObjectItems.length && (
<TableSection title="Disabled">
{disabledObjectItems.map((objectItem) => (
<StyledTableRow key={objectItem.name}>
<div>
<SettingsObjectCoverImage />
<Section>
<H2Title title="Existing objects" />
<Table>
<StyledTableRow>
<TableHeader>Name</TableHeader>
<TableHeader>Type</TableHeader>
<TableHeader align="right">Fields</TableHeader>
<TableHeader align="right">Instances</TableHeader>
<TableHeader></TableHeader>
</StyledTableRow>
<TableSection title="Active">
{activeObjectItems.map((objectItem) => (
<StyledTableRow
key={objectItem.name}
onClick={() =>
navigate(
`/settings/objects/${objectItem.name.toLowerCase()}`,
)
}
>
<StyledNameTableCell>
<objectItem.Icon size={theme.icon.size.md} />
{objectItem.name}
@ -136,7 +113,7 @@ export const SettingsObjects = () => {
<TableCell align="right">{objectItem.fields}</TableCell>
<TableCell align="right">{objectItem.instances}</TableCell>
<StyledIconTableCell>
<StyledIconDotsVertical
<StyledIconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
@ -144,9 +121,38 @@ export const SettingsObjects = () => {
</StyledTableRow>
))}
</TableSection>
)}
</Table>
</Section>
{!!disabledObjectItems.length && (
<TableSection title="Disabled">
{disabledObjectItems.map((objectItem) => (
<StyledTableRow key={objectItem.name}>
<StyledNameTableCell>
<objectItem.Icon size={theme.icon.size.md} />
{objectItem.name}
</StyledNameTableCell>
<TableCell>
{objectItem.type === 'standard' ? (
<StyledTag color="blue" text="Standard" />
) : (
<StyledTag color="orange" text="Custom" />
)}
</TableCell>
<TableCell align="right">{objectItem.fields}</TableCell>
<TableCell align="right">
{objectItem.instances}
</TableCell>
<StyledIconTableCell>
<StyledIconDotsVertical
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconTableCell>
</StyledTableRow>
))}
</TableSection>
)}
</Table>
</Section>
</div>
</SettingsPageContainer>
</SubMenuTopBarContainer>
);