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:
Charles Bochet
2026-03-04 00:50:06 +01:00
committed by GitHub
parent 8a3b96d911
commit 7a2e397ad1
540 changed files with 3654 additions and 4156 deletions
+2 -2
View File
@@ -90,7 +90,7 @@ npx nx run twenty-front:graphql:generate --configuration=metadata
## Architecture Overview
### Tech Stack
- **Frontend**: React 18, TypeScript, Jotai (state management), Emotion (styling), Vite
- **Frontend**: React 18, TypeScript, Jotai (state management), Linaria (styling), Vite
- **Backend**: NestJS, TypeORM, PostgreSQL, Redis, GraphQL (with GraphQL Yoga)
- **Monorepo**: Nx workspace managed with Yarn 4
@@ -175,7 +175,7 @@ IMPORTANT: Use Context7 for code generation, setup or configuration steps, or li
5. Run `graphql:generate` after any GraphQL schema changes
### Code Style Notes
- Use **Emotion** for styling with styled-components pattern
- Use **Linaria** for styling with zero-runtime CSS-in-JS (styled-components pattern)
- Follow **Nx** workspace conventions for imports
- Use **Lingui** for internationalization
- Apply security first, then formatting (sanitize before format)
+1 -1
View File
@@ -109,7 +109,7 @@ Below are a few features we have implemented to date:
- [TypeScript](https://www.typescriptlang.org/)
- [Nx](https://nx.dev/)
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
- [React](https://reactjs.org/), with [Jotai](https://jotai.org/), [Emotion](https://emotion.sh/) and [Lingui](https://lingui.dev/)
- [React](https://reactjs.org/), with [Jotai](https://jotai.org/), [Linaria](https://linaria.dev/) and [Lingui](https://lingui.dev/)
-423
View File
@@ -1,423 +0,0 @@
# Emotion → Linaria Migration Plan: twenty-front
## Overview
Migrate all Emotion (`@emotion/styled`, `@emotion/react`) usages in
`packages/twenty-front/src` to Linaria (`@linaria/react`, `@linaria/core`),
following the same patterns already established in the `twenty-ui` package.
Linaria is a **zero-runtime** CSS-in-JS library. Styles are extracted at
build time by [wyw-in-js](https://wyw-in-js.dev/) (the Vite plugin is
`@wyw-in-js/vite`, already configured in `twenty-front/vite.config.ts`).
This means every expression inside a `styled` or `css` template literal
must be statically evaluable at build time — no runtime theme objects,
no closures over component state, no side-effects.
**Total files to migrate: ~998**
| Category | Files | Description |
|---|---|---|
| styled-only | 694 | Import `@emotion/styled` but not `useTheme` |
| styled + useTheme | 224 | Import both `@emotion/styled` and `useTheme` |
| useTheme-only | 79 | Import `useTheme` but not `@emotion/styled` |
| css / Global only | 1 | Import `css` or `Global` from `@emotion/react` only |
## Theme Architecture
Two build-time utilities produce the theme system:
- **`buildThemeReferencingRootCssVariables`** — walks the theme object and
builds a nested mirror where every leaf is a `var(--t-xxx)` string
(evaluated at build time by wyw-in-js)
- **`prepareThemeForRootCssVariableInjection`** — walks the runtime theme
and collects flat `[--css-variable-name, value]` pairs, injected onto
`document.documentElement` by `ThemeCssVariableInjectorEffect`
`themeCssVariables` is the build-time object; every leaf resolves to a CSS
`var()` reference. It is safe to use inside `styled` and `css` templates
because wyw-in-js can evaluate it statically.
## Migration Patterns
### 1. `styled` import
```diff
- import styled from '@emotion/styled';
+ import { styled } from '@linaria/react';
```
### 2. Theme access in styled components
Replace Emotion's `({ theme }) =>` prop-function pattern with static
`themeCssVariables` references:
```diff
+ import { themeCssVariables } from 'twenty-ui/theme';
const StyledTitle = styled.span`
- color: ${({ theme }) => theme.font.color.primary};
- font-size: ${({ theme }) => theme.font.size.lg};
+ color: ${themeCssVariables.font.color.primary};
+ font-size: ${themeCssVariables.font.size.lg};
`;
```
### 3. Spacing
`theme.spacing(N)` is a function; in Linaria it becomes an indexed lookup:
```diff
- margin-top: ${({ theme }) => theme.spacing(3)};
+ margin-top: ${themeCssVariables.spacing[3]};
```
For multi-arg spacing like `theme.spacing(2, 4)``"8px 16px"`:
```diff
- padding: ${({ theme }) => theme.spacing(2, 4)};
+ padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[4]};
```
The spacing scale covers integers 032 plus `0.5` and `1.5`. Any other
fractional values (`0.25`, `0.75`, `1.25`, `2.5`, `3.5`) must be replaced
with literal pixel values (e.g. `theme.spacing(2.5)``10px`).
### 4. `useTheme` → `useContext(ThemeContext)`
For runtime theme access (icon sizes, animation durations, conditional logic
outside of styled components):
```diff
- import { useTheme } from '@emotion/react';
+ import { useContext } from 'react';
+ import { ThemeContext } from 'twenty-ui/theme';
const MyComponent = () => {
- const theme = useTheme();
+ const { theme } = useContext(ThemeContext);
return <Icon size={theme.icon.size.sm} />;
};
```
### 5. `css` template literal
Linaria's `css` (from `@linaria/core`) returns a **class name string**, not
a serialized style object like Emotion's `css`. This has two consequences:
**Standalone usage** — apply via `className`, not the `css` prop:
```diff
- import { css } from '@emotion/react';
+ import { css } from '@linaria/core';
const myClass = css`
text-decoration: none;
`;
- <Link css={myClass} />
+ <Link className={myClass} />
```
**Inside `styled` templates** — do NOT nest `css` tags. Linaria's `css`
returns a class name, not raw CSS text, so interpolating it inside `styled`
produces broken output. Use plain strings instead:
```diff
// WRONG — css`` returns a class name, not CSS text
${({ handle }) =>
handle === 'left'
- ? css`left: ${themeCssVariables.spacing[1]};`
- : css`right: ${themeCssVariables.spacing[1]};`}
+ ? `left: ${themeCssVariables.spacing[1]};`
+ : `right: ${themeCssVariables.spacing[1]};`}
```
### 6. Interpolation return types
wyw-in-js requires prop interpolation functions to return `string | number`.
They must **never** return `false`, `undefined`, or `null`. Replace
short-circuit `&&` with ternary expressions:
```diff
// WRONG — returns false when condition is false
- ${({ isActive }) => isActive && `background: ${themeCssVariables.color.blue};`}
// CORRECT
+ ${({ isActive }) => isActive ? `background: ${themeCssVariables.color.blue};` : ''}
```
### 7. Block interpolations (multi-declaration returns)
Linaria wraps each interpolation result in a single CSS custom property
(`var(--xxx)`). An interpolation that returns **multiple CSS declarations**
produces invalid CSS. Split into one interpolation per property:
```diff
// WRONG — single interpolation returning multiple declarations
- ${({ divider, theme }) => {
- const border = `1px solid ${theme.border.color.light}`;
- return divider === 'left' ? `border-left: ${border}` : `border-right: ${border}`;
- }}
// CORRECT — one interpolation per property
+ border-left: ${({ divider }) =>
+ divider === 'left' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
+ border-right: ${({ divider }) =>
+ divider === 'right' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
```
### 8. CSS var + unit concatenation
CSS custom properties can't be concatenated with unit suffixes directly
(`var(--x)px` is invalid). Use `calc()` to attach units:
```diff
- transition: background ${themeCssVariables.animation.duration.instant}s ease;
+ transition: background calc(${themeCssVariables.animation.duration.instant} * 1s) ease;
```
### 9. `styled(Component)` requires `className`
Linaria's `styled(Component)` works by passing a generated `className` to
the wrapped component. The component **must** accept and forward a
`className` prop — otherwise the styles are silently lost. If the component
doesn't support it, either add `className` support or use a wrapper div.
Linaria also does **not** support Emotion's `shouldForwardProp` option.
Custom props on HTML elements are automatically filtered by Linaria's
runtime (via `@emotion/is-prop-valid`). For custom components, all props are
forwarded — ensure the wrapped component ignores unknown props gracefully.
### 10. `type Theme` → `type ThemeType`
```diff
- import { type Theme } from '@emotion/react';
+ import { type ThemeType } from 'twenty-ui/theme';
```
### 11. Framer Motion integration
Linaria doesn't support `styled(motion.div)` — wrapping a motion element
with `styled()` causes the component body to be stripped at build time by
wyw-in-js. Define the styled component first, then wrap with
`motion.create()`:
```tsx
const StyledBarBase = styled.div`
background-color: ${themeCssVariables.font.color.primary};
height: 100%;
`;
const StyledBar = motion.create(StyledBarBase);
```
### 12. Dynamic styles via CSS variables
When a component needs to compute styles from multiple props with complex
branching logic (e.g. combining `variant`, `accent`, `disabled`, `focus`),
Linaria's prop interpolations become unwieldy. Use a `computeDynamicStyles`
helper that returns a `CSSProperties` object injected via `style={}`,
referenced from the static CSS with `var()`:
```tsx
const StyledButton = styled.button`
background: var(--btn-bg);
border-color: var(--btn-border-color);
&:hover { background: var(--btn-hover-bg); }
`;
const dynamicStyles = useMemo(() => {
const s = computeButtonDynamicStyles(variant, accent, ...);
return {
'--btn-bg': s.background,
'--btn-hover-bg': s.hoverBackground,
} as CSSProperties;
}, [variant, accent, ...]);
return <StyledButton style={dynamicStyles} />;
```
### 13. `Global` component
Replace Emotion's `<Global styles={...} />` with standard CSS or the
`ThemeCssVariableInjectorEffect` pattern from twenty-ui.
### 14. `ThemeProvider`
The `BaseThemeProvider` already wraps children with both Emotion's
`ThemeProvider` and Linaria's `ThemeContextProvider`. Once all Emotion usages
are gone, the Emotion `ThemeProvider` wrapper can be removed.
---
## PR Breakdown
Files are grouped to keep each PR around ~100 files with consistent review
surface. We start with the simplest, lowest-risk modules.
### PR 1 (~97 files) — Small standalone modules
Low-risk modules with mostly simple `styled`-only patterns.
| Module | Files |
|---|---|
| spreadsheet-import | 28 |
| billing | 10 |
| views | 14 |
| navigation-menu-item | 14 |
| blocknote-editor | 7 |
| advanced-text-editor | 7 |
| favorites | 7 |
| navigation | 4 |
| information-banner | 3 |
| sign-in-background-mock | 3 |
### PR 2 (~100 files) — Auth, tiny modules, loading, testing, pages (part 1)
| Module | Files |
|---|---|
| auth | 19 |
| action-menu | 3 |
| object-metadata | 3 |
| onboarding | 2 |
| workspace | 2 |
| file | 2 |
| error-handler | 2 |
| front-components | 1 |
| geo-map | 1 |
| hooks | 1 |
| loading | 5 |
| testing | 5 |
| pages (first ~55 files) | ~55 |
### PR 3 (~97 files) — Pages (remaining) + activities + AI
| Module | Files |
|---|---|
| pages (remaining ~16 files) | ~16 |
| activities | 53 |
| ai | 28 |
### PR 4 (~100 files) — Command-menu + workflow (part 1)
| Module | Files |
|---|---|
| command-menu | 53 |
| workflow (first ~47 files) | ~47 |
### PR 5 (~115 files) — Workflow (remaining) + page-layout
| Module | Files |
|---|---|
| workflow (remaining ~32 files) | ~32 |
| page-layout | 83 |
### PR 6 (~100 files) — UI module (part 1)
| Module | Files |
|---|---|
| ui (first ~100 files) | ~100 |
### PR 7 (~85 files) — UI module (remaining) + object-record (start)
| Module | Files |
|---|---|
| ui (remaining ~23 files) | ~23 |
| object-record (first ~62 files) | ~62 |
### PR 8 (~100 files) — Object-record (continued)
| Module | Files |
|---|---|
| object-record (next ~100 files) | ~100 |
### PR 9 (~100 files) — Settings (part 1)
| Module | Files |
|---|---|
| settings (first ~100 files) | ~100 |
### PR 10 (~102 files) — Settings (part 2) + final cleanup
| Module | Files |
|---|---|
| settings (remaining ~102 files) | ~102 |
| css/Global-only file | 1 |
### Post-migration PR — Remove Emotion
Once all PRs are merged:
- Remove `ThemeProvider` from `@emotion/react` in `BaseThemeProvider`
- Remove `@emotion/styled` and `@emotion/react` dependencies
- Remove `@styled/typescript-styled-plugin` from tsconfig
- Clean up any remaining Emotion-related configuration
---
## Risk Assessment
| Risk | Mitigation |
|---|---|
| wyw-in-js evaluates at build time; dynamic expressions may fail | Use `themeCssVariables` for static theme values; pass dynamic values as component props or via `style={}` CSS variables |
| `theme.spacing(N)` function → `themeCssVariables.spacing[N]` index | Pre-computed for integers 032 plus 0.5 and 1.5; other fractional values → literal pixel values |
| `useTheme` used for runtime logic (not just styles) | Replace with `useContext(ThemeContext)`, destructure `{ theme }` |
| Multi-arg `theme.spacing(a, b, c)` | Split into individual `themeCssVariables.spacing[N]` references |
| `css` tag inside `styled` templates | Linaria `css` returns class name, not CSS text; use plain strings inside `styled` |
| Interpolation returns `false` / `undefined` | wyw-in-js requires `string \| number`; use ternary `? : ''` instead of `&&` |
| Block interpolations (multiple declarations) | Split into one interpolation per CSS property |
| `var(--x)px` concatenation | Use `calc(var(--x) * 1px)` |
| `styled(motion.div)` stripped by wyw-in-js | Use `motion.create(StyledBase)` pattern |
| `styled(Component)` with no `className` prop | Add `className` support to wrapped component or use wrapper div |
| Complex multi-prop style branching | Use `computeDynamicStyles` + `style={}` + `var()` references |
---
## Known Issues & TODOs
### `styled(Component)` from `twenty-ui` — style overrides silently dropped
When `twenty-front` uses `styled(SomeComponent)` to extend a pre-built
component from `twenty-ui` (e.g. `Card`, `CardContent`, `PropertyBox`,
`Chip`, `Pill`, `Avatar`, `StyledHoverableMenuItemBase`, `MenuItemLeftContent`,
`TabList`, `NavigationDrawerSection`), the style overrides are silently
ignored at runtime. This happens because `wyw-in-js` in `twenty-front`
cannot resolve the base class name of a component that was already compiled
in another package.
**Current workaround:** Replace `styled(Component)` with a plain
`styled.div` (or wrapper div) that duplicates the needed base styles.
This fixes the visual regression but loses the component's built-in
behavior (e.g. `CardContent`'s framer-motion animation, `PropertyBox`'s
layout-context padding logic, `Card`'s border/overflow).
**Proper fix (TODO):** Add customization props directly to the base
components so consumers can override styles without wrapping:
- `Card` / `CardContent` — add `padding`, `backgroundColor`, `borderColor` props
- `PropertyBox` — add `padding`, `height`, `noPadding` props
- Other frequently extended components — audit and add props as needed
**Affected files (using workaround today):**
- `CalendarEventDetails.tsx``styled(PropertyBox)``styled.div`, `styled(Chip)` → wrapper
- `CalendarEventParticipantsResponseStatusField.tsx``styled(PropertyBox)``styled.div`
- `CalendarEventNotSharedContent.tsx``styled(Card)` / `styled(CardContent)``styled.div`
- `CalendarDayCardContent.tsx``styled(CardContent)``styled(motion.div)`
- `ActivityRow.tsx``styled(CardContent)``styled.div`
- `EmailThreadPreview.tsx``styled(Avatar)` → wrapper div
- `SignInAppNavigationDrawerMock.tsx``styled(NavigationDrawerSection)` → wrapper div
- `DefaultLayout.tsx``styled(SignInAppNavigationDrawerMock)` → wrapper div
- `LastUsedPill.tsx``styled(Pill)` → wrapper span
- `Logo.tsx` — dynamic `background-image` via inline `style` instead of prop
- `DatePicker.tsx` / `DateTimePicker.tsx``styled(StyledHoverableMenuItemBase)` / `styled(MenuItemLeftContent)``styled.div`
- `SelectControl.tsx` / `MultiSelectControl.tsx``styled(IconChevronDown)` → wrapper div
---
## Validation Checklist (per PR)
- [ ] `npx nx lint:diff-with-main twenty-front` passes
- [ ] `npx nx typecheck twenty-front` passes
- [ ] `npx nx test twenty-front` passes
- [ ] Visual spot-check of affected components in the app
- [ ] No remaining `@emotion/styled` or `@emotion/react` imports in migrated files
+3 -3
View File
@@ -276,7 +276,7 @@
"generators": {
"@nx/react": {
"application": {
"style": "@emotion/styled",
"style": "@linaria/react",
"linter": "eslint",
"bundler": "vite",
"compiler": "swc",
@@ -284,7 +284,7 @@
"projectNameAndRootFormat": "derived"
},
"library": {
"style": "@emotion/styled",
"style": "@linaria/react",
"linter": "eslint",
"bundler": "vite",
"compiler": "swc",
@@ -292,7 +292,7 @@
"projectNameAndRootFormat": "derived"
},
"component": {
"style": "@emotion/styled"
"style": "@linaria/react"
}
}
},
+2 -2
View File
@@ -2,14 +2,13 @@
"private": true,
"dependencies": {
"@apollo/client": "^3.7.17",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@floating-ui/react": "^0.24.3",
"@linaria/core": "^6.2.0",
"@linaria/react": "^6.2.1",
"@radix-ui/colors": "^3.0.0",
"@sniptt/guards": "^0.2.0",
"@tabler/icons-react": "^3.31.0",
"@wyw-in-js/babel-preset": "^1.0.6",
"@wyw-in-js/vite": "^0.7.0",
"archiver": "^7.0.1",
"danger-plugin-todos": "^1.3.1",
@@ -41,6 +40,7 @@
"lodash.snakecase": "^4.1.1",
"lodash.upperfirst": "^4.3.1",
"microdiff": "^1.3.2",
"next-with-linaria": "^1.3.0",
"planer": "^1.2.0",
"pluralize": "^8.0.0",
"react": "^18.2.0",
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Use StyledComponents
Style the components with [styled-components](https://emotion.sh/docs/styled).
Style the components with [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
@@ -136,7 +136,7 @@ That's expected as user is unauthorized when logged out since its identity is no
Comment out checker plugin in `packages/twenty-ui/vite-config.ts` like in example below
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### استخدام مكونات منسقة
قم بتنسيق المكونات باستخدام [styled-components](https://emotion.sh/docs/styled).
قم بتنسيق المكونات باستخدام [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ سيء
@@ -145,7 +145,7 @@ npx nx worker twenty-server
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -67,11 +67,11 @@ const EmailField: React.FC<{
```
```tsx
/* ✅ - Dobré, explicitně definován samostatný typ (OwnProps) pro
/* ✅ - Dobré, explicitně definován samostatný typ (OwnProps) pro
* rekvizity komponenty
* - Tato metoda automaticky nezahrnuje rekvizitu children. Pokud
* ji chcete zahrnout, musíte ji specifikovat v OwnProps.
*/
*/
type EmailFieldProps = {
value: string;
};
@@ -96,7 +96,7 @@ const MyComponent = (props: OwnProps) => {
```tsx
/* ✅ - Dobré, explicitně uvádí všechny rekvizity
* - Zvyšuje čitelnost a udržovatelnost
*/
*/
const MyComponent = ({ prop1, prop2, prop3 }: MyComponentProps) => {
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
};
@@ -123,7 +123,7 @@ const value = process.env.MY_VALUE ?? 'default';
### Používejte volitelné zřetězení `?.`
```tsx
// ❌ Špatné
// ❌ Špatné
onClick && onClick();
// ✅ Dobré
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Používejte StyledComponents
Styling komponenty s [styled-components](https://emotion.sh/docs/styled).
Styling komponenty s [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Špatné
@@ -146,7 +146,7 @@ Zakomentujte plugin checker v `packages/twenty-ui/vite-config.ts` jako v příkl
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Verwenden Sie StyledComponents
Stylen Sie die Komponenten mit [styled-components](https://emotion.sh/docs/styled).
Stylen Sie die Komponenten mit [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Schlecht
@@ -145,7 +145,7 @@ Kommentieren Sie das Checker-Plugin in `packages/twenty-ui/vite-config.ts` wie i
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Usar StyledComponents
Estiliza los componentes con [styled-components](https://emotion.sh/docs/styled).
Estiliza los componentes con [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
@@ -145,7 +145,7 @@ Comente el plugin checker en `packages/twenty-ui/vite-config.ts` como en el ejem
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Utilisez StyledComponents
Styliser les composants avec [styled-components](https://emotion.sh/docs/styled).
Styliser les composants avec [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
@@ -145,7 +145,7 @@ Commentez le plugin de vérification dans `packages/twenty-ui/vite-config.ts` co
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Usa StyledComponents
Stile i componenti con [styled-components](https://emotion.sh/docs/styled).
Stile i componenti con [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Male
@@ -146,7 +146,7 @@ Commenta il plugin checker in `packages/twenty-ui/vite-config.ts` come mostrato
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### StyledComponentsを使用する
コンポーネントを[styled-components](https://emotion.sh/docs/styled)でスタイル設定する。
コンポーネントを[Linaria styled](https://github.com/callstack/linaria)でスタイル設定する。
```tsx
// ❌ Bad
@@ -152,7 +152,7 @@ npx nx worker twenty-server
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### StyledComponents 사용
[styled-components](https://emotion.sh/docs/styled)로 구성 요소를 스타일링하십시오.
[Linaria styled](https://github.com/callstack/linaria)로 구성 요소를 스타일링하십시오.
```tsx
// ❌ Bad
@@ -145,7 +145,7 @@ npx nx worker twenty-server
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Use StyledComponents
Estilize os componentes com [styled-components](https://emotion.sh/docs/styled).
Estilize os componentes com [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Bad
@@ -145,7 +145,7 @@ Comente o plugin checker em `packages/twenty-ui/vite-config.ts` como no exemplo
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Folosește ComponentaStilizată
Stilizează componentele cu [styled-components](https://emotion.sh/docs/styled).
Stilizează componentele cu [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Rău
@@ -146,7 +146,7 @@ Comentați pluginul checker în `packages/twenty-ui/vite-config.ts`, ca în exem
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### Использование StyledComponents
Стилизуйте компоненты с помощью [styled-components](https://emotion.sh/docs/styled).
Стилизуйте компоненты с помощью [Linaria styled](https://github.com/callstack/linaria).
```tsx
// ❌ Плохо
@@ -146,7 +146,7 @@ npx nx worker twenty-server
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -68,7 +68,7 @@ const EmailField: React.FC<{
```tsx
/* ✅ - İyi, bileşenin prop'ları için ayrı bir tür (OwnProps) açıkça tanımlanmıştır
* - Bu yöntem çocuk prop'unu otomatik olarak içermez.
* - Bu yöntem çocuk prop'unu otomatik olarak içermez.
* Onu dahil etmek istiyorsanız, OwnProps'ta belirtmeniz gerekir.
*/
type EmailFieldProps = {
@@ -193,7 +193,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### StyledComponents kullanın
Bileşenleri [styled-components](https://emotion.sh/docs/styled) ile stillendirin.
Bileşenleri [Linaria styled](https://github.com/callstack/linaria) ile stillendirin.
```tsx
// ❌ Kötü
@@ -145,7 +145,7 @@ Aşağıdaki örnekte olduğu gibi `packages/twenty-ui/vite-config.ts` içindeki
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
@@ -194,7 +194,7 @@ setHotkeyScopeAndMemorizePreviousScope(
### 使用StyledComponents
使用[styled-components](https://emotion.sh/docs/styled)对组件进行样式化。
使用[Linaria styled](https://github.com/callstack/linaria)对组件进行样式化。
```tsx
// ❌ Bad
@@ -145,7 +145,7 @@ npx nx worker twenty-server
```
plugins: [
react({ jsxImportSource: '@emotion/react' }),
react({ jsxImportSource: 'react' }),
tsconfigPaths(),
svgr(),
dts(dtsConfig),
+7 -10
View File
@@ -1,4 +1,3 @@
import { ThemeProvider } from '@emotion/react';
import { i18n } from '@lingui/core';
import { I18nProvider } from '@lingui/react';
import { type Preview } from '@storybook/react-vite';
@@ -71,15 +70,13 @@ const preview: Preview = {
return (
<I18nProvider i18n={i18n}>
<ThemeProvider theme={theme}>
<ThemeContextProvider theme={theme}>
<ClickOutsideListenerContext.Provider
value={{ excludedClickOutsideId: undefined }}
>
<Story />
</ClickOutsideListenerContext.Provider>
</ThemeContextProvider>
</ThemeProvider>
<ThemeContextProvider theme={theme}>
<ClickOutsideListenerContext.Provider
value={{ excludedClickOutsideId: undefined }}
>
<Story />
</ClickOutsideListenerContext.Provider>
</ThemeContextProvider>
</I18nProvider>
);
},
-5
View File
@@ -1,5 +0,0 @@
import { type ThemeType } from 'twenty-ui/theme';
declare module '@emotion/react' {
export interface Theme extends ThemeType {}
}
-2
View File
@@ -1,7 +1,5 @@
import ReactDOM from 'react-dom/client';
import '@emotion/react';
import { App } from '@/app/components/App';
import 'react-loading-skeleton/dist/skeleton.css';
import 'twenty-ui/style.css';
@@ -2,8 +2,8 @@ import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLo
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { BORDER_COMMON, 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 StyledMainContainer = styled.div`
background: ${themeCssVariables.background.noisy};
@@ -25,7 +25,7 @@ const StyledMainContainer = styled.div`
const StyledPanel = styled.div`
background: ${themeCssVariables.background.primary};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${BORDER_COMMON.radius.md};
border-radius: ${themeCssVariables.border.radius.md};
height: 100%;
overflow: auto;
width: 100%;
@@ -3,8 +3,7 @@ import { styled } from '@linaria/react';
import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal';
import { Modal } from '@/ui/layout/modal/components/Modal';
import { NAVIGATION_DRAWER_CONSTRAINTS } from '@/ui/layout/resizable-panel/constants/NavigationDrawerConstraints';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonLoader';
import { RightPanelSkeletonLoader } from '~/loading/components/RightPanelSkeletonLoader';
@@ -4,7 +4,7 @@ import { differenceInSeconds, endOfDay, format } from 'date-fns';
import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow';
import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate';
import { motion } from 'framer-motion';
import { CardContent } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { type TimelineCalendarEvent } from '~/generated/graphql';
@@ -14,19 +14,14 @@ type CalendarDayCardContentProps = {
divider?: boolean;
};
const StyledCardContentBase = styled.div<{ divider?: boolean }>`
const StyledCardContent = styled(CardContent)`
align-items: flex-start;
background-color: ${themeCssVariables.background.secondary};
border-bottom: ${({ divider }) =>
divider ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
display: flex;
flex-direction: row;
gap: ${themeCssVariables.spacing[3]};
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
`;
const StyledCardContent = motion.create(StyledCardContentBase);
const StyledDayContainer = styled.div`
text-align: center;
width: ${themeCssVariables.spacing[6]};
@@ -32,6 +32,7 @@ import {
} from 'twenty-ui/components';
import { IconCalendarEvent } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
type CalendarEventDetailsProps = {
@@ -84,10 +85,7 @@ const StyledFields = styled.div`
width: 100%;
`;
const StyledPropertyBox = styled.div`
align-self: stretch;
display: flex;
flex-direction: column;
const StyledPropertyBox = styled(PropertyBox)`
height: ${themeCssVariables.spacing[6]};
width: 100%;
`;
@@ -2,20 +2,19 @@ import { useContext } from 'react';
import { styled } from '@linaria/react';
import { Trans } from '@lingui/react/macro';
import { IconLock } from 'twenty-ui/display';
import { Card, CardContent } from 'twenty-ui/layout';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledVisibilityCard = styled.div`
border: 1px solid ${themeCssVariables.border.color.light};
border-radius: ${themeCssVariables.border.radius.sm};
const StyledVisibilityCard = styled(Card)`
color: ${themeCssVariables.font.color.light};
flex: 1;
overflow: hidden;
border-color: ${themeCssVariables.border.color.light};
transition: color ${themeCssVariables.animation.duration.normal} ease;
width: 100%;
`;
const StyledVisibilityCardContent = styled.div`
const StyledVisibilityCardContent = styled(CardContent)`
align-items: center;
background-color: ${themeCssVariables.background.transparent.lighter};
box-sizing: border-box;
@@ -2,6 +2,7 @@ import { useContext, useRef } from 'react';
import { styled } from '@linaria/react';
import { type CalendarEventParticipant } from '@/activities/calendar/types/CalendarEventParticipant';
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
import { ParticipantChip } from '@/activities/components/ParticipantChip';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
@@ -21,10 +22,7 @@ const StyledInlineCellBaseContainer = styled.div`
user-select: none;
`;
const StyledPropertyBox = styled.div`
align-self: stretch;
display: flex;
flex-direction: column;
const StyledPropertyBox = styled(PropertyBox)`
height: ${themeCssVariables.spacing[6]};
width: 100%;
`;
@@ -1,15 +1,18 @@
import { styled } from '@linaria/react';
import React from 'react';
import { CardContent } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRowContent = styled.div<{ isClickable: boolean }>`
const StyledRowContent = styled(CardContent)`
align-items: center;
background-color: ${themeCssVariables.background.secondary};
cursor: ${({ isClickable }) => (isClickable ? 'pointer' : 'default')};
display: flex;
gap: ${themeCssVariables.spacing[2]};
height: ${themeCssVariables.spacing[12]};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[4]};
&[data-clickable='false'] {
cursor: default;
}
`;
export const ActivityRow = ({
@@ -15,8 +15,7 @@ import {
AnimatedPlaceholderEmptyTitle,
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
} from 'twenty-ui/layout';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
const StyledMainContainer = styled.div`
align-items: flex-start;
@@ -12,8 +12,7 @@ import { type CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectN
import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache';
import { isNonEmptyString } from '@sniptt/guards';
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
type EventRowActivityProps = EventRowDynamicComponentProps;
@@ -1,7 +1,6 @@
import { styled } from '@linaria/react';
import { Card } from 'twenty-ui/layout';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
type EventCardProps = {
children: React.ReactNode;
@@ -6,8 +6,7 @@ import {
import { EventRowMainObjectUpdated } from '@/activities/timeline-activities/rows/main-object/components/EventRowMainObjectUpdated';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
type EventRowMainObjectProps = EventRowDynamicComponentProps;
@@ -9,8 +9,7 @@ import { EventFieldDiffContainer } from '@/activities/timeline-activities/rows/m
import { type TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity';
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
type EventRowMainObjectUpdatedProps = {
mainObjectMetadataItem: ObjectMetadataItem;
@@ -0,0 +1,13 @@
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledCommandMenuContextChipIconWrapper = styled.div`
align-items: center;
background: ${themeCssVariables.background.primary};
border-radius: ${themeCssVariables.border.radius.sm};
display: flex;
justify-content: center;
`;
export const CommandMenuContextChipIconWrapper =
StyledCommandMenuContextChipIconWrapper;
@@ -12,8 +12,7 @@ import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomStat
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
export type CommandMenuListProps = {
commandGroups: ActionGroupConfig[];
@@ -1,28 +1,19 @@
import { CommandMenuContextChipIconWrapper } from '@/command-menu/components/CommandMenuContextChipIconWrapper';
import { CommandMenuContextRecordChipAvatars } from '@/command-menu/components/CommandMenuContextRecordChipAvatars';
import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistory';
import { commandMenuNavigationMorphItemsByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsByPageState';
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
import { CommandMenuPages } from 'twenty-shared/types';
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { recordStoreIdentifiersFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreIdentifiersSelector';
import { recordStoreRecordsSelector } from '@/object-record/record-store/states/selectors/recordStoreRecordsSelector';
import { styled } from '@linaria/react';
import { useContext, useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext, useMemo } from 'react';
import { CommandMenuPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme';
const StyledIconWrapper = styled.div`
background: ${themeCssVariables.background.primary};
border-radius: ${themeCssVariables.border.radius.sm};
display: flex;
align-items: center;
justify-content: center;
`;
export const useCommandMenuContextChips = () => {
const commandMenuNavigationStack = useAtomStateValue(
commandMenuNavigationStackState,
@@ -122,7 +113,7 @@ export const useCommandMenuContextChips = () => {
Icons: isLastChip
? [<page.pageIcon size={theme.icon.size.sm} />]
: [
<StyledIconWrapper>
<CommandMenuContextChipIconWrapper>
<page.pageIcon
size={theme.icon.size.sm}
color={
@@ -132,7 +123,7 @@ export const useCommandMenuContextChips = () => {
: theme.font.color.tertiary
}
/>
</StyledIconWrapper>,
</CommandMenuContextChipIconWrapper>,
],
text: page.pageTitle,
onClick: isLastChip
@@ -1,10 +1,11 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledColumn = styled.div`
display: flex;
flex-direction: column;
width: 100%;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const AdvancedFilterCommandMenuColumn = StyledColumn;
@@ -9,20 +9,21 @@ import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/adv
import { isRecordFilterGroupChildARecordFilterGroup } from '@/object-record/advanced-filter/utils/isRecordFilterGroupChildARecordFilterGroup';
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: start;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledChildContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(6)};
gap: ${themeCssVariables.spacing[6]};
width: 100%;
`;
@@ -6,23 +6,24 @@ import { type RecordFilterGroup } from '@/object-record/record-filter-group/type
import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { capitalize } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledText = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
display: flex;
height: ${({ theme }) => theme.spacing(8)};
height: ${themeCssVariables.spacing[8]};
`;
const StyledContainer = styled.div`
align-items: start;
display: flex;
min-width: ${({ theme }) => theme.spacing(20)};
color: ${({ theme }) => theme.font.color.tertiary};
min-width: ${themeCssVariables.spacing[20]};
color: ${themeCssVariables.font.color.tertiary};
`;
type AdvancedFilterCommandMenuLogicalOperatorCellProps = {
@@ -11,14 +11,15 @@ import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/obje
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
type AdvancedFilterCommandMenuRecordFilterColumnProps = {
@@ -3,21 +3,24 @@ import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filt
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
align-items: start;
background-color: ${({ theme, isGrayBackground }) =>
isGrayBackground ? theme.background.transparent.lighter : 'transparent'};
border: ${({ theme }) => `1px solid ${theme.border.color.medium}`};
border-radius: ${({ theme }) => theme.border.radius.md};
background-color: ${({ isGrayBackground }) =>
isGrayBackground
? themeCssVariables.background.transparent.lighter
: 'transparent'};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.md};
display: flex;
flex: 1;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(6)};
padding: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[6]};
padding: ${themeCssVariables.spacing[2]};
`;
type AdvancedFilterCommandMenuRecordFilterGroupChildrenProps = {
@@ -5,14 +5,15 @@ import { AdvancedFilterRecordFilterGroupOptionsDropdown } from '@/object-record/
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const AdvancedFilterCommandMenuRecordFilterGroupColumn = ({
@@ -1,9 +1,10 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRow = styled.div`
display: flex;
width: 100%;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const AdvancedFilterDropdownRow = StyledRow;
@@ -4,7 +4,7 @@ import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advance
import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledContainer = styled.div`
flex: 2;
@@ -1,23 +1,24 @@
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { themeCssVariables } from 'twenty-ui/theme-constants';
export const StyledInput = styled.input`
background: transparent;
border: none;
border-top: none;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-bottom: 1px solid ${themeCssVariables.border.color.light};
border-radius: 0;
border-top-left-radius: ${({ theme }) => theme.border.radius.md};
border-top-right-radius: ${({ theme }) => theme.border.radius.md};
color: ${({ theme }) => theme.font.color.primary};
border-top-left-radius: ${themeCssVariables.border.radius.md};
border-top-right-radius: ${themeCssVariables.border.radius.md};
color: ${themeCssVariables.font.color.primary};
margin: 0;
outline: none;
padding: ${({ theme }) => theme.spacing(2)};
padding: ${themeCssVariables.spacing[2]};
min-height: 19px;
font-family: inherit;
font-size: ${({ theme }) => theme.font.size.sm};
font-size: ${themeCssVariables.font.size.sm};
font-weight: inherit;
max-width: 100%;
@@ -25,7 +26,7 @@ export const StyledInput = styled.input`
text-decoration: none;
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
color: ${themeCssVariables.font.color.light};
}
`;
@@ -1,23 +1,24 @@
import { AdvancedFilterLogicalOperatorDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { capitalize } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledText = styled.div`
height: ${({ theme }) => theme.spacing(8)};
height: ${themeCssVariables.spacing[8]};
display: flex;
align-items: center;
padding-left: ${({ theme }) => theme.spacing(2.25)};
padding-left: 9px;
`;
const StyledContainer = styled.div`
align-items: start;
display: flex;
min-width: ${({ theme }) => theme.spacing(20)};
color: ${({ theme }) => theme.font.color.tertiary};
min-width: ${themeCssVariables.spacing[20]};
color: ${themeCssVariables.font.color.tertiary};
`;
type AdvancedFilterLogicalOperatorCellProps = {
@@ -2,20 +2,23 @@ import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filt
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
align-items: start;
background-color: ${({ theme, isGrayBackground }) =>
isGrayBackground ? theme.background.transparent.lighter : 'transparent'};
border: ${({ theme }) => `1px solid ${theme.border.color.medium}`};
border-radius: ${({ theme }) => theme.border.radius.md};
background-color: ${({ isGrayBackground }) =>
isGrayBackground
? themeCssVariables.background.transparent.lighter
: 'transparent'};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.md};
display: flex;
flex: 1;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[2]};
`;
type AdvancedFilterRecordFilterGroupChildrenProps = {
@@ -6,7 +6,7 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
@@ -11,16 +11,17 @@ import { isRecordFilterGroupChildARecordFilterGroup } from '@/object-record/adva
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: start;
display: flex;
flex: 1;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[2]};
`;
export const AdvancedFilterRootRecordFilterGroup = () => {
@@ -14,7 +14,7 @@ import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
const StyledValueDropdownContainer = styled.div`
@@ -5,26 +5,27 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
import { isNonEmptyString } from '@sniptt/guards';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
import { useGetRecordFilterDisplayValue } from '@/object-record/record-filter/hooks/useGetRecordFilterDisplayValue';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
// TODO: factorize this with https://github.com/twentyhq/core-team-issues/issues/752
const StyledControlContainer = styled.div`
display: flex;
align-items: center;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
box-sizing: border-box;
height: ${({ theme }) => theme.spacing(8)};
height: ${themeCssVariables.spacing[8]};
max-width: 100%;
padding: 0 ${({ theme }) => theme.spacing(2)};
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
padding: 0 ${themeCssVariables.spacing[2]};
background-color: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.sm};
color: ${themeCssVariables.font.color.primary};
cursor: pointer;
text-align: left;
`;
@@ -2,9 +2,10 @@ import { CommandMenuForMobile } from '@/command-menu/components/CommandMenuForMo
import { CommandMenuSidePanelForDesktop } from '@/command-menu/components/CommandMenuSidePanelForDesktop';
import { useCommandMenuHotKeys } from '@/command-menu/hooks/useCommandMenuHotKeys';
import { PageBody } from '@/ui/layout/page/components/PageBody';
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';
type MainContainerLayoutWithCommandMenuProps = {
children: ReactNode;
@@ -14,8 +15,8 @@ const StyledMainContainerLayoutForDesktop = styled.div`
display: flex;
flex: 1;
min-height: 0;
padding-bottom: ${({ theme }) => theme.spacing(3)};
padding-right: ${({ theme }) => theme.spacing(3)};
padding-bottom: ${themeCssVariables.spacing[3]};
padding-right: ${themeCssVariables.spacing[3]};
`;
const StyledPageBodyForDesktop = styled(PageBody)`
@@ -35,9 +36,9 @@ const StyledMainContainerLayoutForMobile = styled.div`
const StyledPageBodyForMobile = styled(PageBody)`
padding-bottom: 0;
padding-left: ${({ theme }) => theme.spacing(1)};
padding-left: ${themeCssVariables.spacing[1]};
padding-right: ${({ theme }) => theme.spacing(1.5)};
padding-right: ${themeCssVariables.spacing['1.5']};
`;
export const MainContainerLayoutWithCommandMenu = ({
@@ -1,6 +1,8 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
@@ -16,12 +18,12 @@ const StyledBooleanSelectContainer = styled.div<{ selected?: boolean }>`
align-items: center;
cursor: pointer;
display: flex;
padding: ${({ theme }) =>
`${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing(1)}`};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]}
${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[1]};
border-radius: ${themeCssVariables.border.radius.sm};
color: ${themeCssVariables.font.color.primary};
&:hover {
background: ${({ theme }) => theme.background.transparent.light};
background: ${themeCssVariables.background.transparent.light};
}
`;
@@ -32,7 +34,7 @@ const StyledIconCheckContainer = styled.div`
`;
export const ObjectFilterDropdownBooleanSelect = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const options = [true, false];
const { objectFilterDropdownFilterValue } =
@@ -1,13 +1,14 @@
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue';
import { RatingInput } from '@/ui/field/input/components/RatingInput';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { RATING_VALUES } from 'twenty-shared/constants';
import { type FieldRatingValue } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRatingInputContainer = styled.div`
padding: ${({ theme }) => theme.spacing(2)};
padding: ${themeCssVariables.spacing[2]};
`;
const convertFieldRatingValueToNumber = (
@@ -1,12 +1,13 @@
import { type SelectableItem } from '@/object-record/select/types/SelectableItem';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Avatar } from 'twenty-ui/display';
import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledPinnedItemsContainer = styled.div`
display: flex;
flex-direction: column;
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
`;
export const ObjectFilterDropdownRecordPinnedItems = (props: {
@@ -12,11 +12,12 @@ import { useUpdateViewFromCurrentState } from '@/views/view-picker/hooks/useUpda
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useEffect, useRef, useState } from 'react';
import { styled } from '@linaria/react';
import { useContext, useEffect, useRef, useState } from 'react';
import { Key } from 'ts-key-enum';
import { OverflowingTextWithTooltip, useIcons } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useDebouncedCallback } from 'use-debounce';
const StyledDropdownMenuIconAndNameContainer = styled.div`
@@ -24,27 +25,27 @@ const StyledDropdownMenuIconAndNameContainer = styled.div`
display: flex;
margin-left: 0;
margin-right: 0;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
const StyledMenuTitleContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
padding: ${themeCssVariables.spacing[1]};
`;
const StyledMenuIconContainer = styled.div`
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
align-items: center;
display: flex;
height: ${({ theme }) => theme.spacing(6)};
height: ${themeCssVariables.spacing[6]};
justify-content: center;
width: ${({ theme }) => theme.spacing(6)};
width: ${themeCssVariables.spacing[6]};
`;
const StyledMainText = styled.div`
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
flex-shrink: 0;
overflow: hidden;
text-overflow: ellipsis;
@@ -110,7 +111,7 @@ export const ObjectOptionsDropdownMenuViewName = ({
}
}, [currentView?.key]);
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const MainIcon = getIcon(currentView?.icon);
@@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext, useRef } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { RecordBoardColumns } from '@/object-record/record-board/components/RecordBoardColumns';
import { RecordBoardDragDropContext } from '@/object-record/record-board/components/RecordBoardDragDropContext';
@@ -22,7 +23,7 @@ const StyledContainer = styled.div`
const StyledContainerContainer = styled.div`
display: flex;
flex-direction: column;
min-height: calc(100% - ${({ theme }) => theme.spacing(2)});
min-height: calc(100% - ${themeCssVariables.spacing[2]});
height: min-content;
`;
@@ -2,13 +2,14 @@ import { RecordBoardColumn } from '@/object-record/record-board/record-board-col
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue';
import { ViewType } from '@/views/types/ViewType';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledColumnContainer = styled.div`
display: flex;
& > *:not(:first-of-type) {
border-left: 1px solid ${({ theme }) => theme.border.color.light};
border-left: 1px solid ${themeCssVariables.border.color.light};
}
`;
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
@@ -4,7 +4,8 @@ import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/re
import { RecordIndexGroupAggregatesDataLoader } from '@/object-record/record-index/components/RecordIndexGroupAggregatesDataLoader';
import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue';
import { ViewType } from '@/views/types/ViewType';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledHeaderContainer = styled.div`
display: flex;
@@ -21,7 +22,7 @@ const StyledHeaderContainer = styled.div`
}
& > *:not(:first-of-type) {
border-left: 1px solid ${({ theme }) => theme.border.color.light};
border-left: 1px solid ${themeCssVariables.border.color.light};
}
`;
@@ -30,23 +30,21 @@ import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hoo
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
import { useDebouncedCallback } from 'use-debounce';
const StyledCardContainer = styled.div<{ isPrimaryMultiDrag?: boolean }>`
position: relative;
${({ isPrimaryMultiDrag }) =>
isPrimaryMultiDrag &&
`
transform: scale(1.02);
z-index: 10;
`}
transform: ${({ isPrimaryMultiDrag }) =>
isPrimaryMultiDrag ? 'scale(1.02)' : 'none'};
z-index: ${({ isPrimaryMultiDrag }) => (isPrimaryMultiDrag ? '10' : 'auto')};
`;
const StyledBoardCardWrapper = styled.div`
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${themeCssVariables.spacing[2]};
width: 100%;
`;
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Draggable } from '@hello-pangea/dnd';
import { useContext } from 'react';
@@ -18,8 +18,9 @@ import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAto
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { isDefined } from 'twenty-shared/utils';
import { ChipVariant } from 'twenty-ui/components';
import { IconEye, IconEyeOff } from 'twenty-ui/display';
@@ -29,7 +30,7 @@ const StyledCompactIconContainer = styled.div`
align-items: center;
display: flex;
justify-content: center;
margin-left: ${({ theme }) => theme.spacing(1)};
margin-left: ${themeCssVariables.spacing[1]};
`;
const StyledCheckboxContainer = styled.div`
@@ -1,6 +1,6 @@
import { originalDragSelectionComponentState } from '@/object-record/record-drag/states/originalDragSelectionComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { NotificationCounter } from 'twenty-ui/navigation';
const StyledNotificationCounter = styled(NotificationCounter)`
@@ -1,6 +1,7 @@
import { originalDragSelectionComponentState } from '@/object-record/record-drag/states/originalDragSelectionComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRecordBoardCardStackCard = styled.div<{ offset: number }>`
position: absolute;
@@ -8,9 +9,9 @@ const StyledRecordBoardCardStackCard = styled.div<{ offset: number }>`
left: 0;
right: 0;
height: 100%;
background-color: ${({ theme }) => theme.accent.tertiary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
background-color: ${themeCssVariables.accent.tertiary};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.sm};
z-index: ${({ offset }) => -offset};
`;
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type ReactNode } from 'react';
const StyledFieldContainer = styled.div`
@@ -1,5 +1,6 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Droppable } from '@hello-pangea/dnd';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { RecordBoardColumnCardsContainer } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
@@ -12,14 +13,14 @@ import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hoo
import { isDefined } from 'twenty-shared/utils';
const StyledColumn = styled.div`
background-color: ${({ theme }) => theme.background.primary};
background-color: ${themeCssVariables.background.primary};
display: flex;
flex-direction: column;
max-width: 200px;
min-width: 200px;
min-height: 100%;
flex: 1;
padding: ${({ theme }) => theme.spacing(2)};
padding: ${themeCssVariables.spacing[2]};
padding-top: 0px;
position: relative;
height: 100%;
@@ -1,6 +1,8 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { RecordCardBodyContainer } from '@/object-record/record-card/components/RecordCardBodyContainer';
@@ -11,31 +13,31 @@ import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
const StyledSkeletonIconAndText = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
const StyledSkeletonTitle = styled.div`
padding-left: ${({ theme }) => theme.spacing(1)};
padding-left: ${themeCssVariables.spacing[1]};
`;
const StyledBodyContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(0.5)};
gap: ${themeCssVariables.spacing['0.5']};
padding-top: 4px;
padding-bottom: 4px;
`;
const StyledStaticCellSkeleton = styled.div<{ width: number; height: number }>`
background-color: ${({ theme }) => theme.background.tertiary};
border-radius: ${({ theme }) => theme.border.radius.sm};
background-color: ${themeCssVariables.background.tertiary};
border-radius: ${themeCssVariables.border.radius.sm};
width: ${({ width }) => width}px;
height: ${({ height }) => height}px;
`;
export const RecordBoardColumnCardContainerSkeletonLoader = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { currentView } = useGetCurrentViewOnly();
@@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Draggable, type DroppableProvided } from '@hello-pangea/dnd';
import { useContext } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { RecordBoardCardDraggableContainer } from '@/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer';
@@ -19,7 +20,7 @@ const StyledColumnCardsContainer = styled.div`
`;
const StyledNewButtonContainer = styled.div`
padding-bottom: ${({ theme }) => theme.spacing(4)};
padding-bottom: ${themeCssVariables.spacing[4]};
`;
type RecordBoardColumnCardsContainerProps = {
@@ -1,5 +1,6 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
@@ -38,7 +39,7 @@ const StyledHeaderActions = styled.div`
`;
const StyledHeaderContainer = styled.div`
background: ${({ theme }) => theme.background.primary};
background: ${themeCssVariables.background.primary};
display: flex;
justify-content: space-between;
width: 100%;
@@ -46,7 +47,7 @@ const StyledHeaderContainer = styled.div`
const StyledLeftContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
overflow: hidden;
`;
@@ -56,13 +57,13 @@ const StyledRightContainer = styled.div`
`;
const StyledColumn = styled.div`
background-color: ${({ theme }) => theme.background.primary};
background-color: ${themeCssVariables.background.primary};
display: flex;
flex-direction: column;
max-width: ${RECORD_BOARD_COLUMN_WIDTH}px;
min-width: ${RECORD_BOARD_COLUMN_WIDTH}px;
padding: ${({ theme }) => theme.spacing(2)};
padding: ${themeCssVariables.spacing[2]};
position: relative;
`;
@@ -7,7 +7,7 @@ import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record
import { type RecordBoardColumnHeaderAggregateContentId } from '@/object-record/record-board/types/RecordBoardColumnHeaderAggregateContentId';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type Nullable } from 'twenty-shared/types';
type RecordBoardColumnHeaderAggregateDropdownProps = {
@@ -1,7 +1,7 @@
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type Nullable } from 'twenty-shared/types';
import { Tag } from 'twenty-ui/components';
import { AppTooltip, TooltipDelay } from 'twenty-ui/display';
@@ -1,16 +1,17 @@
import { RECORD_BOARD_QUERY_PAGE_SIZE } from '@/object-record/record-board/constants/RecordBoardQueryPageSize';
import { RecordBoardColumnCardContainerSkeletonLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardContainerSkeletonLoader';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledSkeletonCardContainer = styled.div`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.background.quaternary};
border-radius: ${({ theme }) => theme.border.radius.sm};
background-color: ${themeCssVariables.background.secondary};
border: 1px solid ${themeCssVariables.background.quaternary};
border-radius: ${themeCssVariables.border.radius.sm};
box-shadow:
0px 4px 8px 0px ${({ theme }) => theme.color.gray2},
0px 0px 4px 0px ${({ theme }) => theme.color.gray2};
color: ${({ theme }) => theme.font.color.primary};
margin-bottom: ${({ theme }) => theme.spacing(2)};
0px 4px 8px 0px ${themeCssVariables.color.gray2},
0px 0px 4px 0px ${themeCssVariables.color.gray2};
color: ${themeCssVariables.font.color.primary};
margin-bottom: ${themeCssVariables.spacing[2]};
`;
export const RecordBoardColumnLoadingSkeletonCards = () => {
@@ -4,31 +4,32 @@ import { RecordBoardColumnContext } from '@/object-record/record-board/record-bo
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useCreateNewIndexRecord';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { IconPlus } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledNewButton = styled.button`
align-items: center;
align-self: baseline;
background-color: ${({ theme }) => theme.background.primary};
background-color: ${themeCssVariables.background.primary};
border: none;
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.tertiary};
border-radius: ${themeCssVariables.border.radius.sm};
color: ${themeCssVariables.font.color.tertiary};
cursor: pointer;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
padding: ${themeCssVariables.spacing[1]};
&:hover {
background-color: ${({ theme }) => theme.background.tertiary};
background-color: ${themeCssVariables.background.tertiary};
}
`;
export const RecordBoardColumnNewRecordButton = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { objectMetadataItem, selectFieldMetadataItem } =
useContext(RecordBoardContext);
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId';
import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId';
@@ -14,14 +14,15 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainerContainer = styled.div`
display: flex;
flex-direction: column;
height: inherit;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[2]};
padding-left: ${themeCssVariables.spacing[1]};
`;
export const RecordCalendar = () => {
@@ -7,14 +7,16 @@ import { useCreateNewIndexRecord } from '@/object-record/record-table/hooks/useC
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
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 Temporal } from 'temporal-polyfill';
import { IconPlus } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledButton = styled(Button)`
padding: ${({ theme }) => theme.spacing(0.5)};
padding: ${themeCssVariables.spacing['0.5']};
min-width: unset;
height: auto;
`;
@@ -28,7 +30,7 @@ export const RecordCalendarAddNew = ({
}: RecordCalendarAddNewProps) => {
const { userTimezone } = useUserTimezone();
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { createNewIndexRecord } = useCreateNewIndexRecord({
objectMetadataItem,
@@ -9,7 +9,7 @@ import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { format } from 'date-fns';
import { Temporal } from 'temporal-polyfill';
@@ -20,6 +20,7 @@ import {
} from 'twenty-shared/utils';
import { IconChevronLeft, IconChevronRight } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: center;
@@ -30,19 +31,19 @@ const StyledContainer = styled.div`
`;
const StyledNavigationButton = styled(Button)`
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
`;
const StyledLeftSection = styled.div`
display: flex;
align-items: center;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledNavigationSection = styled.div`
display: flex;
align-items: center;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
justify-content: flex-end;
`;
@@ -7,7 +7,7 @@ import { useEndRecordDrag } from '@/object-record/record-drag/hooks/useEndRecord
import { useProcessCalendarCardDrop } from '@/object-record/record-drag/hooks/useProcessCalendarCardDrop';
import { useStartRecordDrag } from '@/object-record/record-drag/hooks/useStartRecordDrag';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import {
DragDropContext,
type DragStart,
@@ -1,12 +1,13 @@
import { RecordCalendarMonthBodyWeek } from '@/object-record/record-calendar/month/components/RecordCalendarMonthBodyWeek';
import { useRecordCalendarMonthContextOrThrow } from '@/object-record/record-calendar/month/contexts/RecordCalendarMonthContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
flex: 1;
border: 1px solid ${({ theme }) => theme.border.color.light};
border: 1px solid ${themeCssVariables.border.color.light};
border-radius: 4px;
overflow: hidden;
`;
@@ -4,8 +4,7 @@ import { calendarDayRecordIdsComponentFamilySelector } from '@/object-record/rec
import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone';
import { useAtomComponentFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilySelectorValue';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Droppable } from '@hello-pangea/dnd';
import { useState } from 'react';
import { Temporal } from 'temporal-polyfill';
@@ -16,6 +15,7 @@ import {
isSamePlainDate,
} from 'twenty-shared/utils';
import { RecordCalendarAddNew } from '@/object-record/record-calendar/components/RecordCalendarAddNew';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{
isOtherMonth: boolean;
@@ -25,27 +25,20 @@ const StyledContainer = styled.div<{
width: calc(100% / 7);
flex-direction: column;
min-height: 122px;
padding: ${({ theme }) => theme.spacing(1)};
background: ${({ theme }) => theme.background.primary};
padding: ${themeCssVariables.spacing[1]};
min-width: 0;
color: ${({ theme }) => theme.font.color.primary};
background: ${({ isOtherMonth, isDayOfWeekend }) =>
isOtherMonth || isDayOfWeekend
? themeCssVariables.background.secondary
: themeCssVariables.background.primary};
color: ${({ isOtherMonth }) =>
isOtherMonth
? themeCssVariables.font.color.light
: themeCssVariables.font.color.primary};
&:not(:last-child) {
border-right: 1px solid ${({ theme }) => theme.border.color.light};
border-right: 1px solid ${themeCssVariables.border.color.light};
}
${({ isOtherMonth, theme }) =>
isOtherMonth &&
css`
background: ${theme.background.secondary};
color: ${theme.font.color.light};
`}
${({ isDayOfWeekend, theme }) =>
isDayOfWeekend &&
css`
background: ${theme.background.secondary};
`}
`;
const StyledDayHeader = styled.div`
@@ -61,42 +54,44 @@ const StyledDayHeader = styled.div`
const StyledDayHeaderDayContainer = styled.div`
display: flex;
margin-left: auto;
padding: ${({ theme }) => theme.spacing(0.5, 0.5)};
padding: ${themeCssVariables.spacing['0.5']}
${themeCssVariables.spacing['0.5']};
`;
const StyledDayHeaderDay = styled.span<{ isToday: boolean }>`
align-items: center;
display: flex;
font-size: ${({ theme }) => theme.font.size.sm};
font-size: ${themeCssVariables.font.size.sm};
justify-content: center;
line-height: 140%;
width: 20px;
${({ isToday, theme }) =>
isToday &&
css`
border-radius: 4px;
background: ${theme.color.blue};
color: ${theme.font.color.inverted};
font-weight: ${theme.font.weight.medium};
`}
border-radius: ${({ isToday }) => (isToday ? '4px' : '0')};
background: ${({ isToday }) =>
isToday ? themeCssVariables.color.blue : 'transparent'};
color: ${({ isToday }) =>
isToday
? themeCssVariables.font.color.inverted
: themeCssVariables.font.color.primary};
font-weight: ${({ isToday }) =>
isToday ? themeCssVariables.font.weight.medium : 'normal'};
`;
const StyledCardsContainer = styled.div<{ isDraggedOver?: boolean }>`
background: ${({ isDraggedOver }) =>
isDraggedOver
? themeCssVariables.background.transparent.lighter
: 'transparent'};
border: ${({ isDraggedOver }) =>
isDraggedOver
? `1px dashed ${themeCssVariables.border.color.medium}`
: 'none'};
border-radius: ${themeCssVariables.border.radius.sm};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(0.5)};
flex: 1;
flex-direction: column;
gap: ${themeCssVariables.spacing['0.5']};
min-height: 60px;
border-radius: ${({ theme }) => theme.border.radius.sm};
transition: background-color 0.1s ease;
${({ isDraggedOver, theme }) =>
isDraggedOver &&
css`
background: ${theme.background.transparent.lighter};
border: 1px dashed ${theme.border.color.medium};
`}
`;
type RecordCalendarMonthBodyDayProps = {
@@ -1,6 +1,7 @@
import { RecordCalendarMonthBodyDay } from '@/object-record/record-calendar/month/components/RecordCalendarMonthBodyDay';
import { useRecordCalendarMonthContextOrThrow } from '@/object-record/record-calendar/month/contexts/RecordCalendarMonthContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { eachDayOfInterval, endOfWeek } from 'date-fns';
import { type Temporal } from 'temporal-polyfill';
import {
@@ -14,7 +15,7 @@ const StyledContainer = styled.div`
flex: 1;
&:not(:last-of-type) {
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-bottom: 1px solid ${themeCssVariables.border.color.light};
}
`;
@@ -1,6 +1,6 @@
import { RecordCalendarMonthHeaderDay } from '@/object-record/record-calendar/month/components/RecordCalendarMonthHeaderDay';
import { useRecordCalendarMonthContextOrThrow } from '@/object-record/record-calendar/month/contexts/RecordCalendarMonthContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledContainer = styled.div`
display: flex;
@@ -1,16 +1,17 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type RecordCalendarMonthHeaderDayProps = {
label: string;
};
const StyledLabel = styled.div`
color: ${({ theme }) => theme.font.color.light};
color: ${themeCssVariables.font.color.light};
display: flex;
font-size: ${({ theme }) => theme.font.size.sm};
font-size: ${themeCssVariables.font.size.sm};
height: 24px;
justify-content: flex-end;
padding: ${({ theme }) => theme.spacing(0, 1)};
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1]};
width: calc(100% / 7);
`;
@@ -17,7 +17,7 @@ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/com
import { useAtomComponentFamilyState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyState';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
const StyledContainer = styled.div`
@@ -17,10 +17,11 @@ import { RecordInlineCell } from '@/object-record/record-inline-cell/components/
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentSelectorValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledRecordCardBodyContainer = styled(RecordCardBodyContainer)`
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
`;
type RecordCalendarCardBodyProps = {

Some files were not shown because too many files have changed in this diff Show More