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:
+10
-9
@@ -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 = {
|
||||
|
||||
+9
-8
@@ -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 = {
|
||||
|
||||
+23
-22
@@ -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 = {
|
||||
|
||||
+9
-8
@@ -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%;
|
||||
`;
|
||||
|
||||
|
||||
+6
-4
@@ -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">
|
||||
|
||||
+4
-3
@@ -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 {
|
||||
|
||||
+6
-5
@@ -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 = {
|
||||
|
||||
+1
-1
@@ -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`
|
||||
|
||||
+6
-5
@@ -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
-4
@@ -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 = ({
|
||||
|
||||
+31
-28
@@ -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>
|
||||
|
||||
+8
-7
@@ -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;
|
||||
|
||||
+3
-2
@@ -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`
|
||||
|
||||
+4
-3
@@ -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 = {
|
||||
|
||||
+4
-4
@@ -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();
|
||||
|
||||
|
||||
+7
-7
@@ -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]};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
+3
-2
@@ -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 = {
|
||||
|
||||
+26
-27
@@ -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%;
|
||||
`;
|
||||
|
||||
+14
-13
@@ -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;
|
||||
|
||||
+5
-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';
|
||||
// @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';
|
||||
|
||||
Reference in New Issue
Block a user