Complete linaria migration (#18361)
## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
This commit is contained in:
+2
-2
@@ -19,8 +19,8 @@ import {
|
||||
} from 'twenty-ui/display';
|
||||
import { ProgressBar, useProgressAnimation } from 'twenty-ui/feedback';
|
||||
import { LightButton, LightIconButton } from 'twenty-ui/input';
|
||||
import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export enum SnackBarVariant {
|
||||
Default = 'default',
|
||||
|
||||
+1
-2
@@ -7,9 +7,8 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { snackBarInternalComponentState } from '@/ui/feedback/snack-bar-manager/states/snackBarInternalComponentState';
|
||||
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { SnackBar } from './SnackBar';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSnackBarContainer = styled.div`
|
||||
bottom: ${themeCssVariables.spacing[3]};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDr
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme-constants';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
@@ -7,14 +7,28 @@ import { CurrencyPickerDropdownButton } from '@/ui/input/components/internal/cur
|
||||
import { type Currency } from '@/ui/input/components/internal/types/Currency';
|
||||
import { IMaskInput } from 'react-imask';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { TEXT_INPUT_STYLE, ThemeContext } from 'twenty-ui/theme';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledIMaskInput = styled(IMaskInput)`
|
||||
margin: 0;
|
||||
${TEXT_INPUT_STYLE}
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1.5]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
@@ -5,7 +5,6 @@ import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton';
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
@@ -27,7 +26,22 @@ export type TextAreaInputProps = {
|
||||
};
|
||||
|
||||
const StyledTextArea = styled(TextareaAutosize)`
|
||||
${TEXT_INPUT_STYLE}
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -3,12 +3,26 @@ import { useEffect, useRef, useState, type ChangeEvent } from 'react';
|
||||
|
||||
import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton';
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledTextInput = styled.input`
|
||||
margin: 0;
|
||||
${TEXT_INPUT_STYLE}
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
|
||||
&:disabled {
|
||||
@@ -16,7 +30,7 @@ export const StyledTextInput = styled.input`
|
||||
}
|
||||
`;
|
||||
|
||||
type TextInputProps = {
|
||||
export type TextInputProps = {
|
||||
instanceId: string;
|
||||
placeholder?: string;
|
||||
autoFocus?: boolean;
|
||||
@@ -30,6 +44,7 @@ type TextInputProps = {
|
||||
copyButton?: boolean;
|
||||
shouldTrim?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const getValue = (value: string, shouldTrim: boolean) => {
|
||||
@@ -54,6 +69,7 @@ export const TextInput = ({
|
||||
copyButton = true,
|
||||
shouldTrim = true,
|
||||
disabled,
|
||||
className,
|
||||
}: TextInputProps) => {
|
||||
const [internalText, setInternalText] = useState(value);
|
||||
|
||||
@@ -91,6 +107,7 @@ export const TextInput = ({
|
||||
autoFocus={autoFocus}
|
||||
value={internalText}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
/>
|
||||
{copyButton && (
|
||||
<div ref={copyRef}>
|
||||
|
||||
@@ -44,17 +44,12 @@ const StyledPicture = styled.button<{ withPicture: boolean }>`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
}
|
||||
|
||||
${({ withPicture, disabled }) => {
|
||||
if ((withPicture || disabled) === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.medium};
|
||||
}
|
||||
`;
|
||||
}};
|
||||
&:hover {
|
||||
background: ${({ withPicture, disabled }) =>
|
||||
withPicture || disabled
|
||||
? 'transparent'
|
||||
: themeCssVariables.background.transparent.medium};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
|
||||
+15
-2
@@ -8,7 +8,6 @@ import {
|
||||
import 'react-phone-number-input/style.css';
|
||||
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
@@ -16,7 +15,21 @@ const StyledInput = styled.input<{
|
||||
withRightComponent?: boolean;
|
||||
hasError?: boolean;
|
||||
}>`
|
||||
${TEXT_INPUT_STYLE}
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
box-sizing: border-box;
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
|
||||
+15
-2
@@ -3,7 +3,6 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { useInputFocusWithoutScrollOnMount } from '@/ui/input/hooks/useInputFocusWithoutScrollOnMount';
|
||||
import { styled } from '@linaria/react';
|
||||
import { forwardRef, type InputHTMLAttributes } from 'react';
|
||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledDropdownMenuSearchInputContainer = styled.div`
|
||||
@@ -18,7 +17,21 @@ const StyledDropdownMenuSearchInputContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledInput = styled.input`
|
||||
${TEXT_INPUT_STYLE}
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
background-color: transparent;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
|
||||
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
||||
import {
|
||||
Breadcrumb,
|
||||
type BreadcrumbProps,
|
||||
} from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { styled } from '@linaria/react';
|
||||
import { forwardRef } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFullScreenOverlay = styled.div`
|
||||
background: ${themeCssVariables.background.noisy};
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: ${RootStackingContextZIndices.RootModal};
|
||||
`;
|
||||
|
||||
const StyledFullScreenHeader = styled(PageHeader)`
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledFullScreenContent = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
min-height: 0;
|
||||
padding: 0 ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[3]}
|
||||
${themeCssVariables.spacing[3]};
|
||||
|
||||
> * {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
row-gap: ${themeCssVariables.spacing[5]};
|
||||
}
|
||||
`;
|
||||
|
||||
type FullScreenModalProps = {
|
||||
children: React.ReactNode;
|
||||
links: BreadcrumbProps['links'];
|
||||
onClose: () => void;
|
||||
hasClosePageButton?: boolean;
|
||||
};
|
||||
|
||||
export const FullScreenModal = forwardRef<HTMLDivElement, FullScreenModalProps>(
|
||||
({ children, links, onClose, hasClosePageButton = true }, ref) => {
|
||||
return (
|
||||
<StyledFullScreenOverlay
|
||||
ref={ref}
|
||||
data-globally-prevent-click-outside="true"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<StyledFullScreenHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={hasClosePageButton}
|
||||
onClosePage={onClose}
|
||||
/>
|
||||
<StyledFullScreenContent>{children}</StyledFullScreenContent>
|
||||
</StyledFullScreenOverlay>
|
||||
);
|
||||
},
|
||||
);
|
||||
+8
-56
@@ -1,51 +1,7 @@
|
||||
import { RootStackingContextZIndices } from '@/ui/layout/constants/RootStackingContextZIndices';
|
||||
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
|
||||
import {
|
||||
Breadcrumb,
|
||||
type BreadcrumbProps,
|
||||
} from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { styled } from '@linaria/react';
|
||||
import { FullScreenModal } from '@/ui/layout/fullscreen/components/FullScreenModal';
|
||||
import { type BreadcrumbProps } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useRef } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFullScreenOverlay = styled.div`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: ${themeCssVariables.background.noisy};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
z-index: ${RootStackingContextZIndices.RootModal};
|
||||
`;
|
||||
|
||||
const StyledFullScreenHeader = styled(PageHeader)`
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledFullScreenContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 0 ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[3]}
|
||||
${themeCssVariables.spacing[3]};
|
||||
|
||||
// Make the immediate child a flex column that grows, so nested components
|
||||
// with height="100%" (e.g., editors) can size correctly.
|
||||
> * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
row-gap: ${themeCssVariables.spacing[5]};
|
||||
}
|
||||
`;
|
||||
|
||||
type UseFullScreenModalProps = {
|
||||
links: BreadcrumbProps['links'];
|
||||
@@ -67,18 +23,14 @@ export const useFullScreenModal = ({
|
||||
if (!isOpen) return null;
|
||||
|
||||
return createPortal(
|
||||
<StyledFullScreenOverlay
|
||||
<FullScreenModal
|
||||
ref={overlayRef}
|
||||
data-globally-prevent-click-outside="true"
|
||||
tabIndex={-1}
|
||||
links={links}
|
||||
onClose={onClose}
|
||||
hasClosePageButton={hasClosePageButton}
|
||||
>
|
||||
<StyledFullScreenHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={hasClosePageButton}
|
||||
onClosePage={onClose}
|
||||
/>
|
||||
<StyledFullScreenContent>{children}</StyledFullScreenContent>
|
||||
</StyledFullScreenOverlay>,
|
||||
{children}
|
||||
</FullScreenModal>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTopBarContainer = styled.div<{ isMobile: boolean }>`
|
||||
align-items: center;
|
||||
|
||||
+4
-2
@@ -3,7 +3,7 @@ import { type ReactNode, useEffect, useRef } from 'react';
|
||||
import { SelectableListItemHotkeyEffect } from '@/ui/layout/selectable-list/components/SelectableListItemHotkeyEffect';
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledListItemContainer = styled.div`
|
||||
@@ -18,12 +18,14 @@ export type SelectableListItemProps = {
|
||||
itemId: string;
|
||||
children: ReactNode;
|
||||
onEnter?: () => void;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const SelectableListItem = ({
|
||||
itemId,
|
||||
children,
|
||||
onEnter,
|
||||
className,
|
||||
}: SelectableListItemProps) => {
|
||||
const isSelectedItemId = useAtomComponentFamilyStateValue(
|
||||
isSelectedItemIdComponentFamilyState,
|
||||
@@ -56,7 +58,7 @@ export const SelectableListItem = ({
|
||||
{isSelectedItemId && isDefined(onEnter) && (
|
||||
<SelectableListItemHotkeyEffect itemId={itemId} onEnter={onEnter} />
|
||||
)}
|
||||
<StyledListItemContainer ref={listItemRef}>
|
||||
<StyledListItemContainer ref={listItemRef} className={className}>
|
||||
{children}
|
||||
</StyledListItemContainer>
|
||||
</>
|
||||
|
||||
+7
-6
@@ -3,12 +3,13 @@ import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectN
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { lazy, Suspense, useContext } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const ActivityRichTextEditor = lazy(() =>
|
||||
import('@/activities/components/ActivityRichTextEditor').then((module) => ({
|
||||
@@ -17,7 +18,7 @@ const ActivityRichTextEditor = lazy(() =>
|
||||
);
|
||||
|
||||
const StyledShowPageActivityContainer = styled.div`
|
||||
margin-top: ${({ theme }) => theme.spacing(6)};
|
||||
margin-top: ${themeCssVariables.spacing[6]};
|
||||
width: 100%;
|
||||
padding-inline: 44px;
|
||||
box-sizing: border-box;
|
||||
@@ -27,11 +28,11 @@ const StyledSkeletonContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: ${({ theme }) => theme.spacing(0, 4)};
|
||||
padding: 0 ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
const LoadingSkeleton = () => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<StyledSkeletonContainer>
|
||||
|
||||
+18
-17
@@ -1,8 +1,7 @@
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { type ChangeEvent, type ReactNode, useRef } from 'react';
|
||||
import { type ChangeEvent, type ReactNode, useContext, useRef } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
@@ -12,6 +11,8 @@ import {
|
||||
type IconComponent,
|
||||
} from 'twenty-ui/display';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
import {
|
||||
@@ -39,11 +40,11 @@ export const StyledShowPageSummaryCard = styled.div<{
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: ${({ isMobile }) => (isMobile ? 'row' : 'column')};
|
||||
gap: ${({ theme, isMobile }) =>
|
||||
isMobile ? theme.spacing(2) : theme.spacing(3)};
|
||||
gap: ${({ isMobile }) =>
|
||||
isMobile ? themeCssVariables.spacing[2] : themeCssVariables.spacing[3]};
|
||||
justify-content: ${({ isMobile }) => (isMobile ? 'flex-start' : 'center')};
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
padding: ${themeCssVariables.spacing[4]};
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
height: ${({ isMobile }) => (isMobile ? '77px' : '127px')};
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
@@ -52,21 +53,21 @@ const StyledInfoContainer = styled.div<{ isMobile: boolean }>`
|
||||
align-items: ${({ isMobile }) => (isMobile ? 'flex-start' : 'center')};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDate = styled.div<{ isMobile: boolean }>`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
cursor: pointer;
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div<{ isMobile: boolean }>`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${themeCssVariables.font.size.xl};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
justify-content: ${({ isMobile }) => (isMobile ? 'flex-start' : 'center')};
|
||||
width: 90%;
|
||||
`;
|
||||
@@ -75,9 +76,9 @@ const StyledAvatarWrapper = styled.div<{
|
||||
isAvatarEditable: boolean;
|
||||
hasIcon: boolean;
|
||||
}>`
|
||||
background-color: ${({ theme, hasIcon }) =>
|
||||
hasIcon ? theme.background.transparent.light : 'unset'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
background-color: ${({ hasIcon }) =>
|
||||
hasIcon ? themeCssVariables.background.transparent.light : 'unset'};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
cursor: ${({ isAvatarEditable }) =>
|
||||
isAvatarEditable ? 'pointer' : 'default'};
|
||||
`;
|
||||
@@ -95,7 +96,7 @@ const StyledSubSkeleton = styled.div`
|
||||
`;
|
||||
|
||||
const StyledShowPageSummaryCardSkeletonLoader = () => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<SkeletonTheme
|
||||
baseColor={theme.background.tertiary}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
|
||||
export const TabAvatar = ({ tab }: { tab: SingleTabProps }) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
if (isDefined(tab.logo)) {
|
||||
return <Avatar avatarUrl={tab.logo} size="md" placeholder={tab.title} />;
|
||||
|
||||
@@ -7,23 +7,24 @@ import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimensio
|
||||
import { TabListHiddenMeasurements } from '@/ui/layout/tab-list/components/TabListHiddenMeasurements';
|
||||
import { useTabListMeasurements } from '@/ui/layout/tab-list/hooks/useTabListMeasurements';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TabButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { TabListDropdown } from './TabListDropdown';
|
||||
import { TabListFromUrlOptionalEffect } from './TabListFromUrlOptionalEffect';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(10)};
|
||||
height: ${themeCssVariables.spacing[10]};
|
||||
position: relative;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
|
||||
&::after {
|
||||
background-color: ${({ theme }) => theme.border.color.light};
|
||||
background-color: ${themeCssVariables.border.color.light};
|
||||
bottom: 0;
|
||||
content: '';
|
||||
height: 1px;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import type { ReactNode } from 'react';
|
||||
import { TAB_LIST_GAP } from '@/ui/layout/tab-list/constants/TabListGap';
|
||||
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
|
||||
import { NodeDimension } from '@/ui/utilities/dimensions/components/NodeDimension';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { IconPlus } from 'twenty-ui/display';
|
||||
import { TabButton } from 'twenty-ui/input';
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconChevronDown } from 'twenty-ui/display';
|
||||
import { TabButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTabMoreButton = styled(TabButton)`
|
||||
height: ${({ theme }) => theme.spacing(10)};
|
||||
height: ${themeCssVariables.spacing[10]};
|
||||
`;
|
||||
|
||||
export const TabMoreButton = ({
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
import { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import {
|
||||
IconCalendar,
|
||||
@@ -35,12 +35,14 @@ const tabs = [
|
||||
{ id: 'reports', title: 'Reports', Icon: IconCheckbox },
|
||||
];
|
||||
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledInteractiveContainer = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.strong};
|
||||
border: 1px solid ${themeCssVariables.border.color.strong};
|
||||
max-width: 100%;
|
||||
min-width: 300px;
|
||||
overflow: auto;
|
||||
padding: ${({ theme }) => theme.spacing(5)};
|
||||
padding: ${themeCssVariables.spacing[5]};
|
||||
resize: horizontal;
|
||||
width: 600px;
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const StyledTable = styled.div``;
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTableBody = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: ${({ theme }) => theme.spacing(2)} 0;
|
||||
padding: ${themeCssVariables.spacing[2]} 0;
|
||||
`;
|
||||
|
||||
export { StyledTableBody as TableBody };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type TableCellProps = {
|
||||
align?: 'left' | 'center' | 'right';
|
||||
@@ -7,16 +8,16 @@ type TableCellProps = {
|
||||
|
||||
const StyledTableCell = styled.div<TableCellProps>`
|
||||
align-items: center;
|
||||
color: ${({ color, theme }) => color || theme.font.color.secondary};
|
||||
color: ${({ color }) => color || themeCssVariables.font.color.secondary};
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: ${({ align }) =>
|
||||
align === 'right'
|
||||
? 'flex-end'
|
||||
: align === 'center'
|
||||
? 'center'
|
||||
: 'flex-start'};
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
text-align: ${({ align }) => align ?? 'left'};
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTableHeader = styled.div<{
|
||||
align?: 'left' | 'center' | 'right';
|
||||
onClick?: () => void;
|
||||
}>`
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
align-items: center;
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: ${({ align }) =>
|
||||
align === 'right'
|
||||
? 'flex-end'
|
||||
: align === 'center'
|
||||
? 'center'
|
||||
: 'flex-start'};
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
text-align: ${({ align }) => align ?? 'left'};
|
||||
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTableHeaderText = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(0.5)};
|
||||
padding-bottom: ${themeCssVariables.spacing['0.5']};
|
||||
`;
|
||||
|
||||
export { StyledTableHeaderText as TableHeaderText };
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTableRow = styled('div', {
|
||||
shouldForwardProp: (prop) =>
|
||||
!['isSelected'].includes(prop) && isPropValid(prop),
|
||||
})<{
|
||||
const StyledTableRow = styled.div<{
|
||||
isSelected?: boolean;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
@@ -14,15 +10,13 @@ const StyledTableRow = styled('div', {
|
||||
gridTemplateColumns?: string;
|
||||
mobileGridAutoColumns?: string;
|
||||
}>`
|
||||
background-color: ${({ isSelected, theme }) =>
|
||||
isSelected ? theme.accent.quaternary : 'transparent'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
background-color: ${({ isSelected }) =>
|
||||
isSelected ? themeCssVariables.accent.quaternary : 'transparent'};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
display: grid;
|
||||
grid-auto-columns: ${({ gridAutoColumns }) => gridAutoColumns ?? '1fr'};
|
||||
${({ gridTemplateColumns }) =>
|
||||
gridTemplateColumns
|
||||
? `grid-template-columns: ${gridTemplateColumns};`
|
||||
: ''};
|
||||
grid-template-columns: ${({ gridTemplateColumns }) =>
|
||||
gridTemplateColumns ?? 'none'};
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
grid-auto-columns: ${({ mobileGridAutoColumns, gridAutoColumns }) =>
|
||||
@@ -31,13 +25,15 @@ const StyledTableRow = styled('div', {
|
||||
|
||||
grid-auto-flow: column;
|
||||
transition: background-color
|
||||
${({ theme }) => theme.animation.duration.normal}s;
|
||||
calc(${themeCssVariables.animation.duration.normal} * 1s);
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ onClick, to, theme }) =>
|
||||
onClick || to ? theme.background.transparent.light : 'transparent'};
|
||||
background-color: ${({ onClick, to }) =>
|
||||
onClick || to
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'transparent'};
|
||||
cursor: ${({ onClick, to }) => (onClick || to ? 'pointer' : 'default')};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode, useContext, useState } from 'react';
|
||||
import { TableBody } from './TableBody';
|
||||
import { IconChevronDown, IconChevronUp, Label } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type TableSectionProps = {
|
||||
children: ReactNode;
|
||||
@@ -12,27 +13,27 @@ type TableSectionProps = {
|
||||
|
||||
const StyledSectionHeader = styled.div<{ isExpanded: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
justify-content: space-between;
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
const StyledSection = styled.div<{ isExpanded: boolean }>`
|
||||
max-height: ${({ isExpanded }) => (isExpanded ? 'fit-content' : 0)};
|
||||
max-height: ${({ isExpanded }) => (isExpanded ? 'fit-content' : '0')};
|
||||
opacity: ${({ isExpanded }) => (isExpanded ? 1 : 0)};
|
||||
overflow: hidden;
|
||||
transition:
|
||||
max-height ${({ theme }) => theme.animation.duration.normal}s,
|
||||
opacity ${({ theme }) => theme.animation.duration.normal}s;
|
||||
max-height calc(${themeCssVariables.animation.duration.normal} * 1s),
|
||||
opacity calc(${themeCssVariables.animation.duration.normal} * 1s);
|
||||
`;
|
||||
|
||||
const StyledSectionContent = styled(TableBody)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
`;
|
||||
|
||||
export const TableSection = ({
|
||||
@@ -41,7 +42,7 @@ export const TableSection = ({
|
||||
title,
|
||||
}: TableSectionProps) => {
|
||||
const [isExpanded, setIsExpanded] = useState(isInitiallyExpanded);
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const handleToggleSection = () =>
|
||||
setIsExpanded((previousIsExpanded) => !previousIsExpanded);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
|
||||
const StyledTableSubRow = styled(TableRow)`
|
||||
padding-left: ${({ theme }) => theme.spacing(4)};
|
||||
padding-left: ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
export const TableSubRow = StyledTableSubRow;
|
||||
|
||||
-1
@@ -13,7 +13,6 @@ const meta: Meta<typeof Table> = {
|
||||
decorators: [ComponentDecorator],
|
||||
argTypes: {
|
||||
as: { table: { disable: true } },
|
||||
theme: { table: { disable: true } },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type TopBarProps = {
|
||||
className?: string;
|
||||
@@ -10,9 +11,9 @@ type TopBarProps = {
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
display: flex;
|
||||
margin-left: ${({ theme }) => theme.spacing(3)};
|
||||
margin-left: ${themeCssVariables.spacing[3]};
|
||||
|
||||
flex-direction: column;
|
||||
`;
|
||||
@@ -21,13 +22,13 @@ const StyledTopBar = styled.div`
|
||||
align-items: center;
|
||||
|
||||
box-sizing: border-box;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
height: 39px;
|
||||
justify-content: space-between;
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
|
||||
z-index: 7;
|
||||
`;
|
||||
@@ -38,8 +39,8 @@ const StyledLeftSection = styled.div`
|
||||
|
||||
const StyledRightSection = styled.div`
|
||||
display: flex;
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
gap: ${({ theme }) => theme.betweenSiblingsGap};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
gap: ${themeCssVariables.betweenSiblingsGap};
|
||||
`;
|
||||
|
||||
export const TopBar = ({
|
||||
|
||||
+8
-7
@@ -1,8 +1,9 @@
|
||||
import { MobileBreadcrumb } from '@/ui/navigation/bread-crumb/components/MobileBreadcrumb';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Fragment, type ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export type BreadcrumbProps = {
|
||||
className?: string;
|
||||
@@ -11,14 +12,14 @@ export type BreadcrumbProps = {
|
||||
|
||||
const StyledWrapper = styled.nav`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: grid;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
grid-auto-flow: column;
|
||||
grid-column-gap: ${({ theme }) => theme.spacing(1)};
|
||||
grid-column-gap: ${themeCssVariables.spacing[1]};
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
@@ -30,14 +31,14 @@ const StyledLink = styled(Link)`
|
||||
`;
|
||||
|
||||
const StyledText = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledDivider = styled.span`
|
||||
width: ${({ theme }) => theme.spacing(2)};
|
||||
width: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
||||
|
||||
+9
-8
@@ -1,11 +1,12 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useOpenSettingsMenu } from '@/navigation/hooks/useOpenSettings';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type ReactNode } from 'react';
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { IconChevronLeft } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export type MobileBreadcrumbProps = {
|
||||
className?: string;
|
||||
@@ -14,14 +15,14 @@ export type MobileBreadcrumbProps = {
|
||||
|
||||
const StyledWrapper = styled.nav`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: grid;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
grid-auto-flow: column;
|
||||
grid-column-gap: ${({ theme }) => theme.spacing(1)};
|
||||
grid-column-gap: ${themeCssVariables.spacing[1]};
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
@@ -43,7 +44,7 @@ export const MobileBreadcrumb = ({
|
||||
className,
|
||||
links,
|
||||
}: MobileBreadcrumbProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const { openSettingsMenu } = useOpenSettingsMenu();
|
||||
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { type Placement } from '@floating-ui/react';
|
||||
import {
|
||||
type FunctionComponent,
|
||||
type MouseEvent,
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import {
|
||||
IconChevronRight,
|
||||
type IconComponent,
|
||||
@@ -62,7 +63,7 @@ export const MenuItemWithOptionDropdown = ({
|
||||
hasSubMenu = false,
|
||||
dropdownPlacement = 'bottom-end',
|
||||
}: MenuItemWithOptionDropdownProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const handleMenuItemClick = (event: MouseEvent<HTMLDivElement>) => {
|
||||
if (!onClick) return;
|
||||
|
||||
+3
-2
@@ -7,13 +7,14 @@ import {
|
||||
} from '@/ui/navigation/navigation-drawer/components/MultiWorkspaceDropdown/internal/MultiWorkspacesDropdownStyles';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
|
||||
export const MultiWorkspaceDropdownClickableComponent = () => {
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const isNavigationDrawerExpanded = useAtomStateValue(
|
||||
isNavigationDrawerExpandedState,
|
||||
|
||||
+4
-3
@@ -25,8 +25,9 @@ import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { type ApolloError } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { AppPath, SettingsPath } from 'twenty-shared/types';
|
||||
@@ -54,8 +55,8 @@ import {
|
||||
import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl';
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
export const MultiWorkspaceDropdownDefaultComponents = () => {
|
||||
|
||||
+15
-12
@@ -1,39 +1,42 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { IconChevronDown } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledContainer = styled.div<{
|
||||
isNavigationDrawerExpanded: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
border: 1px solid transparent;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: ${({ theme }) => theme.spacing(5)};
|
||||
padding: calc(${({ theme }) => theme.spacing(1)} - 1px);
|
||||
height: ${themeCssVariables.spacing[5]};
|
||||
padding: calc(${themeCssVariables.spacing[1]} - 1px);
|
||||
width: ${({ isNavigationDrawerExpanded }) =>
|
||||
isNavigationDrawerExpanded ? '100%' : 'auto'};
|
||||
gap: ${({ theme, isNavigationDrawerExpanded }) =>
|
||||
isNavigationDrawerExpanded ? theme.spacing(1) : '0'};
|
||||
gap: ${({ isNavigationDrawerExpanded }) =>
|
||||
isNavigationDrawerExpanded ? themeCssVariables.spacing[1] : '0'};
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledLabel = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
export const StyledIconChevronDown = styled(IconChevronDown)<{
|
||||
disabled?: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
color: ${({ disabled, theme }) =>
|
||||
disabled ? theme.font.color.extraLight : theme.font.color.tertiary};
|
||||
color: ${({ disabled }) =>
|
||||
disabled
|
||||
? themeCssVariables.font.color.extraLight
|
||||
: themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
+13
-11
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
|
||||
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { NavigationDrawerBackButton } from './NavigationDrawerBackButton';
|
||||
import { NavigationDrawerHeader } from './NavigationDrawerHeader';
|
||||
|
||||
@@ -39,8 +39,10 @@ const StyledAnimatedContainer = styled.div<{
|
||||
isExpanded
|
||||
? `var(${NAVIGATION_DRAWER_WIDTH_VAR})`
|
||||
: `${NAVIGATION_DRAWER_COLLAPSED_WIDTH}px`};
|
||||
transition: ${({ isResizing, theme }) =>
|
||||
isResizing ? 'none' : `width ${theme.animation.duration.normal}s`};
|
||||
transition: ${({ isResizing }) =>
|
||||
isResizing
|
||||
? 'none'
|
||||
: `width calc(${themeCssVariables.animation.duration.normal} * 1s)`};
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: ${({ isExpanded }) => (isExpanded ? '100%' : '0')};
|
||||
@@ -57,18 +59,18 @@ const StyledContainer = styled.div<{
|
||||
flex-direction: column;
|
||||
width: ${({ isExpanded }) =>
|
||||
isExpanded ? `var(${NAVIGATION_DRAWER_WIDTH_VAR})` : '100%'};
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
height: 100%;
|
||||
padding: ${({ theme, isSettings, isMobile }) =>
|
||||
padding: ${({ isSettings, isMobile }) =>
|
||||
isSettings
|
||||
? isMobile
|
||||
? theme.spacing(3, 0, 0, 8)
|
||||
: theme.spacing(3, 0, 4, 0)
|
||||
: theme.spacing(3, 0, 4, 2)};
|
||||
? `${themeCssVariables.spacing[3]} 0 0 ${themeCssVariables.spacing[8]}`
|
||||
: `${themeCssVariables.spacing[3]} 0 ${themeCssVariables.spacing[4]} 0`
|
||||
: `${themeCssVariables.spacing[3]} 0 ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[2]}`};
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: 100%;
|
||||
padding-left: ${({ theme }) => theme.spacing(5)};
|
||||
padding-right: ${({ theme }) => theme.spacing(5)};
|
||||
padding-left: ${themeCssVariables.spacing[5]};
|
||||
padding-right: ${themeCssVariables.spacing[5]};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
+7
-4
@@ -1,24 +1,27 @@
|
||||
import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import {
|
||||
type AnimationControls,
|
||||
motion,
|
||||
type TargetAndTransition,
|
||||
} from 'framer-motion';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
|
||||
const StyledAnimatedContainer = styled(motion.span)`
|
||||
const StyledAnimatedContainerBase = styled.span`
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const StyledAnimatedContainer = motion.create(StyledAnimatedContainerBase);
|
||||
|
||||
export const NavigationDrawerAnimatedCollapseWrapper = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isSettingsPage = useIsSettingsPage();
|
||||
const isNavigationDrawerExpanded = useAtomStateValue(
|
||||
isNavigationDrawerExpandedState,
|
||||
|
||||
+14
-12
@@ -1,5 +1,5 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
|
||||
@@ -10,6 +10,8 @@ import { useIsWorkspaceActivationStatusEqualsTo } from '@/workspace/hooks/useIsW
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { IconX } from 'twenty-ui/display';
|
||||
import { UndecoratedLink } from 'twenty-ui/navigation';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type NavigationDrawerBackButtonProps = {
|
||||
title: string;
|
||||
@@ -19,18 +21,18 @@ const StyledIconAndButtonContainer = styled.button`
|
||||
align-items: center;
|
||||
background: inherit;
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(1.5, 1)};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing['1.5']} ${themeCssVariables.spacing[1]};
|
||||
width: 100%;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -38,15 +40,15 @@ const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: space-between;
|
||||
padding-left: ${({ theme }) => theme.spacing(5)};
|
||||
padding-left: ${themeCssVariables.spacing[5]};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerBackButton = ({
|
||||
title,
|
||||
}: NavigationDrawerBackButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const navigationMemorizedUrl = useAtomStateValue(navigationMemorizedUrlState);
|
||||
|
||||
const setIsNavigationDrawerExpanded = useSetAtomState(
|
||||
|
||||
+4
-3
@@ -3,17 +3,18 @@ import { navigationDrawerActiveTabState } from '@/ui/navigation/states/navigatio
|
||||
import { NAVIGATION_DRAWER_TABS } from '@/ui/navigation/states/navigationDrawerTabs';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import {
|
||||
IconLayoutSidebarLeftCollapse,
|
||||
IconLayoutSidebarRightCollapse,
|
||||
} from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledCollapseButton = styled.div`
|
||||
align-items: center;
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
+9
-7
@@ -2,20 +2,22 @@ import { type ReactNode } from 'react';
|
||||
|
||||
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFixedContainer = styled.div<{
|
||||
isSettings?: boolean;
|
||||
isMobile?: boolean;
|
||||
}>`
|
||||
${({ isSettings, theme, isMobile }) =>
|
||||
padding-left: ${({ isSettings }) =>
|
||||
isSettings ? themeCssVariables.spacing[5] : '0'};
|
||||
padding-right: ${({ isSettings, isMobile }) =>
|
||||
isSettings
|
||||
? `
|
||||
padding-left: ${theme.spacing(5)};
|
||||
padding-right: ${isMobile ? theme.spacing(5) : theme.spacing(8)};
|
||||
`
|
||||
: ''}
|
||||
? isMobile
|
||||
? themeCssVariables.spacing[5]
|
||||
: themeCssVariables.spacing[8]
|
||||
: '0'};
|
||||
`;
|
||||
export const NavigationDrawerFixedContent = ({
|
||||
children,
|
||||
|
||||
+11
-10
@@ -1,7 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconSearch } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu';
|
||||
import { PAGE_BAR_MIN_HEIGHT } from '@/ui/layout/page/constants/PageBarMinHeight';
|
||||
@@ -15,28 +16,28 @@ const StyledContainer = styled.div<{ isExpanded: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: ${({ isExpanded }) => (isExpanded ? 'row' : 'column')};
|
||||
gap: ${({ theme, isExpanded }) => (isExpanded ? 0 : theme.spacing(4))};
|
||||
gap: ${({ isExpanded }) => (isExpanded ? '0' : themeCssVariables.spacing[4])};
|
||||
user-select: none;
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
min-height: ${PAGE_BAR_MIN_HEIGHT}px;
|
||||
transition: gap ${({ theme }) => theme.animation.duration.normal}s ease;
|
||||
transition: gap calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
|
||||
`;
|
||||
|
||||
const StyledRightActions = styled.div<{ isExpanded: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: ${({ isExpanded }) => (isExpanded ? 'row' : 'column')};
|
||||
gap: ${({ theme, isExpanded }) => (isExpanded ? 0 : theme.spacing(1))};
|
||||
margin-left: ${({ isExpanded }) => (isExpanded ? 'auto' : 0)};
|
||||
transition: gap ${({ theme }) => theme.animation.duration.normal}s ease;
|
||||
gap: ${({ isExpanded }) => (isExpanded ? '0' : themeCssVariables.spacing[1])};
|
||||
margin-left: ${({ isExpanded }) => (isExpanded ? 'auto' : '0')};
|
||||
transition: gap calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
|
||||
`;
|
||||
|
||||
const StyledNavigationDrawerCollapseButton = styled(
|
||||
NavigationDrawerCollapseButton,
|
||||
)`
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
width: ${({ theme }) => theme.spacing(6)};
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
type NavigationDrawerHeaderProps = {
|
||||
|
||||
+88
-108
@@ -9,12 +9,10 @@ import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNaviga
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type ReactNode } from 'react';
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Pill } from 'twenty-ui/components';
|
||||
@@ -27,7 +25,8 @@ import {
|
||||
TooltipDelay,
|
||||
TooltipPosition,
|
||||
} from 'twenty-ui/display';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import {
|
||||
type TriggerEventType,
|
||||
useMouseDownNavigation,
|
||||
@@ -81,80 +80,70 @@ type StyledItemProps = Pick<
|
||||
rel?: string;
|
||||
};
|
||||
|
||||
const StyledItem = styled('button', {
|
||||
shouldForwardProp: (prop) =>
|
||||
![
|
||||
'active',
|
||||
'danger',
|
||||
'soon',
|
||||
'isDragging',
|
||||
'isSelectedInEditMode',
|
||||
].includes(prop) && isPropValid(prop),
|
||||
})<StyledItemProps>`
|
||||
const StyledItem = styled.button<StyledItemProps>`
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
background: ${(props) =>
|
||||
props.active ? props.theme.background.transparent.light : 'inherit'};
|
||||
height: ${({ theme }) => theme.spacing(7)};
|
||||
border: ${({ theme, isSelectedInEditMode }) =>
|
||||
background: ${({ active }) =>
|
||||
active ? themeCssVariables.background.transparent.light : 'transparent'};
|
||||
height: ${themeCssVariables.spacing[7]};
|
||||
border: ${({ isSelectedInEditMode }) =>
|
||||
isSelectedInEditMode
|
||||
? `1px solid ${theme.color.blue}`
|
||||
? `1px solid ${themeCssVariables.color.blue}`
|
||||
: '1px solid transparent'};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
text-decoration: none;
|
||||
color: ${(props) => {
|
||||
if (props.active === true) {
|
||||
return props.theme.font.color.primary;
|
||||
color: ${({ active, danger, soon }) => {
|
||||
if (active === true) {
|
||||
return themeCssVariables.font.color.primary;
|
||||
}
|
||||
if (props.danger === true) {
|
||||
return props.theme.color.red;
|
||||
if (danger === true) {
|
||||
return themeCssVariables.color.red;
|
||||
}
|
||||
if (props.soon === true) {
|
||||
return props.theme.font.color.light;
|
||||
if (soon === true) {
|
||||
return themeCssVariables.font.color.light;
|
||||
}
|
||||
return props.theme.font.color.secondary;
|
||||
return themeCssVariables.font.color.secondary;
|
||||
}};
|
||||
cursor: ${(props) => (props.soon ? 'default' : 'pointer')};
|
||||
cursor: ${({ soon, isDragging }) =>
|
||||
isDragging ? 'grabbing' : soon ? 'default' : 'pointer'};
|
||||
display: flex;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-right: ${({ theme, hasRightOptions }) =>
|
||||
hasRightOptions ? theme.spacing(0.5) : theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
padding-bottom: ${themeCssVariables.spacing[1]};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
padding-right: ${({ hasRightOptions }) =>
|
||||
hasRightOptions
|
||||
? themeCssVariables.spacing['0.5']
|
||||
: themeCssVariables.spacing[1]};
|
||||
padding-top: ${themeCssVariables.spacing[1]};
|
||||
|
||||
margin-top: ${({ indentationLevel }) =>
|
||||
indentationLevel === 2 ? '2px' : '0'};
|
||||
|
||||
pointer-events: ${(props) => (props.soon ? 'none' : 'auto')};
|
||||
pointer-events: ${({ soon }) => (soon ? 'none' : 'auto')};
|
||||
|
||||
width: ${(props) =>
|
||||
!props.isNavigationDrawerExpanded
|
||||
? `calc(${NAVIGATION_DRAWER_COLLAPSED_WIDTH}px - ${props.theme.spacing(6)} + ${props.theme.spacing(1)} + ${props.hasRightOptions ? props.theme.spacing(0.5) : props.theme.spacing(1)})`
|
||||
: `calc(100% - ${props.theme.spacing(1.5)} + ${props.theme.spacing(1)} + ${props.hasRightOptions ? props.theme.spacing(0.5) : props.theme.spacing(1)})`};
|
||||
width: ${({ isNavigationDrawerExpanded, hasRightOptions }) =>
|
||||
!isNavigationDrawerExpanded
|
||||
? `calc(${NAVIGATION_DRAWER_COLLAPSED_WIDTH}px - ${themeCssVariables.spacing[6]} + ${themeCssVariables.spacing[1]} + ${hasRightOptions ? themeCssVariables.spacing['0.5'] : themeCssVariables.spacing[1]})`
|
||||
: `calc(100% - ${themeCssVariables.spacing['1.5']} + ${themeCssVariables.spacing[1]} + ${hasRightOptions ? themeCssVariables.spacing['0.5'] : themeCssVariables.spacing[1]})`};
|
||||
|
||||
${({ isDragging }) =>
|
||||
isDragging &&
|
||||
`
|
||||
cursor: grabbing;
|
||||
`}
|
||||
|
||||
:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
color: ${(props) =>
|
||||
props.danger ? props.theme.color.red : props.theme.font.color.primary};
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
color: ${({ danger }) =>
|
||||
danger
|
||||
? themeCssVariables.color.red
|
||||
: themeCssVariables.font.color.primary};
|
||||
}
|
||||
|
||||
:hover .keyboard-shortcuts {
|
||||
&:hover .keyboard-shortcuts {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
user-select: none;
|
||||
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-size: ${themeCssVariables.font.size.lg};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -175,22 +164,22 @@ const StyledLabelParent = styled.div`
|
||||
`;
|
||||
|
||||
const StyledItemLabel = styled.span`
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledItemSecondaryLabel = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
`;
|
||||
|
||||
const StyledItemCount = styled.span`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.color.blue};
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
color: ${({ theme }) => theme.grayScale.gray1};
|
||||
background-color: ${themeCssVariables.color.blue};
|
||||
border-radius: ${themeCssVariables.border.radius.rounded};
|
||||
color: ${themeCssVariables.grayScale.gray1};
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-size: ${themeCssVariables.font.size.xs};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
@@ -201,15 +190,15 @@ const StyledKeyBoardShortcut = styled.span`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
height: ${themeCssVariables.spacing[4]};
|
||||
justify-content: center;
|
||||
width: ${({ theme }) => theme.spacing(4)};
|
||||
width: ${themeCssVariables.spacing[4]};
|
||||
box-sizing: border-box;
|
||||
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.strong};
|
||||
background: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
border: 1px solid ${themeCssVariables.border.color.strong};
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
`;
|
||||
|
||||
const StyledNavigationDrawerItemContainer = styled.div`
|
||||
@@ -230,18 +219,18 @@ const StyledIcon = styled.div<{
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
${({ theme, $backgroundColor, $borderColor }) =>
|
||||
$backgroundColor &&
|
||||
css`
|
||||
background-color: ${$backgroundColor};
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
height: ${theme.spacing(4)};
|
||||
width: ${theme.spacing(4)};
|
||||
${$borderColor ? `border: 1px solid ${$borderColor};` : ''}
|
||||
`}
|
||||
margin-right: ${themeCssVariables.spacing[2]};
|
||||
background-color: ${({ $backgroundColor }) =>
|
||||
$backgroundColor || 'transparent'};
|
||||
border-radius: ${({ $backgroundColor }) => ($backgroundColor ? '4px' : '0')};
|
||||
box-sizing: ${({ $backgroundColor }) =>
|
||||
$backgroundColor ? 'border-box' : 'content-box'};
|
||||
height: ${({ $backgroundColor }) =>
|
||||
$backgroundColor ? themeCssVariables.spacing[4] : 'auto'};
|
||||
width: ${({ $backgroundColor }) =>
|
||||
$backgroundColor ? themeCssVariables.spacing[4] : 'auto'};
|
||||
border: ${({ $backgroundColor, $borderColor }) =>
|
||||
$backgroundColor && $borderColor ? `1px solid ${$borderColor}` : 'none'};
|
||||
`;
|
||||
|
||||
const StyledRightOptionsContainer = styled.div`
|
||||
@@ -250,42 +239,31 @@ const StyledRightOptionsContainer = styled.div`
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
height: ${({ theme }) => theme.spacing(6)};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
`;
|
||||
|
||||
const visibleStateStyles = css`
|
||||
clip-path: unset;
|
||||
display: flex;
|
||||
height: unset;
|
||||
opacity: 1;
|
||||
overflow: unset;
|
||||
position: unset;
|
||||
width: unset;
|
||||
`;
|
||||
|
||||
const StyledRightOptionsVisbility = styled.div<{
|
||||
isMobile: boolean;
|
||||
isRightOptionsDropdownOpen?: boolean;
|
||||
alwaysVisible?: boolean;
|
||||
}>`
|
||||
const StyledRightOptionsVisbility = styled.div`
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transition: opacity 150ms;
|
||||
position: absolute;
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
overflow: hidden;
|
||||
clip-path: inset(1px);
|
||||
white-space: nowrap;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
|
||||
${({ isMobile, isRightOptionsDropdownOpen, alwaysVisible }) =>
|
||||
(isMobile || isRightOptionsDropdownOpen || alwaysVisible) &&
|
||||
visibleStateStyles}
|
||||
|
||||
&[data-visible='true'],
|
||||
.navigation-drawer-item:hover & {
|
||||
${visibleStateStyles}
|
||||
clip-path: unset;
|
||||
display: flex;
|
||||
height: unset;
|
||||
opacity: 1;
|
||||
overflow: unset;
|
||||
position: unset;
|
||||
width: unset;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -314,7 +292,7 @@ export const NavigationDrawerItem = ({
|
||||
preventCollapseOnMobile = false,
|
||||
isSelectedInEditMode = false,
|
||||
}: NavigationDrawerItemProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isMobile = useIsMobile();
|
||||
const isSettingsPage = useIsSettingsPage();
|
||||
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
|
||||
@@ -472,10 +450,12 @@ export const NavigationDrawerItem = ({
|
||||
}}
|
||||
>
|
||||
<StyledRightOptionsVisbility
|
||||
alwaysVisible={alwaysShowRightOptions}
|
||||
isMobile={isMobile}
|
||||
isRightOptionsDropdownOpen={
|
||||
isRightOptionsDropdownOpen || false
|
||||
data-visible={
|
||||
isMobile ||
|
||||
isRightOptionsDropdownOpen ||
|
||||
alwaysShowRightOptions
|
||||
? 'true'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{rightOptions}
|
||||
|
||||
+22
-15
@@ -1,5 +1,6 @@
|
||||
import { type NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export type NavigationDrawerItemBreadcrumbProps = {
|
||||
state?: NavigationDrawerSubItemState;
|
||||
@@ -9,13 +10,15 @@ const StyledNavigationDrawerItemBreadcrumbContainer = styled.div`
|
||||
height: 28px;
|
||||
|
||||
margin-left: 7.5px;
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
margin-right: ${themeCssVariables.spacing[2]};
|
||||
width: 9px;
|
||||
`;
|
||||
|
||||
const StyledGapVerticalLine = styled.div<{ darker: boolean }>`
|
||||
background: ${({ theme, darker }) =>
|
||||
darker ? theme.font.color.tertiary : theme.border.color.strong};
|
||||
background: ${({ darker }) =>
|
||||
darker
|
||||
? themeCssVariables.font.color.tertiary
|
||||
: themeCssVariables.border.color.strong};
|
||||
|
||||
position: relative;
|
||||
top: -2px;
|
||||
@@ -25,8 +28,10 @@ const StyledGapVerticalLine = styled.div<{ darker: boolean }>`
|
||||
`;
|
||||
|
||||
const StyledSecondaryFullVerticalBar = styled.div<{ darker: boolean }>`
|
||||
background: ${({ theme, darker }) =>
|
||||
darker ? theme.font.color.tertiary : theme.border.color.strong};
|
||||
background: ${({ darker }) =>
|
||||
darker
|
||||
? themeCssVariables.font.color.tertiary
|
||||
: themeCssVariables.border.color.strong};
|
||||
|
||||
position: relative;
|
||||
top: -17px;
|
||||
@@ -35,21 +40,23 @@ const StyledSecondaryFullVerticalBar = styled.div<{ darker: boolean }>`
|
||||
`;
|
||||
|
||||
const StyledRoundedProtrusion = styled.div<{ darker: boolean }>`
|
||||
position: relative;
|
||||
top: -2px;
|
||||
|
||||
border: 1px solid
|
||||
${({ darker }) =>
|
||||
darker
|
||||
? themeCssVariables.font.color.tertiary
|
||||
: themeCssVariables.border.color.strong};
|
||||
border-bottom-left-radius: 4px;
|
||||
|
||||
border: 1px solid
|
||||
${({ theme, darker }) =>
|
||||
darker ? theme.font.color.tertiary : theme.border.color.strong};
|
||||
|
||||
${({ darker }) => (darker ? 'z-index: 1;' : '')}
|
||||
border-right: none;
|
||||
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
|
||||
height: 14px;
|
||||
|
||||
position: relative;
|
||||
top: -2px;
|
||||
width: 8px;
|
||||
z-index: ${({ darker }) => (darker ? '1' : 'auto')};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerItemBreadcrumb = ({
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const StyledGroup = styled.div`
|
||||
display: flex;
|
||||
|
||||
+9
-5
@@ -1,16 +1,20 @@
|
||||
import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext, type ReactNode } from 'react';
|
||||
import {
|
||||
type AnimationControls,
|
||||
motion,
|
||||
type TargetAndTransition,
|
||||
} from 'framer-motion';
|
||||
import { type ReactNode } from 'react';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
|
||||
const StyledAnimationGroupContainer = styled(motion.div)``;
|
||||
const StyledAnimationGroupContainerBase = styled.div``;
|
||||
|
||||
const StyledAnimationGroupContainer = motion.create(
|
||||
StyledAnimationGroupContainerBase,
|
||||
);
|
||||
|
||||
type NavigationDrawerItemsCollapsableContainerProps = {
|
||||
isGroup?: boolean;
|
||||
@@ -21,7 +25,7 @@ export const NavigationDrawerItemsCollapsableContainer = ({
|
||||
isGroup = false,
|
||||
children,
|
||||
}: NavigationDrawerItemsCollapsableContainerProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isSettingsPage = useIsSettingsPage();
|
||||
const isNavigationDrawerExpanded = useAtomStateValue(
|
||||
isNavigationDrawerExpandedState,
|
||||
|
||||
+5
-4
@@ -1,8 +1,9 @@
|
||||
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledItemsContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -14,9 +15,9 @@ const StyledItemsContainer = styled.div`
|
||||
|
||||
const StyledScrollableInnerContainer = styled.div<{ isMobile?: boolean }>`
|
||||
height: 100%;
|
||||
padding-left: ${({ theme }) => theme.spacing(5)};
|
||||
padding-right: ${({ theme, isMobile }) =>
|
||||
isMobile ? theme.spacing(5) : theme.spacing(8)};
|
||||
padding-left: ${themeCssVariables.spacing[5]};
|
||||
padding-right: ${({ isMobile }) =>
|
||||
isMobile ? themeCssVariables.spacing[5] : themeCssVariables.spacing[8]};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerScrollableContent = ({
|
||||
|
||||
+9
-6
@@ -1,10 +1,11 @@
|
||||
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSection = styled.div<{ isSettingsDrawer?: boolean }>`
|
||||
margin-bottom: ${({ theme, isSettingsDrawer }) =>
|
||||
isSettingsDrawer ? theme.spacing(3) : 0};
|
||||
margin-bottom: ${({ isSettingsDrawer }) =>
|
||||
isSettingsDrawer ? themeCssVariables.spacing[3] : '0'};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -14,9 +15,11 @@ const StyledSectionInnerContainerMinusScrollPadding = styled.div<{
|
||||
}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.betweenSiblingsGap};
|
||||
width: ${({ isMobile, theme, isSettingsDrawer }) =>
|
||||
`calc(100% - ${isMobile || isSettingsDrawer ? 0 : theme.spacing(2)})`};
|
||||
gap: ${themeCssVariables.betweenSiblingsGap};
|
||||
width: ${({ isMobile, isSettingsDrawer }) =>
|
||||
isMobile || isSettingsDrawer
|
||||
? '100%'
|
||||
: `calc(100% - ${themeCssVariables.spacing[2]})`};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerSection = ({
|
||||
|
||||
+9
-8
@@ -5,25 +5,26 @@ import { NavigationDrawerSectionTitleSkeletonLoader } from '@/ui/navigation/navi
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import React from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Label } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
align-items: center;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(5)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
padding-right: ${({ theme }) => theme.spacing(0.5)};
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${themeCssVariables.spacing[5]};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
padding-right: ${themeCssVariables.spacing['0.5']};
|
||||
padding-top: ${themeCssVariables.spacing[1]};
|
||||
padding-bottom: ${themeCssVariables.spacing[1]};
|
||||
justify-content: space-between;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
+7
-5
@@ -1,15 +1,17 @@
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSkeletonTitle = styled.div`
|
||||
margin-bottom: ${(props) => props.theme.spacing(2)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
margin-bottom: ${themeCssVariables.spacing[2]};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
export const NavigationDrawerSectionTitleSkeletonLoader = () => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<SkeletonTheme
|
||||
baseColor={theme.background.tertiary}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme-constants';
|
||||
|
||||
const isMobile = window.innerWidth <= MOBILE_VIEWPORT;
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { AnimatedCheckmark } from 'twenty-ui/display';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledContainer = styled.div<{ isLast: boolean }>`
|
||||
align-items: center;
|
||||
@@ -15,15 +16,15 @@ const StyledContainer = styled.div<{ isLast: boolean }>`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledStepCircle = styled(motion.div)<{ isInNextSteps: boolean }>`
|
||||
const StyledStepCircleBase = styled.div<{ isInNextSteps: boolean }>`
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: ${({ theme, isInNextSteps }) =>
|
||||
border-color: ${({ isInNextSteps }) =>
|
||||
isInNextSteps
|
||||
? theme.border.color.medium
|
||||
: theme.border.color.inverted} !important;
|
||||
? themeCssVariables.border.color.medium
|
||||
: themeCssVariables.border.color.inverted} !important;
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
flex-shrink: 0;
|
||||
@@ -34,30 +35,38 @@ const StyledStepCircle = styled(motion.div)<{ isInNextSteps: boolean }>`
|
||||
width: 20px;
|
||||
`;
|
||||
|
||||
const StyledStepCircle = motion.create(StyledStepCircleBase);
|
||||
|
||||
const StyledStepIndex = styled.span<{ isCurrentStep: boolean }>`
|
||||
color: ${({ theme, isCurrentStep }) =>
|
||||
isCurrentStep ? theme.font.color.inverted : theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
color: ${({ isCurrentStep }) =>
|
||||
isCurrentStep
|
||||
? themeCssVariables.font.color.inverted
|
||||
: themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledStepLabel = styled.span<{ isInNextSteps: boolean }>`
|
||||
color: ${({ theme, isInNextSteps }) =>
|
||||
isInNextSteps ? theme.font.color.tertiary : theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
color: ${({ isInNextSteps }) =>
|
||||
isInNextSteps
|
||||
? themeCssVariables.font.color.tertiary
|
||||
: themeCssVariables.font.color.primary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin-left: ${themeCssVariables.spacing[2]};
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledStepLine = styled(motion.div)`
|
||||
const StyledStepLineBase = styled.div`
|
||||
height: 2px;
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
margin-left: ${themeCssVariables.spacing[2]};
|
||||
margin-right: ${themeCssVariables.spacing[2]};
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledStepLine = motion.create(StyledStepLineBase);
|
||||
|
||||
export type StepProps = React.PropsWithChildren &
|
||||
React.ComponentProps<'div'> & {
|
||||
isLast?: boolean;
|
||||
@@ -73,7 +82,7 @@ export const Step = ({
|
||||
children,
|
||||
activeStep = 0,
|
||||
}: StepProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const variantsLine = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import React from 'react';
|
||||
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme-constants';
|
||||
import { Step, type StepProps } from './Step';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { useSystemColorScheme } from '@/ui/theme/hooks/useSystemColorScheme';
|
||||
@@ -32,9 +31,7 @@ export const BaseThemeProvider = ({ children }: BaseThemeProviderProps) => {
|
||||
|
||||
return (
|
||||
<ThemeSchemeContext.Provider value={setPersistedColorScheme}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeContextProvider theme={theme}>{children}</ThemeContextProvider>
|
||||
</ThemeProvider>
|
||||
<ThemeContextProvider theme={theme}>{children}</ThemeContextProvider>
|
||||
</ThemeSchemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type RefObject, useCallback, useState } from 'react';
|
||||
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
@@ -23,12 +23,14 @@ type Position = {
|
||||
y: number;
|
||||
};
|
||||
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledDragSelection = styled.div<SelectionBox>`
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
opacity: 0.2;
|
||||
border: 1px solid ${({ theme }) => theme.color.blue3};
|
||||
background: ${({ theme }) => theme.color.blue7};
|
||||
border: 1px solid ${themeCssVariables.color.blue3};
|
||||
background: ${themeCssVariables.color.blue7};
|
||||
top: ${({ top }) => top}px;
|
||||
left: ${({ left }) => left}px;
|
||||
width: ${({ width }) => width}px;
|
||||
|
||||
+21
-16
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { useRef, useState } from 'react';
|
||||
import { userEvent, within } from 'storybook/test';
|
||||
@@ -7,13 +7,14 @@ import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
border: 1px solid ${themeCssVariables.border.color.light};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
height: 400px;
|
||||
overflow: hidden;
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
padding: ${themeCssVariables.spacing[4]};
|
||||
position: relative;
|
||||
width: 600px;
|
||||
`;
|
||||
@@ -21,27 +22,31 @@ const StyledContainer = styled.div`
|
||||
const StyledSelectableItem = styled.div<{ selected?: boolean }>`
|
||||
width: 100px;
|
||||
height: 80px;
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
background: ${({ theme, selected }) =>
|
||||
selected ? theme.color.blue3 : theme.background.secondary};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
background: ${({ selected }) =>
|
||||
selected
|
||||
? themeCssVariables.color.blue3
|
||||
: themeCssVariables.background.secondary};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: ${({ theme }) => theme.spacing(1)};
|
||||
margin: ${themeCssVariables.spacing[1]};
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme, selected }) =>
|
||||
selected ? theme.color.blue5 : theme.background.tertiary};
|
||||
background: ${({ selected }) =>
|
||||
selected
|
||||
? themeCssVariables.color.blue5
|
||||
: themeCssVariables.background.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
@@ -49,15 +54,15 @@ const StyledGrid = styled.div`
|
||||
const StyledLargeGrid = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[4]};
|
||||
width: 900px;
|
||||
height: 600px;
|
||||
`;
|
||||
|
||||
const StyledScrollableWrapper = styled(ScrollWrapper)`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
border: 1px solid ${themeCssVariables.border.color.light};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
height: 300px;
|
||||
width: 600px;
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMediaQuery } from 'react-responsive';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const useIsMobile = () =>
|
||||
useMediaQuery({ query: `(max-width: ${MOBILE_VIEWPORT}px)` });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { ScrollWrapperInitEffect } from '@/ui/utilities/scroll/components/internal/ScrollWrapperInitEffect';
|
||||
import { ScrollWrapperComponentInstanceContext } from '@/ui/utilities/scroll/states/contexts/ScrollWrapperComponentInstanceContext';
|
||||
|
||||
Reference in New Issue
Block a user