refactor: move @/ui/display/icon to twenty-ui (#4820)
Split from https://github.com/twentyhq/twenty/pull/4518 Part of https://github.com/twentyhq/twenty/issues/4766
This commit is contained in:
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-address-book" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M20 6v12a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2z" />
|
||||
<path d="M10 16h6" />
|
||||
<path d="M13 11m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" />
|
||||
<path d="M4 8h3" />
|
||||
<path d="M4 12h3" />
|
||||
<path d="M4 16h3" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 536 B |
7
packages/twenty-ui/src/display/icon/assets/gmail.svg
Normal file
7
packages/twenty-ui/src/display/icon/assets/gmail.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="12" fill="none" viewBox="0 0 14 12">
|
||||
<path fill="#4285F4" d="M.955 11.252h2.227v-5.41L0 3.456v6.841c0 .528.428.954.955.954Z"/>
|
||||
<path fill="#34A853" d="M10.818 11.252h2.227a.954.954 0 0 0 .955-.955V3.456l-3.182 2.386"/>
|
||||
<path fill="#FBBC04" d="M10.818 1.706v4.136L14 3.456V2.183c0-1.18-1.348-1.853-2.291-1.145"/>
|
||||
<path fill="#EA4335" d="M3.182 5.842V1.706L7 4.57l3.818-2.864v4.136L7 8.706"/>
|
||||
<path fill="#C5221F" d="M0 2.183v1.273l3.182 2.386V1.706l-.891-.668C1.346.33 0 1.003 0 2.183Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 563 B |
@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 76 76">
|
||||
<path fill="#fff" d="M58 18H18v40h40z"/>
|
||||
<path fill="#4285f4" d="M26.2 49.03c-1.495-1.01-2.53-2.485-3.095-4.435l3.47-1.43c.315 1.2.865 2.13 1.65 2.79.78.66 1.73.985 2.84.985 1.135 0 2.11-.345 2.925-1.035s1.225-1.57 1.225-2.635c0-1.09-.43-1.98-1.29-2.67s-1.94-1.035-3.23-1.035H28.69V36.13h1.8c1.11 0 2.045-.3 2.805-.9s1.14-1.42 1.14-2.465c0-.93-.34-1.67-1.02-2.225s-1.54-.835-2.585-.835c-1.02 0-1.83.27-2.43.815a4.784 4.784 0 0 0-1.31 2.005l-3.435-1.43c.455-1.29 1.29-2.43 2.515-3.415s2.79-1.48 4.69-1.48c1.405 0 2.67.27 3.79.815s2 1.3 2.635 2.26c.635.965.95 2.045.95 3.245 0 1.225-.295 2.26-.885 3.11s-1.315 1.5-2.175 1.955v.205a6.605 6.605 0 0 1 2.79 2.175c.725.975 1.09 2.14 1.09 3.5s-.345 2.575-1.035 3.64-1.645 1.905-2.855 2.515c-1.215.61-2.58.92-4.095.92-1.755.005-3.375-.5-4.87-1.51zm21.315-17.22-3.81 2.755-1.905-2.89 6.835-4.93h2.62V50h-3.74z"/>
|
||||
<path fill="#34a853" d="M58 58H18v18h40z"/>
|
||||
<path fill="#4285f4" d="M58 0H6C2.685 0 0 2.685 0 6v52h18V18h40z"/>
|
||||
<path fill="#188038" d="M0 58v12c0 3.315 2.685 6 6 6h12V58z"/>
|
||||
<path fill="#fbbc04" d="M76 18H58v40h18z"/>
|
||||
<path fill="#1967d2" d="M76 18V6c0-3.315-2.685-6-6-6H58v18z"/>
|
||||
<path fill="#ea4335" d="m58 76 18-18H58z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
13
packages/twenty-ui/src/display/icon/assets/google.svg
Normal file
13
packages/twenty-ui/src/display/icon/assets/google.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 14 14">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#4285F4" d="M7 5.727v2.711h3.767a3.228 3.228 0 0 1-1.406 2.107l2.272 1.762c1.323-1.221 2.087-3.016 2.087-5.148 0-.496-.045-.973-.127-1.432H7Z"/>
|
||||
<path fill="#34A853" d="m3.077 8.332-.512.393L.75 10.137C1.903 12.422 4.263 14 7 14c1.89 0 3.474-.624 4.633-1.693L9.36 10.544c-.624.42-1.42.675-2.361.675-1.82 0-3.366-1.228-3.92-2.883l-.003-.004Z"/>
|
||||
<path fill="#FBBC05" d="M.75 3.863A6.914 6.914 0 0 0 0 7c0 1.133.274 2.196.75 3.137 0 .007 2.33-1.807 2.33-1.807A4.195 4.195 0 0 1 2.857 7c0-.465.083-.91.223-1.33L.75 3.863Z"/>
|
||||
<path fill="#EA4335" d="M7 2.787c1.03 0 1.947.357 2.68 1.044l2.004-2.005C10.468.694 8.89 0 7 0A6.988 6.988 0 0 0 .75 3.863L3.08 5.67C3.634 4.015 5.18 2.787 7 2.787Z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h13.72v14H0z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 956 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M6.06216 1.53416C6.38434 0.663593 7.61566 0.663591 7.93784 1.53416L9.00134 4.40789C9.10263 4.68158 9.31842 4.89737 9.59211 4.99866L12.4658 6.06216C13.3364 6.38434 13.3364 7.61566 12.4658 7.93784L9.59211 9.00134C9.31842 9.10263 9.10263 9.31842 9.00134 9.59211L7.93784 12.4658C7.61566 13.3364 6.38434 13.3364 6.06216 12.4658L4.99866 9.59211C4.89737 9.31842 4.68158 9.10263 4.40789 9.00134L1.53416 7.93784C0.663593 7.61566 0.663591 6.38434 1.53416 6.06216L4.40789 4.99866C4.68158 4.89737 4.89737 4.68158 4.99866 4.40789L6.06216 1.53416Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 668 B |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.4673 3.06709C10.9938 1.64431 13.0062 1.6443 13.5327 3.06709L15.2708 7.76367C15.4364 8.21097 15.789 8.56364 16.2363 8.72917L20.9329 10.4673C22.3557 10.9938 22.3557 13.0062 20.9329 13.5327L16.2363 15.2708C15.789 15.4364 15.4364 15.789 15.2708 16.2363L13.5327 20.9329C13.0062 22.3557 10.9938 22.3557 10.4673 20.9329L8.72917 16.2363C8.56364 15.789 8.21097 15.4364 7.76367 15.2708L3.06709 13.5327C1.64431 13.0062 1.6443 10.9938 3.06709 10.4673L7.76367 8.72917C8.21097 8.56364 8.56364 8.21097 8.72917 7.76367L10.4673 3.06709Z" stroke="currentColor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 663 B |
@ -0,0 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconAddressBookRaw from '@ui/display/icon/assets/address-book.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconAddressBookProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconAddressBook = (props: IconAddressBookProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
return <IconAddressBookRaw height={size} width={size} strokeWidth={stroke} />;
|
||||
};
|
||||
13
packages/twenty-ui/src/display/icon/components/IconGmail.tsx
Normal file
13
packages/twenty-ui/src/display/icon/components/IconGmail.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconGmailRaw from '@ui/display/icon/assets/gmail.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconGmailProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGmail = (props: IconGmailProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGmailRaw height={size} width={size} />;
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconGoogleRaw from '@ui/display/icon/assets/google.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconGoogleProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGoogle = (props: IconGoogleProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGoogleRaw height={size} width={size} />;
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconGoogleCalendarRaw from '@ui/display/icon/assets/google-calendar.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconGoogleCalendarProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGoogleCalendar = (props: IconGoogleCalendarProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGoogleCalendarRaw height={size} width={size} />;
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconTwentyStarRaw from '@ui/display/icon/assets/twenty-star.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconTwentyStarProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconTwentyStar = (props: IconTwentyStarProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
return <IconTwentyStarRaw height={size} width={size} strokeWidth={stroke} />;
|
||||
};
|
||||
@ -0,0 +1,16 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconTwentyStarFilledRaw from '@ui/display/icon/assets/twenty-star-filled.svg?react';
|
||||
import { IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconTwentyStarFilledProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconTwentyStarFilled = (props: IconTwentyStarFilledProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
return (
|
||||
<IconTwentyStarFilledRaw height={size} width={size} strokeWidth={stroke} />
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,44 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import * as recoil from 'recoil';
|
||||
|
||||
import {
|
||||
Icon123,
|
||||
IconBuildingSkyscraper,
|
||||
IconUser,
|
||||
} from '@ui/display/icon/components/TablerIcons';
|
||||
import { useIcons } from '@ui/display/icon/hooks/useIcons';
|
||||
|
||||
describe('useIcons', () => {
|
||||
const mockedStateIcons = {
|
||||
IconUser,
|
||||
Icon123,
|
||||
IconBuildingSkyscraper,
|
||||
};
|
||||
jest
|
||||
.spyOn(recoil, 'useRecoilValue')
|
||||
.mockImplementationOnce(() => mockedStateIcons);
|
||||
const { result } = renderHook(() => useIcons(), {
|
||||
wrapper: recoil.RecoilRoot,
|
||||
});
|
||||
|
||||
it('returns default icon when no icon key is provided', () => {
|
||||
expect(result.current.getIcon()).toEqual(Icon123);
|
||||
});
|
||||
|
||||
it('returns the specified icon if the icon key exists in the iconsState', () => {
|
||||
expect(result.current.getIcon('Icon123')).toEqual(Icon123);
|
||||
expect(result.current.getIcon('IconUser')).toEqual(IconUser);
|
||||
expect(result.current.getIcon('IconBuildingSkyscraper')).toEqual(
|
||||
IconBuildingSkyscraper,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns default icon if the specified icon key does not exist in the iconsState', () => {
|
||||
expect(result.current.getIcon('nonExistentKey')).toEqual(Icon123);
|
||||
});
|
||||
|
||||
it('returns all icons in getIcons', () => {
|
||||
expect(result.current.getIcons()).toEqual(mockedStateIcons);
|
||||
expect(Object.keys(result.current.getIcons())).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
20
packages/twenty-ui/src/display/icon/hooks/useIcons.ts
Normal file
20
packages/twenty-ui/src/display/icon/hooks/useIcons.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { Icon123 } from '@ui/display/icon/components/TablerIcons';
|
||||
import { iconsState } from '@ui/display/icon/states/iconsState';
|
||||
|
||||
export const useIcons = () => {
|
||||
const icons = useRecoilValue(iconsState);
|
||||
const defaultIcon = Icon123;
|
||||
|
||||
const getIcons = () => {
|
||||
return icons;
|
||||
};
|
||||
|
||||
const getIcon = (iconKey?: string | null) => {
|
||||
if (!iconKey) return defaultIcon;
|
||||
return icons[iconKey] ?? defaultIcon;
|
||||
};
|
||||
|
||||
return { getIcons, getIcon };
|
||||
};
|
||||
@ -0,0 +1,20 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { iconsState } from '@ui/display/icon/states/iconsState';
|
||||
|
||||
type IconsProviderProps = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
export const IconsProvider = ({ children }: IconsProviderProps) => {
|
||||
const setIcons = useSetRecoilState(iconsState);
|
||||
|
||||
useEffect(() => {
|
||||
import('./internal/AllIcons').then((lazyLoadedIcons) => {
|
||||
setIcons(lazyLoadedIcons.ALL_ICONS);
|
||||
});
|
||||
}, [setIcons]);
|
||||
|
||||
return children;
|
||||
};
|
||||
8396
packages/twenty-ui/src/display/icon/providers/internal/AllIcons.ts
Normal file
8396
packages/twenty-ui/src/display/icon/providers/internal/AllIcons.ts
Normal file
File diff suppressed because it is too large
Load Diff
7
packages/twenty-ui/src/display/icon/states/iconsState.ts
Normal file
7
packages/twenty-ui/src/display/icon/states/iconsState.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { IconComponent } from '@ui/display/icon/types/IconComponent';
|
||||
import { createState } from '@ui/utilities/state/utils/createState';
|
||||
|
||||
export const iconsState = createState<Record<string, IconComponent>>({
|
||||
key: 'iconsState',
|
||||
defaultValue: {},
|
||||
});
|
||||
10
packages/twenty-ui/src/display/icon/types/IconComponent.ts
Normal file
10
packages/twenty-ui/src/display/icon/types/IconComponent.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { FunctionComponent } from 'react';
|
||||
|
||||
export type IconComponentProps = {
|
||||
className?: string;
|
||||
color?: string;
|
||||
size?: number;
|
||||
stroke?: number;
|
||||
};
|
||||
|
||||
export type IconComponent = FunctionComponent<IconComponentProps>;
|
||||
@ -1 +1,11 @@
|
||||
export * from './icon/components/IconAddressBook';
|
||||
export * from './icon/components/IconGmail';
|
||||
export * from './icon/components/IconGoogle';
|
||||
export * from './icon/components/IconGoogleCalendar';
|
||||
export * from './icon/components/IconTwentyStar';
|
||||
export * from './icon/components/IconTwentyStarFilled';
|
||||
export * from './icon/components/TablerIcons';
|
||||
export * from './icon/hooks/useIcons';
|
||||
export * from './icon/providers/IconsProvider';
|
||||
export * from './icon/states/iconsState';
|
||||
export * from './icon/types/IconComponent';
|
||||
|
||||
Reference in New Issue
Block a user