Migrate to a monorepo structure (#2909)
This commit is contained in:
BIN
packages/twenty-front/src/modules/ui/theme/assets/dark-noise.jpg
Normal file
BIN
packages/twenty-front/src/modules/ui/theme/assets/dark-noise.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,23 @@
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
|
||||
import { darkTheme, lightTheme } from '@/ui/theme/constants/theme';
|
||||
|
||||
import { useColorScheme } from '../hooks/useColorScheme';
|
||||
import { useSystemColorScheme } from '../hooks/useSystemColorScheme';
|
||||
|
||||
type AppThemeProviderProps = {
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
export const AppThemeProvider = ({ children }: AppThemeProviderProps) => {
|
||||
const systemColorScheme = useSystemColorScheme();
|
||||
|
||||
const { colorScheme } = useColorScheme();
|
||||
|
||||
const computedColorScheme =
|
||||
colorScheme === 'System' ? systemColorScheme : colorScheme;
|
||||
|
||||
const theme = computedColorScheme === 'Dark' ? darkTheme : lightTheme;
|
||||
|
||||
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
||||
};
|
||||
@ -0,0 +1,19 @@
|
||||
import { color } from './colors';
|
||||
|
||||
export const accentLight = {
|
||||
primary: color.blueAccent25,
|
||||
secondary: color.blueAccent20,
|
||||
tertiary: color.blueAccent15,
|
||||
quaternary: color.blueAccent10,
|
||||
accent3570: color.blueAccent35,
|
||||
accent4060: color.blueAccent40,
|
||||
};
|
||||
|
||||
export const accentDark = {
|
||||
primary: color.blueAccent75,
|
||||
secondary: color.blueAccent80,
|
||||
tertiary: color.blueAccent85,
|
||||
quaternary: color.blueAccent90,
|
||||
accent3570: color.blueAccent70,
|
||||
accent4060: color.blueAccent60,
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
export const animation = {
|
||||
duration: {
|
||||
instant: 0.075,
|
||||
fast: 0.15,
|
||||
normal: 0.3,
|
||||
},
|
||||
};
|
||||
|
||||
export type AnimationDuration = 'instant' | 'fast' | 'normal';
|
||||
@ -0,0 +1,47 @@
|
||||
/* eslint-disable twenty/no-hardcoded-colors */
|
||||
import DarkNoise from '../assets/dark-noise.jpg';
|
||||
import LightNoise from '../assets/light-noise.png';
|
||||
|
||||
import { color, grayScale, rgba } from './colors';
|
||||
|
||||
export const backgroundLight = {
|
||||
noisy: `url(${LightNoise.toString()});`,
|
||||
primary: grayScale.gray0,
|
||||
secondary: grayScale.gray10,
|
||||
tertiary: grayScale.gray15,
|
||||
quaternary: grayScale.gray20,
|
||||
danger: color.red10,
|
||||
transparent: {
|
||||
primary: rgba(grayScale.gray0, 0.8),
|
||||
secondary: rgba(grayScale.gray10, 0.8),
|
||||
strong: rgba(grayScale.gray100, 0.16),
|
||||
medium: rgba(grayScale.gray100, 0.08),
|
||||
light: rgba(grayScale.gray100, 0.04),
|
||||
lighter: rgba(grayScale.gray100, 0.02),
|
||||
danger: rgba(color.red, 0.08),
|
||||
},
|
||||
overlay: rgba(grayScale.gray80, 0.8),
|
||||
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
|
||||
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
|
||||
};
|
||||
|
||||
export const backgroundDark = {
|
||||
noisy: `url(${DarkNoise.toString()});`,
|
||||
primary: grayScale.gray85,
|
||||
secondary: grayScale.gray80,
|
||||
tertiary: grayScale.gray75,
|
||||
quaternary: grayScale.gray70,
|
||||
danger: color.red80,
|
||||
transparent: {
|
||||
primary: rgba(grayScale.gray85, 0.8),
|
||||
secondary: rgba(grayScale.gray80, 0.8),
|
||||
strong: rgba(grayScale.gray0, 0.14),
|
||||
medium: rgba(grayScale.gray0, 0.1),
|
||||
light: rgba(grayScale.gray0, 0.06),
|
||||
lighter: rgba(grayScale.gray0, 0.03),
|
||||
danger: rgba(color.red, 0.08),
|
||||
},
|
||||
overlay: rgba(grayScale.gray80, 0.8),
|
||||
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
|
||||
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${grayScale.gray60} 100%)`,
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
export const blur = {
|
||||
light: 'blur(6px)',
|
||||
strong: 'blur(20px)',
|
||||
};
|
||||
@ -0,0 +1,36 @@
|
||||
import { color, grayScale } from './colors';
|
||||
|
||||
const common = {
|
||||
radius: {
|
||||
xs: '2px',
|
||||
sm: '4px',
|
||||
md: '8px',
|
||||
xl: '20px',
|
||||
pill: '999px',
|
||||
rounded: '100%',
|
||||
},
|
||||
};
|
||||
|
||||
export const borderLight = {
|
||||
color: {
|
||||
strong: grayScale.gray25,
|
||||
medium: grayScale.gray20,
|
||||
light: grayScale.gray15,
|
||||
secondaryInverted: grayScale.gray50,
|
||||
inverted: grayScale.gray60,
|
||||
danger: color.red20,
|
||||
},
|
||||
...common,
|
||||
};
|
||||
|
||||
export const borderDark = {
|
||||
color: {
|
||||
strong: grayScale.gray55,
|
||||
medium: grayScale.gray65,
|
||||
light: grayScale.gray70,
|
||||
secondaryInverted: grayScale.gray35,
|
||||
inverted: grayScale.gray20,
|
||||
danger: color.red70,
|
||||
},
|
||||
...common,
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { grayScale, rgba } from './colors';
|
||||
|
||||
export const boxShadowLight = {
|
||||
extraLight: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.04)}`,
|
||||
light: `0px 2px 4px 0px ${rgba(
|
||||
grayScale.gray100,
|
||||
0.04,
|
||||
)}, 0px 0px 4px 0px ${rgba(grayScale.gray100, 0.08)}`,
|
||||
strong: `2px 4px 16px 0px ${rgba(
|
||||
grayScale.gray100,
|
||||
0.12,
|
||||
)}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.04)}`,
|
||||
underline: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.32)}`,
|
||||
};
|
||||
|
||||
export const boxShadowDark = {
|
||||
extraLight: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.04)}`,
|
||||
light: `0px 2px 4px 0px ${rgba(
|
||||
grayScale.gray100,
|
||||
0.04,
|
||||
)}, 0px 0px 4px 0px ${rgba(grayScale.gray100, 0.08)}`,
|
||||
strong: `2px 4px 16px 0px ${rgba(
|
||||
grayScale.gray100,
|
||||
0.16,
|
||||
)}, 0px 2px 4px 0px ${rgba(grayScale.gray100, 0.08)}`,
|
||||
underline: `0px 1px 0px 0px ${rgba(grayScale.gray100, 0.32)}`,
|
||||
};
|
||||
155
packages/twenty-front/src/modules/ui/theme/constants/colors.ts
Normal file
155
packages/twenty-front/src/modules/ui/theme/constants/colors.ts
Normal file
@ -0,0 +1,155 @@
|
||||
/* eslint-disable twenty/no-hardcoded-colors */
|
||||
import hexRgb from 'hex-rgb';
|
||||
|
||||
export const grayScale = {
|
||||
gray100: '#000000',
|
||||
gray90: '#141414',
|
||||
gray85: '#171717',
|
||||
gray80: '#1b1b1b',
|
||||
gray75: '#1d1d1d',
|
||||
gray70: '#222222',
|
||||
gray65: '#292929',
|
||||
gray60: '#333333',
|
||||
gray55: '#4c4c4c',
|
||||
gray50: '#666666',
|
||||
gray45: '#818181',
|
||||
gray40: '#999999',
|
||||
gray35: '#b3b3b3',
|
||||
gray30: '#cccccc',
|
||||
gray25: '#d6d6d6',
|
||||
gray20: '#ebebeb',
|
||||
gray15: '#f1f1f1',
|
||||
gray10: '#fcfcfc',
|
||||
gray0: '#ffffff',
|
||||
};
|
||||
|
||||
export const mainColors = {
|
||||
green: '#55ef3c',
|
||||
turquoise: '#15de8f',
|
||||
sky: '#00e0ff',
|
||||
blue: '#1961ed',
|
||||
purple: '#915ffd',
|
||||
pink: '#f54bd0',
|
||||
red: '#f83e3e',
|
||||
orange: '#ff7222',
|
||||
yellow: '#ffd338',
|
||||
gray: grayScale.gray30,
|
||||
};
|
||||
|
||||
export type ThemeColor = keyof typeof mainColors;
|
||||
|
||||
export const mainColorNames = Object.keys(mainColors) as ThemeColor[];
|
||||
|
||||
export const secondaryColors = {
|
||||
yellow80: '#2e2a1a',
|
||||
yellow70: '#453d1e',
|
||||
yellow60: '#746224',
|
||||
yellow50: '#b99b2e',
|
||||
yellow40: '#ffe074',
|
||||
yellow30: '#ffedaf',
|
||||
yellow20: '#fff6d7',
|
||||
yellow10: '#fffbeb',
|
||||
|
||||
green80: '#1d2d1b',
|
||||
green70: '#23421e',
|
||||
green60: '#2a5822',
|
||||
green50: '#42ae31',
|
||||
green40: '#88f477',
|
||||
green30: '#ccfac5',
|
||||
green20: '#ddfcd8',
|
||||
green10: '#eefdec',
|
||||
|
||||
turquoise80: '#172b23',
|
||||
turquoise70: '#173f2f',
|
||||
turquoise60: '#166747',
|
||||
turquoise50: '#16a26b',
|
||||
turquoise40: '#5be8b1',
|
||||
turquoise30: '#a1f2d2',
|
||||
turquoise20: '#d0f8e9',
|
||||
turquoise10: '#e8fcf4',
|
||||
|
||||
sky80: '#152b2e',
|
||||
sky70: '#123f45',
|
||||
sky60: '#0e6874',
|
||||
sky50: '#07a4b9',
|
||||
sky40: '#4de9ff',
|
||||
sky30: '#99f3ff',
|
||||
sky20: '#ccf9ff',
|
||||
sky10: '#e5fcff',
|
||||
|
||||
blue80: '#171e2c',
|
||||
blue70: '#172642',
|
||||
blue60: '#18356d',
|
||||
blue50: '#184bad',
|
||||
blue40: '#5e90f2',
|
||||
blue30: '#a3c0f8',
|
||||
blue20: '#d1dffb',
|
||||
blue10: '#e8effd',
|
||||
|
||||
purple80: '#231e2e',
|
||||
purple70: '#2f2545',
|
||||
purple60: '#483473',
|
||||
purple50: '#6c49b8',
|
||||
purple40: '#b28ffe',
|
||||
purple30: '#d3bffe',
|
||||
purple20: '#e9dfff',
|
||||
purple10: '#f4efff',
|
||||
|
||||
pink80: '#2d1c29',
|
||||
pink70: '#43213c',
|
||||
pink60: '#702c61',
|
||||
pink50: '#b23b98',
|
||||
pink40: '#f881de',
|
||||
pink30: '#fbb7ec',
|
||||
pink20: '#fddbf6',
|
||||
pink10: '#feedfa',
|
||||
|
||||
red80: '#2d1b1b',
|
||||
red70: '#441f1f',
|
||||
red60: '#712727',
|
||||
red50: '#b43232',
|
||||
red40: '#fa7878',
|
||||
red30: '#fcb2b2',
|
||||
red20: '#fed8d8',
|
||||
red10: '#feecec',
|
||||
|
||||
orange80: '#2e2018',
|
||||
orange70: '#452919',
|
||||
orange60: '#743b1b',
|
||||
orange50: '#b9571f',
|
||||
orange40: '#ff9c64',
|
||||
orange30: '#ffc7a7',
|
||||
orange20: '#ffe3d3',
|
||||
orange10: '#fff1e9',
|
||||
|
||||
gray80: grayScale.gray70,
|
||||
gray70: grayScale.gray65,
|
||||
gray60: grayScale.gray55,
|
||||
gray50: grayScale.gray40,
|
||||
gray40: grayScale.gray25,
|
||||
gray30: grayScale.gray20,
|
||||
gray20: grayScale.gray15,
|
||||
gray10: grayScale.gray10,
|
||||
blueAccent90: '#141a25',
|
||||
blueAccent85: '#151d2e',
|
||||
blueAccent80: '#152037',
|
||||
blueAccent75: '#16233f',
|
||||
blueAccent70: '#17294a',
|
||||
blueAccent60: '#18356d',
|
||||
blueAccent40: '#a3c0f8',
|
||||
blueAccent35: '#c8d9fb',
|
||||
blueAccent25: '#dae6fc',
|
||||
blueAccent20: '#e2ecfd',
|
||||
blueAccent15: '#edf2fe',
|
||||
blueAccent10: '#f5f9fd',
|
||||
};
|
||||
|
||||
export const color = {
|
||||
...mainColors,
|
||||
...secondaryColors,
|
||||
};
|
||||
|
||||
export const rgba = (hex: string, alpha: number) => {
|
||||
const rgb = hexRgb(hex, { format: 'array' }).slice(0, -1).join(',');
|
||||
return `rgba(${rgb},${alpha})`;
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import { ThemeType } from './theme';
|
||||
|
||||
export const overlayBackground = (props: { theme: ThemeType }) => css`
|
||||
backdrop-filter: blur(8px);
|
||||
background: ${props.theme.background.transparent.secondary};
|
||||
box-shadow: ${props.theme.boxShadow.strong};
|
||||
`;
|
||||
|
||||
export const textInputStyle = (props: { theme: ThemeType }) => css`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${props.theme.font.color.primary};
|
||||
font-family: ${props.theme.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${props.theme.spacing(0)} ${props.theme.spacing(2)};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${props.theme.font.color.light};
|
||||
font-family: ${props.theme.font.family};
|
||||
font-weight: ${props.theme.font.weight.medium};
|
||||
}
|
||||
`;
|
||||
|
||||
export const hoverBackground = (props: any) => css`
|
||||
transition: background 0.1s ease;
|
||||
&:hover {
|
||||
background: ${props.theme.background.transparent.light};
|
||||
}
|
||||
`;
|
||||
45
packages/twenty-front/src/modules/ui/theme/constants/font.ts
Normal file
45
packages/twenty-front/src/modules/ui/theme/constants/font.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { color, grayScale } from './colors';
|
||||
|
||||
const common = {
|
||||
size: {
|
||||
xxs: '0.625rem',
|
||||
xs: '0.85rem',
|
||||
sm: '0.92rem',
|
||||
md: '1rem',
|
||||
lg: '1.23rem',
|
||||
xl: '1.54rem',
|
||||
xxl: '1.85rem',
|
||||
},
|
||||
weight: {
|
||||
regular: 400,
|
||||
medium: 500,
|
||||
semiBold: 600,
|
||||
},
|
||||
family: 'Inter, sans-serif',
|
||||
};
|
||||
|
||||
export const fontLight = {
|
||||
color: {
|
||||
primary: grayScale.gray60,
|
||||
secondary: grayScale.gray50,
|
||||
tertiary: grayScale.gray40,
|
||||
light: grayScale.gray35,
|
||||
extraLight: grayScale.gray30,
|
||||
inverted: grayScale.gray0,
|
||||
danger: color.red,
|
||||
},
|
||||
...common,
|
||||
};
|
||||
|
||||
export const fontDark = {
|
||||
color: {
|
||||
primary: grayScale.gray20,
|
||||
secondary: grayScale.gray35,
|
||||
tertiary: grayScale.gray45,
|
||||
light: grayScale.gray50,
|
||||
extraLight: grayScale.gray55,
|
||||
inverted: grayScale.gray100,
|
||||
danger: color.red,
|
||||
},
|
||||
...common,
|
||||
};
|
||||
13
packages/twenty-front/src/modules/ui/theme/constants/icon.ts
Normal file
13
packages/twenty-front/src/modules/ui/theme/constants/icon.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const icon = {
|
||||
size: {
|
||||
sm: 14,
|
||||
md: 16,
|
||||
lg: 20,
|
||||
xl: 40,
|
||||
},
|
||||
stroke: {
|
||||
sm: 1.6,
|
||||
md: 2,
|
||||
lg: 2.5,
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
export const modal = {
|
||||
size: {
|
||||
sm: '300px',
|
||||
md: '400px',
|
||||
lg: '53%',
|
||||
},
|
||||
};
|
||||
55
packages/twenty-front/src/modules/ui/theme/constants/tag.ts
Normal file
55
packages/twenty-front/src/modules/ui/theme/constants/tag.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { color } from './colors';
|
||||
|
||||
export const tagLight: { [key: string]: { [key: string]: string } } = {
|
||||
text: {
|
||||
green: color.green60,
|
||||
turquoise: color.turquoise60,
|
||||
sky: color.sky60,
|
||||
blue: color.blue60,
|
||||
purple: color.purple60,
|
||||
pink: color.pink60,
|
||||
red: color.red60,
|
||||
orange: color.orange60,
|
||||
yellow: color.yellow60,
|
||||
gray: color.gray60,
|
||||
},
|
||||
background: {
|
||||
green: color.green20,
|
||||
turquoise: color.turquoise20,
|
||||
sky: color.sky20,
|
||||
blue: color.blue20,
|
||||
purple: color.purple20,
|
||||
pink: color.pink20,
|
||||
red: color.red20,
|
||||
orange: color.orange20,
|
||||
yellow: color.yellow20,
|
||||
gray: color.gray20,
|
||||
},
|
||||
};
|
||||
|
||||
export const tagDark = {
|
||||
text: {
|
||||
green: color.green10,
|
||||
turquoise: color.turquoise10,
|
||||
sky: color.sky10,
|
||||
blue: color.blue10,
|
||||
purple: color.purple10,
|
||||
pink: color.pink10,
|
||||
red: color.red10,
|
||||
orange: color.orange10,
|
||||
yellow: color.yellow10,
|
||||
gray: color.gray10,
|
||||
},
|
||||
background: {
|
||||
green: color.green60,
|
||||
turquoise: color.turquoise60,
|
||||
sky: color.sky60,
|
||||
blue: color.blue60,
|
||||
purple: color.purple60,
|
||||
pink: color.pink60,
|
||||
red: color.red60,
|
||||
orange: color.orange60,
|
||||
yellow: color.yellow60,
|
||||
gray: color.gray60,
|
||||
},
|
||||
};
|
||||
13
packages/twenty-front/src/modules/ui/theme/constants/text.ts
Normal file
13
packages/twenty-front/src/modules/ui/theme/constants/text.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export const text = {
|
||||
lineHeight: {
|
||||
lg: 1.5,
|
||||
md: 1.2,
|
||||
},
|
||||
|
||||
iconSizeMedium: 16,
|
||||
iconSizeSmall: 14,
|
||||
|
||||
iconStrikeLight: 1.6,
|
||||
iconStrikeMedium: 2,
|
||||
iconStrikeBold: 2.5,
|
||||
};
|
||||
@ -0,0 +1,77 @@
|
||||
/* eslint-disable twenty/no-hardcoded-colors */
|
||||
import { accentDark, accentLight } from './accent';
|
||||
import { animation } from './animation';
|
||||
import { backgroundDark, backgroundLight } from './background';
|
||||
import { blur } from './blur';
|
||||
import { borderDark, borderLight } from './border';
|
||||
import { boxShadowDark, boxShadowLight } from './boxShadow';
|
||||
import { color, grayScale } from './colors';
|
||||
import { fontDark, fontLight } from './font';
|
||||
import { icon } from './icon';
|
||||
import { modal } from './modal';
|
||||
import { tagDark, tagLight } from './tag';
|
||||
import { text } from './text';
|
||||
|
||||
const common = {
|
||||
color: color,
|
||||
grayScale: grayScale,
|
||||
icon: icon,
|
||||
modal: modal,
|
||||
text: text,
|
||||
blur: blur,
|
||||
animation: animation,
|
||||
snackBar: {
|
||||
success: {
|
||||
background: '#16A26B',
|
||||
color: '#D0F8E9',
|
||||
},
|
||||
error: {
|
||||
background: '#B43232',
|
||||
color: '#FED8D8',
|
||||
},
|
||||
info: {
|
||||
background: color.gray80,
|
||||
color: grayScale.gray0,
|
||||
},
|
||||
},
|
||||
spacingMultiplicator: 4,
|
||||
spacing: (...args: number[]) =>
|
||||
args.map((multiplicator) => `${multiplicator * 4}px`).join(' '),
|
||||
betweenSiblingsGap: `2px`,
|
||||
table: {
|
||||
horizontalCellMargin: '8px',
|
||||
checkboxColumnWidth: '32px',
|
||||
},
|
||||
rightDrawerWidth: '500px',
|
||||
clickableElementBackgroundTransition: 'background 0.1s ease',
|
||||
lastLayerZIndex: 2147483647,
|
||||
};
|
||||
|
||||
export const lightTheme = {
|
||||
...common,
|
||||
...{
|
||||
accent: accentLight,
|
||||
background: backgroundLight,
|
||||
border: borderLight,
|
||||
tag: tagLight,
|
||||
boxShadow: boxShadowLight,
|
||||
font: fontLight,
|
||||
name: 'light',
|
||||
},
|
||||
};
|
||||
export type ThemeType = typeof lightTheme;
|
||||
|
||||
export const darkTheme: ThemeType = {
|
||||
...common,
|
||||
...{
|
||||
accent: accentDark,
|
||||
background: backgroundDark,
|
||||
border: borderDark,
|
||||
tag: tagDark,
|
||||
boxShadow: boxShadowDark,
|
||||
font: fontDark,
|
||||
name: 'dark',
|
||||
},
|
||||
};
|
||||
|
||||
export const MOBILE_VIEWPORT = 768;
|
||||
@ -0,0 +1,51 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
export const useColorScheme = () => {
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { updateOneRecord: updateOneWorkspaceMember } = useUpdateOneRecord({
|
||||
objectNameSingular: 'workspaceMember',
|
||||
});
|
||||
|
||||
const colorScheme = currentWorkspaceMember?.colorScheme ?? 'System';
|
||||
|
||||
const setColorScheme = useCallback(
|
||||
async (value: ColorScheme) => {
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
setCurrentWorkspaceMember((current) => {
|
||||
if (!current) {
|
||||
return current;
|
||||
}
|
||||
return {
|
||||
...current,
|
||||
colorScheme: value,
|
||||
};
|
||||
});
|
||||
await updateOneWorkspaceMember?.({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
colorScheme: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
currentWorkspaceMember,
|
||||
setCurrentWorkspaceMember,
|
||||
updateOneWorkspaceMember,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
colorScheme,
|
||||
setColorScheme,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
export const useSystemColorScheme = (): ColorScheme => {
|
||||
const mediaQuery = useMemo(
|
||||
() => window.matchMedia('(prefers-color-scheme: dark)'),
|
||||
[],
|
||||
);
|
||||
|
||||
const [preferredColorScheme, setPreferredColorScheme] = useState<ColorScheme>(
|
||||
!window.matchMedia || !mediaQuery.matches ? 'Light' : 'Dark',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!window.matchMedia) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleChange = (event: MediaQueryListEvent): void => {
|
||||
setPreferredColorScheme(event.matches ? 'Dark' : 'Light');
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handleChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener('change', handleChange);
|
||||
};
|
||||
}, [mediaQuery]);
|
||||
|
||||
return preferredColorScheme;
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mainColorNames, ThemeColor } from '../constants/colors';
|
||||
|
||||
export const themeColorSchema = z.enum(
|
||||
mainColorNames as [ThemeColor, ...ThemeColor[]],
|
||||
);
|
||||
Reference in New Issue
Block a user