chore(twenty-front): migrate small modules from Emotion to Linaria (PR 1/10) (#18314)

## Emotion → Linaria migration — PR 1 of 10

First batch of the `twenty-front` migration from Emotion (runtime
CSS-in-JS) to Linaria (zero-runtime, build-time extraction via
wyw-in-js). Covers **100 files** across 10 small standalone modules —
chosen as the lowest-risk starting point.

### Modules migrated

spreadsheet-import (28) · navigation-menu-item (17) · views (14) ·
billing (10) · blocknote-editor (7) · advanced-text-editor (7) ·
favorites (7) · navigation (4) · information-banner (3) ·
sign-in-background-mock (3)

### Migration pattern

Every file follows the same mechanical transformation:

| Emotion | Linaria |
|---|---|
| `import styled from '@emotion/styled'` | `import { styled } from
'@linaria/react'` |
| `${({ theme }) => theme.font.color.primary}` |
`${themeCssVariables.font.color.primary}` |
| `${({ theme }) => theme.spacing(4)}` |
`${themeCssVariables.spacing[4]}` |
| `const theme = useTheme()` | `const { theme } =
useContext(ThemeContext)` |
| `import { type Theme } from '@emotion/react'` | `import { type
ThemeType } from 'twenty-ui/theme'` |

`themeCssVariables` is a build-time object where every leaf is a
`var(--t-xxx)` CSS custom property reference, evaluated statically by
wyw-in-js. Runtime theme access (icon sizes, colors passed as props)
uses `useContext(ThemeContext)`.

### Gotchas encountered & fixed

- **Interpolation return types** — wyw-in-js requires `string | number`,
never `false`/`undefined`. Replaced `condition && 'css'` with `condition
? 'css' : ''`.
- **`css` tag inside `styled` templates** — Linaria `css` returns a
class name, not CSS text. Replaced with plain template strings.
- **`styled(Component)` needs `className`** — added `className` prop to
`NavigationDrawerSection`, `DropdownMenuItemsContainer`, and `Heading`.
- **`shouldForwardProp` not supported** — Linaria filters invalid DOM
props automatically for HTML elements. For custom components, used
wrapper divs where needed.
- **`FormFieldPlaceholderStyles`** — converted from Emotion `css`
function to a static string using `themeCssVariables`.
This commit is contained in:
Charles Bochet
2026-03-02 16:33:40 +01:00
committed by GitHub
parent 9c4b0f526c
commit c4140f85df
211 changed files with 4754 additions and 1114 deletions
@@ -4,7 +4,8 @@ import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpre
import { spreadsheetImportCreatedRecordsProgressState } from '@/spreadsheet-import/states/spreadsheetImportCreatedRecordsProgressState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { Modal } from '@/ui/layout/modal/components/Modal';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { t } from '@lingui/core/macro';
import { Loader } from 'twenty-ui/feedback';
@@ -16,17 +17,17 @@ const StyledContent = styled(Modal.Content)`
`;
const StyledHeader = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-bottom: ${({ theme }) => theme.spacing(2)};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.medium};
margin-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledDescription = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-bottom: ${({ theme }) => theme.spacing(5)};
color: ${themeCssVariables.font.color.tertiary};
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.regular};
margin-bottom: ${themeCssVariables.spacing[5]};
`;
type ImportDataStepProps = {
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useCallback, useMemo, useState } from 'react';
import { StepNavigationButton } from '@/spreadsheet-import/components/StepNavigationButton';
@@ -31,6 +31,7 @@ import { type SpreadsheetImportField } from '@/spreadsheet-import/types/Spreadsh
import { useAtomFamilySelectorState } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorState';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { Trans, useLingui } from '@lingui/react/macro';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContent = styled(Modal.Content)`
align-items: center;
@@ -41,19 +42,19 @@ const StyledColumnsContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
margin-bottom: ${({ theme }) => theme.spacing(4)};
margin-bottom: ${themeCssVariables.spacing[4]};
`;
const StyledColumns = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
`;
const StyledColumn = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.regular};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.regular};
`;
export type MatchColumnsStepProps = {
@@ -1,6 +1,7 @@
import { type SpreadsheetColumns } from '@/spreadsheet-import/types/SpreadsheetColumns';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import React from 'react';
const StyledGridContainer = styled.div`
@@ -25,8 +26,8 @@ type HeightProps = {
};
const StyledGridRow = styled.div<HeightProps>`
border-bottom: ${({ withBorder, theme }) =>
withBorder && `1px solid ${theme.border.color.medium}`};
border-bottom: ${({ withBorder }) =>
withBorder ? `1px solid ${themeCssVariables.border.color.medium}` : 'none'};
box-sizing: border-box;
display: flex;
flex-direction: row;
@@ -43,44 +44,44 @@ const StyledGridCell = styled.div<PositionProps>`
display: flex;
flex: 1;
overflow-x: auto;
padding-bottom: ${({ theme }) => theme.spacing(4)};
padding-top: ${({ theme }) => theme.spacing(4)};
${({ position, theme }) => {
padding-bottom: ${themeCssVariables.spacing[4]};
padding-top: ${themeCssVariables.spacing[4]};
${({ position }) => {
if (position === 'left') {
return `
padding-left: ${theme.spacing(4)};
padding-right: ${theme.spacing(2)};
padding-top: ${theme.spacing(4)};
padding-left: ${themeCssVariables.spacing[4]};
padding-right: ${themeCssVariables.spacing[2]};
padding-top: ${themeCssVariables.spacing[4]};
`;
}
if (position === 'full-line') {
return `
padding-left: ${theme.spacing(2)};
padding-right: ${theme.spacing(4)};
padding-top: ${theme.spacing(0)};
padding-left: ${themeCssVariables.spacing[2]};
padding-right: ${themeCssVariables.spacing[4]};
padding-top: ${themeCssVariables.spacing[0]};
width: 100%;
`;
}
return `
padding-left: ${theme.spacing(2)};
padding-right: ${theme.spacing(4)};
padding-top: ${theme.spacing(4)};
padding-left: ${themeCssVariables.spacing[2]};
padding-right: ${themeCssVariables.spacing[4]};
padding-top: ${themeCssVariables.spacing[4]};
`;
}};
`;
const StyledGridHeader = styled.div<PositionProps>`
align-items: center;
background-color: ${({ theme }) => theme.background.secondary};
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
background-color: ${themeCssVariables.background.secondary};
border-bottom: 1px solid ${themeCssVariables.border.color.medium};
box-sizing: border-box;
color: ${({ theme }) => theme.font.color.light};
color: ${themeCssVariables.font.color.light};
display: flex;
flex: 1;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
padding-left: ${({ theme }) => theme.spacing(4)};
padding-right: ${({ theme }) => theme.spacing(4)};
font-size: ${themeCssVariables.font.size.xs};
font-weight: ${themeCssVariables.font.weight.semiBold};
padding-left: ${themeCssVariables.spacing[4]};
padding-right: ${themeCssVariables.spacing[4]};
`;
type ColumnGridProps = {
@@ -1,18 +1,19 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledControlContainer = styled.div<{ cursor: string }>`
align-items: center;
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};
box-sizing: border-box;
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
border-radius: ${themeCssVariables.border.radius.sm};
color: ${themeCssVariables.font.color.primary};
cursor: ${({ cursor }) => cursor};
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
gap: ${themeCssVariables.spacing[1]};
height: ${themeCssVariables.spacing[8]};
justify-content: space-between;
padding: 0 ${({ theme }) => theme.spacing(2)};
padding: 0 ${themeCssVariables.spacing[2]};
width: 100%;
`;
@@ -7,13 +7,15 @@ import {
import { type SpreadsheetMatchedOptions } from '@/spreadsheet-import/types/SpreadsheetMatchedOptions';
import { getFieldOptions } from '@/spreadsheet-import/utils/getFieldOptions';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { Tag, type TagColor } from 'twenty-ui/components';
import { IconChevronDown } from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
const StyledIconChevronDown = styled(IconChevronDown)`
color: ${({ theme }) => theme.font.color.tertiary};
color: ${themeCssVariables.font.color.tertiary};
`;
export type SubMatchingSelectDropdownButtonProps = {
@@ -33,7 +35,7 @@ export const SubMatchingSelectDropdownButton = ({
const options = getFieldOptions(fields, column.value) as SelectOption[];
const value = options.find((opt) => opt.value === option.value);
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<SubMatchingSelectControlContainer cursor="pointer" id="control">
@@ -5,14 +5,15 @@ import {
type SpreadsheetMatchedSelectOptionsColumn,
} from '@/spreadsheet-import/types/SpreadsheetColumn';
import { type SpreadsheetMatchedOptions } from '@/spreadsheet-import/types/SpreadsheetMatchedOptions';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRowContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
justify-content: space-between;
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-bottom: ${themeCssVariables.spacing[1]};
`;
interface SubMatchingSelectRowProps {
@@ -1,18 +1,19 @@
import { SubMatchingSelectControlContainer } from '@/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelectControlContainer';
import { type SpreadsheetMatchedOptions } from '@/spreadsheet-import/types/SpreadsheetMatchedOptions';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-weight: ${({ theme }) => theme.font.weight.regular};
font-size: ${({ theme }) => theme.font.size.md};
color: ${themeCssVariables.font.color.primary};
font-weight: ${themeCssVariables.font.weight.regular};
font-size: ${themeCssVariables.font.size.md};
`;
const StyledControlLabel = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export type SubMatchingSelectRowLeftSelectProps = {
@@ -11,7 +11,7 @@ import {
import { type SpreadsheetMatchedOptions } from '@/spreadsheet-import/types/SpreadsheetMatchedOptions';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type SelectOption } from 'twenty-ui/input';
const StyledDropdownContainer = styled.div`
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MatchColumnToFieldSelect } from '@/spreadsheet-import/components/MatchColumnToFieldSelect';
import { DO_NOT_IMPORT_OPTION_KEY } from '@/spreadsheet-import/constants/DoNotImportOptionKey';
@@ -19,10 +20,10 @@ const StyledContainer = styled.div`
`;
const StyledErrorMessage = styled.span`
color: ${({ theme }) => theme.font.color.danger};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-top: ${({ theme }) => theme.spacing(1)};
color: ${themeCssVariables.font.color.danger};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.regular};
margin-top: ${themeCssVariables.spacing[1]};
`;
type TemplateColumnProps = {
@@ -5,7 +5,8 @@ import { type SpreadsheetImportFields } from '@/spreadsheet-import/types';
import { type SpreadsheetColumn } from '@/spreadsheet-import/types/SpreadsheetColumn';
import { type SpreadsheetColumns } from '@/spreadsheet-import/types/SpreadsheetColumns';
import { SpreadsheetColumnType } from '@/spreadsheet-import/types/SpreadsheetColumnType';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { t } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
@@ -41,9 +42,9 @@ const StyledContainer = styled.div`
const StyledContentWrapper = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
margin-top: ${({ theme }) => theme.spacing(4)};
padding-bottom: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[3]};
margin-top: ${themeCssVariables.spacing[4]};
padding-bottom: ${themeCssVariables.spacing[4]};
`;
export const UnmatchColumn = ({
@@ -1,43 +1,45 @@
import { useTheme } from '@emotion/react';
import isPropValid from '@emotion/is-prop-valid';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { Banner, IconChevronDown, IconInfoCircle } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledBanner = styled(Banner, {
shouldForwardProp: (prop) => isPropValid(prop) && prop !== 'allMatched',
})<{ allMatched: boolean }>`
background: ${({ allMatched, theme }) =>
allMatched ? theme.accent.secondary : theme.background.transparent.light};
border-radius: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2) + ' 10px'};
const StyledBanner = styled(Banner)<{ allMatched: boolean }>`
background: ${({ allMatched }) =>
allMatched
? themeCssVariables.accent.secondary
: themeCssVariables.background.transparent.light};
border-radius: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[2]} 10px;
`;
const StyledText = styled('div', {
shouldForwardProp: (prop) => isPropValid(prop) && prop !== 'allMatched',
})<{ allMatched: boolean }>`
color: ${({ allMatched, theme }) =>
allMatched ? theme.color.blue : theme.font.color.secondary};
const StyledText = styled.div<{ allMatched: boolean }>`
color: ${({ allMatched }) =>
allMatched
? themeCssVariables.color.blue
: themeCssVariables.font.color.secondary};
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledTransitionedIconChevronDown = styled(IconChevronDown, {
shouldForwardProp: (prop) =>
isPropValid(prop) && !['isExpanded', 'allMatched'].includes(prop),
})<{
const StyledIconChevronDownWrapper = styled.div<{
isExpanded: boolean;
allMatched: boolean;
}>`
color: ${({ allMatched, theme }) =>
allMatched ? theme.color.blue : theme.font.color.secondary};
align-items: center;
color: ${({ allMatched }) =>
allMatched
? themeCssVariables.color.blue
: themeCssVariables.font.color.secondary};
cursor: pointer;
display: flex;
transform: ${({ isExpanded }) =>
isExpanded ? 'rotate(180deg)' : 'rotate(0deg)'};
transition: ${({ theme }) =>
`transform ${theme.animation.duration.normal}s ease`};
cursor: pointer;
transition: transform
calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
`;
const StyledClickableContainer = styled.div`
@@ -61,7 +63,7 @@ export const UnmatchColumnBanner = ({
buttonOnClick?: () => void;
allMatched: boolean;
}) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<StyledBanner allMatched={allMatched}>
@@ -72,11 +74,12 @@ export const UnmatchColumnBanner = ({
{isDefined(buttonOnClick) ? (
<StyledClickableContainer onClick={buttonOnClick}>
<StyledText allMatched={allMatched}>{message}</StyledText>
<StyledTransitionedIconChevronDown
<StyledIconChevronDownWrapper
isExpanded={isExpanded}
allMatched={allMatched}
size={theme.icon.size.md}
/>
>
<IconChevronDown size={theme.icon.size.md} />
</StyledIconChevronDownWrapper>
</StyledClickableContainer>
) : (
<StyledText allMatched={allMatched}>{message}</StyledText>
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type ImportedRow } from '@/spreadsheet-import/types';
@@ -12,18 +13,18 @@ const StyledContainer = styled.div`
`;
const StyledValue = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.medium};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledExample = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.tertiary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useCallback, useState } from 'react';
import { Heading } from '@/spreadsheet-import/components/Heading';
@@ -15,7 +16,7 @@ import { useLingui } from '@lingui/react/macro';
import { SelectHeaderTable } from './components/SelectHeaderTable';
const StyledHeading = styled(Heading)`
margin-bottom: ${({ theme }) => theme.spacing(8)};
margin-bottom: ${themeCssVariables.spacing[8]};
`;
const StyledTableContainer = styled.div`
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useCallback, useState } from 'react';
import { Heading } from '@/spreadsheet-import/components/Heading';
@@ -12,6 +12,7 @@ import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { useLingui } from '@lingui/react/macro';
import { Radio, RadioGroup } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type WorkBook } from 'xlsx-ugnis';
const StyledContent = styled(Modal.Content)`
@@ -19,7 +20,7 @@ const StyledContent = styled(Modal.Content)`
display: flex;
flex-direction: column;
justify-content: center;
gap: ${({ theme }) => theme.spacing(8)};
gap: ${themeCssVariables.spacing[8]};
`;
const StyledHeading = styled(Heading)`
@@ -32,7 +33,7 @@ const StyledRadioContainer = styled.div`
`;
const StyledRadio = styled(Radio)`
margin-bottom: ${({ theme }) => theme.spacing(6)};
margin-bottom: ${themeCssVariables.spacing[6]};
`;
type SelectSheetStepProps = {
@@ -1,6 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useCallback, useState } from 'react';
import { styled } from '@linaria/react';
import { useCallback, useContext, useState } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@@ -31,7 +31,7 @@ export const SpreadsheetImportStepper = ({
nextStep,
prevStep,
}: SpreadsheetImportStepperProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { initialStepState } = useSpreadsheetImportInternal();
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useSpreadsheetImportInitialStep } from '@/spreadsheet-import/hooks/useSpreadsheetImportInitialStep';
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
@@ -10,18 +10,18 @@ import { spreadsheetImportDialogState } from '@/spreadsheet-import/states/spread
import { Modal } from '@/ui/layout/modal/components/Modal';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useLingui } from '@lingui/react/macro';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
import { SpreadsheetImportStepper } from './SpreadsheetImportStepper';
const StyledHeader = styled(Modal.Header)`
background-color: ${({ theme }) => theme.background.secondary};
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
padding: 0px ${({ theme }) => theme.spacing(30)};
background-color: ${themeCssVariables.background.secondary};
border-bottom: 1px solid ${themeCssVariables.border.color.medium};
padding: 0px ${themeCssVariables.spacing[30]};
height: 60px;
flex-shrink: 0;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(4)};
padding-right: ${({ theme }) => theme.spacing(4)};
padding-left: ${themeCssVariables.spacing[4]};
padding-right: ${themeCssVariables.spacing[4]};
}
`;
@@ -1,5 +1,6 @@
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useCallback, useState } from 'react';
import { type WorkBook } from 'xlsx-ugnis';
@@ -14,7 +15,7 @@ import { mapWorkbook } from '@/spreadsheet-import/utils/mapWorkbook';
import { DropZone } from './components/DropZone';
const StyledContent = styled(Modal.Content)`
padding: ${({ theme }) => theme.spacing(6)};
padding: ${themeCssVariables.spacing[6]};
`;
type UploadStepProps = {
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import { read, type WorkBook } from 'xlsx-ugnis';
@@ -14,40 +15,38 @@ import { MainButton } from 'twenty-ui/input';
const StyledContainer = styled.div`
align-items: center;
background: ${({ theme }) => `
repeating-linear-gradient(
background: repeating-linear-gradient(
0deg,
${theme.font.color.primary},
${theme.font.color.primary} 10px,
${themeCssVariables.font.color.primary},
${themeCssVariables.font.color.primary} 10px,
transparent 10px,
transparent 20px,
${theme.font.color.primary} 20px
${themeCssVariables.font.color.primary} 20px
),
repeating-linear-gradient(
90deg,
${theme.font.color.primary},
${theme.font.color.primary} 10px,
${themeCssVariables.font.color.primary},
${themeCssVariables.font.color.primary} 10px,
transparent 10px,
transparent 20px,
${theme.font.color.primary} 20px
${themeCssVariables.font.color.primary} 20px
),
repeating-linear-gradient(
180deg,
${theme.font.color.primary},
${theme.font.color.primary} 10px,
${themeCssVariables.font.color.primary},
${themeCssVariables.font.color.primary} 10px,
transparent 10px,
transparent 20px,
${theme.font.color.primary} 20px
${themeCssVariables.font.color.primary} 20px
),
repeating-linear-gradient(
270deg,
${theme.font.color.primary},
${theme.font.color.primary} 10px,
${themeCssVariables.font.color.primary},
${themeCssVariables.font.color.primary} 10px,
transparent 10px,
transparent 20px,
${theme.font.color.primary} 20px
${themeCssVariables.font.color.primary} 20px
);
`};
background-position:
0 0,
0 0,
@@ -59,7 +58,7 @@ const StyledContainer = styled.div`
100% 2px,
2px 100%,
100% 2px;
border-radius: ${({ theme }) => theme.border.radius.sm};
border-radius: ${themeCssVariables.border.radius.sm};
display: flex;
flex: 1;
flex-direction: column;
@@ -68,8 +67,8 @@ const StyledContainer = styled.div`
`;
const StyledOverlay = styled.div`
background: ${({ theme }) => theme.background.transparent.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
background: ${themeCssVariables.background.transparent.medium};
border-radius: ${themeCssVariables.border.radius.sm};
bottom: 0px;
left: 0px;
position: absolute;
@@ -78,20 +77,20 @@ const StyledOverlay = styled.div`
`;
const StyledText = styled.span`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
text-align: center;
padding: 16px;
`;
const StyledFooterText = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.regular};
color: ${themeCssVariables.font.color.tertiary};
font-size: ${themeCssVariables.font.size.xs};
font-weight: ${themeCssVariables.font.weight.regular};
text-align: center;
position: absolute;
bottom: ${({ theme }) => theme.spacing(4)};
bottom: ${themeCssVariables.spacing[4]};
left: 50%;
transform: translateX(-50%);
width: 100%;
@@ -101,7 +100,7 @@ const StyledButtonsContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
max-width: 200px;
width: 100%;
`;
@@ -13,7 +13,8 @@ import { SpreadsheetColumnType } from '@/spreadsheet-import/types/SpreadsheetCol
import { addErrorsAndRunHooks } from '@/spreadsheet-import/utils/dataMutations';
import { useDialogManager } from '@/ui/feedback/dialog-manager/hooks/useDialogManager';
import { Modal } from '@/ui/layout/modal/components/Modal';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { Trans, useLingui } from '@lingui/react/macro';
import {
type Dispatch,
@@ -37,10 +38,10 @@ const StyledContent = styled(Modal.Content)`
const StyledToolbar = styled.div`
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.md};
border: 1px solid ${({ theme }) => theme.border.color.medium};
background-color: ${({ theme }) => theme.background.secondary};
bottom: ${({ theme }) => theme.spacing(3)};
border-radius: ${themeCssVariables.border.radius.md};
border: 1px solid ${themeCssVariables.border.color.medium};
background-color: ${themeCssVariables.background.secondary};
bottom: ${themeCssVariables.spacing[3]};
display: flex;
flex-direction: row;
justify-content: space-between;
@@ -48,9 +49,9 @@ const StyledToolbar = styled.div`
position: absolute;
transform: translateX(-50%);
width: 400px;
padding: ${({ theme }) => theme.spacing(3)};
padding: ${themeCssVariables.spacing[3]};
z-index: 1;
box-shadow: ${({ theme }) => theme.boxShadow.strong};
box-shadow: ${themeCssVariables.boxShadow.strong};
`;
const StyledButton = styled(Button)`
@@ -64,10 +65,10 @@ const StyledErrorToggle = styled.div`
`;
const StyledErrorToggleDescription = styled.span`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.regular};
margin-left: ${({ theme }) => theme.spacing(2)};
color: ${themeCssVariables.font.color.secondary};
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.regular};
margin-left: ${themeCssVariables.spacing[2]};
`;
const StyledScrollContainer = styled.div`
@@ -82,11 +83,11 @@ const StyledNoRowsContainer = styled.div`
display: flex;
grid-column: 1/-1;
justify-content: center;
margin-top: ${({ theme }) => theme.spacing(8)};
margin-top: ${themeCssVariables.spacing[8]};
`;
const StyledNoRowsWithErrorsContainer = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
color: ${themeCssVariables.font.color.tertiary};
display: flex;
justify-content: center;
margin: auto 0;
@@ -1,5 +1,6 @@
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
// @ts-expect-error // Todo: remove usage of react-data-grid
import { type Column, useRowSelection } from 'react-data-grid';
import { createPortal } from 'react-dom';
@@ -19,7 +20,7 @@ import { type ImportedStructuredRowMetadata } from '@/spreadsheet-import/steps/c
const StyledHeaderContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
position: relative;
`;
@@ -52,7 +53,7 @@ const StyledInputContainer = styled.div`
display: flex;
min-height: 100%;
min-width: 100%;
padding-right: ${({ theme }) => theme.spacing(2)};
padding-right: ${themeCssVariables.spacing[2]};
`;
const StyledDefaultContainer = styled.div`
@@ -63,7 +64,7 @@ const StyledDefaultContainer = styled.div`
`;
const StyledSelectReadonlyValueContianer = styled.div`
padding-left: ${({ theme }) => theme.spacing(2)};
padding-left: ${themeCssVariables.spacing[2]};
`;
const SELECT_COLUMN_KEY = 'select-row';