chore(twenty-front): migrate command-menu, workflow, page-layout and UI modules from Emotion to Linaria (PR 4-6/10) (#18342)

## Summary

Continues the Emotion → Linaria migration (PR 4-6 from the [migration
plan](docs/emotion-to-linaria-migration-plan.md)). Migrates **311
files** across four module groups:

| Module | Files |
|---|---|
| command-menu | 53 |
| workflow | 84 |
| page-layout | 84 |
| UI (partial - first ~80 files) | ~80 |
| twenty-ui (TEXT_INPUT_STYLE) | 1 |
| misc (hooks, keyboard-shortcut-menu, file-upload) | ~9 |

### Migration patterns applied

- `import styled from '@emotion/styled'` → `import { styled } from
'@linaria/react'`
- `import { useTheme } from '@emotion/react'` → `import { useContext }
from 'react'` + `import { ThemeContext } from 'twenty-ui/theme'`
- `${({ theme }) => theme.X.Y.Z}` → `${themeCssVariables.X.Y.Z}` (static
CSS variables)
- `theme.spacing(N)` → `themeCssVariables.spacing[N]`
- `styled(motion.div)` → `motion.create(StyledBase)` (11 components)
- `styled(Component)<TypeParams>` → wrapper div approach for non-HTML
elements
- Multi-declaration interpolations split into one CSS property per
interpolation
- Interpolation return types fixed (`&&` → ternary `? : ''`)
- `TEXT_INPUT_STYLE` converted from function to static string constant
(backward compatible)
- Emotion `<Global>` replaced with `useEffect` style injection
- Complex runtime-dependent styles use CSS custom properties via
`style={}` prop

### After this PR

- **Remaining files**: ~400 (object-record: ~160, settings: ~200, UI:
~44)
- **No breaking changes**: CSS variables resolve identically to the
previous Emotion theme values
This commit is contained in:
Charles Bochet
2026-03-03 16:42:03 +01:00
committed by GitHub
parent 8b26020a0b
commit 3bfdc2c83f
339 changed files with 3439 additions and 2613 deletions
@@ -1,6 +1,5 @@
import { LightCopyIconButton } from '@/object-record/record-field/ui/components/LightCopyIconButton';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import {
IconLoader,
@@ -9,6 +8,9 @@ import {
} from 'twenty-ui/display';
import { CodeEditor, CoreEditorHeader } from 'twenty-ui/input';
import { AnimatedCircleLoading } from 'twenty-ui/utilities';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
const StyledContainer = styled.div`
display: flex;
@@ -28,26 +30,26 @@ type OutputAccent = 'default' | 'success' | 'error';
const StyledInfoContainer = styled.div`
display: flex;
font-size: ${({ theme }) => theme.font.size.md};
font-size: ${themeCssVariables.font.size.md};
`;
const StyledOutput = styled.div<{ accent?: OutputAccent }>`
align-items: center;
gap: ${({ theme }) => theme.spacing(1)};
color: ${({ theme, accent }) =>
gap: ${themeCssVariables.spacing[1]};
color: ${({ accent }) =>
accent === 'success'
? theme.color.turquoise
? themeCssVariables.color.turquoise
: accent === 'error'
? theme.color.red
: theme.font.color.secondary};
? themeCssVariables.color.red
: themeCssVariables.font.color.secondary};
display: flex;
`;
const StyledStatusInfo = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
font-size: ${({ theme }) => theme.font.size.sm};
color: ${({ theme }) => theme.font.color.tertiary};
gap: ${themeCssVariables.spacing[2]};
font-size: ${themeCssVariables.font.size.sm};
color: ${themeCssVariables.font.color.tertiary};
`;
export type ExecutionStatus = {
@@ -77,7 +79,7 @@ export const WorkflowStepExecutionResult = ({
loadingMessage = t`Processing...`,
idleMessage = t`Output`,
}: WorkflowStepExecutionResultProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const SuccessLeftNode = (
<StyledOutput accent="success">
@@ -32,8 +32,7 @@ import { getConnectionOptionsForSourceHandle } from '@/workflow/workflow-diagram
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultSourceHandleId';
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_TARGET_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultTargetHandleId';
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import {
Background,
ReactFlow,
@@ -64,6 +63,8 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { isDefined } from 'twenty-shared/utils';
import { Tag, type TagColor } from 'twenty-ui/components';
import { useStore } from 'jotai';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
const StyledResetReactflowStyles = styled.div`
height: 100%;
@@ -92,7 +93,7 @@ const StyledStatusTagContainer = styled.div`
left: 0;
top: 0;
position: absolute;
padding: ${({ theme }) => theme.spacing(4)};
padding: ${themeCssVariables.spacing[4]};
`;
const defaultFitViewOptions = {
@@ -166,7 +167,7 @@ export const WorkflowDiagramCanvasBase = ({
}) => void;
}) => {
const store = useStore();
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const reactflow = useReactFlow();
@@ -1,7 +1,8 @@
import { useTheme } from '@emotion/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramConnector = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<svg
@@ -2,7 +2,7 @@ import { WorkflowDiagramConnector } from '@/workflow/workflow-diagram/components
import { VERTICAL_DISTANCE_BETWEEN_TWO_NODES } from '@/workflow/workflow-diagram/constants/VerticalDistanceBetweenTwoNodes';
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { IconPlus } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
@@ -4,23 +4,24 @@ import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStart
import { useWorkflowDiagramScreenToFlowPosition } from '@/workflow/workflow-diagram/hooks/useWorkflowDiagramScreenToFlowPosition';
import { workflowDiagramRightClickMenuPositionState } from '@/workflow/workflow-diagram/states/workflowDiagramRightClickMenuPositionState';
import { useTidyUp } from '@/workflow/workflow-version/hooks/useTidyUp';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useRef } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconPlus, IconReorder } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { WorkflowDiagramRightClickCommandMenuClickOutsideEffect } from './WorkflowDiagramRightClickCommandMenuClickOutsideEffect';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ x: number; y: number }>`
background: ${({ theme }) => theme.background.primary};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
border-radius: ${({ theme }) => theme.spacing(2)};
background: ${themeCssVariables.background.primary};
box-shadow: ${themeCssVariables.boxShadow.strong};
border-radius: ${themeCssVariables.spacing[2]};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(0.5)};
gap: ${themeCssVariables.spacing[0.5]};
left: ${({ x }) => `${x}px`};
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
position: absolute;
top: ${({ y }) => `${y}px`};
width: 200px;
@@ -6,22 +6,23 @@ import { WorkflowRunVisualizer } from '@/workflow/workflow-diagram/components/Wo
import { WorkflowRunVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect';
import { WorkflowRunVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext';
import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Suspense, useId } from 'react';
import { styled } from '@linaria/react';
import { Suspense, useContext, useId } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
const StyledLoadingSkeletonContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
height: 100%;
padding: ${({ theme }) => theme.spacing(4)};
padding: ${themeCssVariables.spacing[4]};
width: 100%;
`;
const LoadingSkeleton = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<StyledLoadingSkeletonContainer>
@@ -4,22 +4,23 @@ import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWo
import { WorkflowVersionVisualizer } from '@/workflow/workflow-diagram/components/WorkflowVersionVisualizer';
import { WorkflowVersionVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect';
import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Suspense } from 'react';
import { styled } from '@linaria/react';
import { Suspense, useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
const StyledLoadingSkeletonContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
height: 100%;
padding: ${({ theme }) => theme.spacing(4)};
padding: ${themeCssVariables.spacing[4]};
width: 100%;
`;
const LoadingSkeleton = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<StyledLoadingSkeletonContainer>
@@ -1,5 +1,5 @@
import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { type Theme } from '@emotion/react';
import type { ThemeType } from 'twenty-ui/theme';
type WorkflowDiagramColors = {
background: string;
@@ -18,7 +18,7 @@ export const getWorkflowDiagramColors = ({
theme,
runStatus,
}: {
theme: Theme;
theme: ThemeType;
runStatus?: WorkflowRunStepStatus;
}): WorkflowDiagramNodeColors => {
switch (runStatus) {
@@ -1,7 +1,8 @@
import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState';
import { type WorkflowDiagramEdgeComponentProps } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdgeComponentProps';
import { useTheme } from '@emotion/react';
import { BaseEdge } from '@xyflow/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowDiagramBaseEdgeProps = Pick<
WorkflowDiagramEdgeComponentProps,
@@ -24,7 +25,7 @@ export const WorkflowDiagramBaseEdge = ({
markerEnd,
path,
}: WorkflowDiagramBaseEdgeProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { isEdgeSelected, isEdgeHovered } = useEdgeState();
@@ -1,7 +1,8 @@
import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getEdgePath } from '@/workflow/workflow-diagram/workflow-edges/utils/getEdgePath';
import { useTheme } from '@emotion/react';
import { BaseEdge, type EdgeProps } from '@xyflow/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowDiagramBlankEdgeProps = EdgeProps<WorkflowDiagramEdge>;
@@ -15,7 +16,7 @@ export const WorkflowDiagramBlankEdge = ({
targetX,
targetPosition,
}: WorkflowDiagramBlankEdgeProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { segments } = getEdgePath({
sourceX,
@@ -2,8 +2,9 @@ import {
type ConnectionLineComponentProps,
getBezierPath,
} from '@xyflow/react';
import { useTheme } from '@emotion/react';
import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowDiagramConnectionProps = ConnectionLineComponentProps;
@@ -13,7 +14,7 @@ export const WorkflowDiagramConnection = ({
toX,
toY,
}: WorkflowDiagramConnectionProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const [path] = getBezierPath({
sourceX: fromX,
@@ -1,8 +1,9 @@
import { useContext } from 'react';
import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker';
import { useTheme } from '@emotion/react';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramCustomMarkers = () => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<svg style={{ position: 'absolute', top: 0, left: 0 }}>
@@ -2,10 +2,11 @@ import { WorkflowDiagramEdgeLabel } from '@/workflow/workflow-diagram/workflow-e
import { WorkflowDiagramEdgeLabelContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeLabelContainer';
import { type WorkflowDiagramEdgeComponentProps } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdgeComponentProps';
import { getEdgePath } from '@/workflow/workflow-diagram/workflow-edges/utils/getEdgePath';
import { useTheme } from '@emotion/react';
import { i18n } from '@lingui/core';
import { BaseEdge, EdgeLabelRenderer } from '@xyflow/react';
import { isDefined } from 'twenty-shared/utils';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowDiagramDefaultEdgeReadonlyProps =
WorkflowDiagramEdgeComponentProps;
@@ -21,7 +22,7 @@ export const WorkflowDiagramDefaultEdgeReadonly = ({
markerEnd,
data,
}: WorkflowDiagramDefaultEdgeReadonlyProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const {
segments,
@@ -1,19 +1,18 @@
import styled from '@emotion/styled';
import { IconButtonGroup, type IconButtonGroupProps } from 'twenty-ui/input';
import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
import { css } from '@emotion/react';
import { css } from '@linaria/core';
import { styled } from '@linaria/react';
import { useContext, useMemo, type CSSProperties } from 'react';
import { IconButtonGroup, type IconButtonGroupProps } from 'twenty-ui/input';
import { ThemeContext } from 'twenty-ui/theme';
const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>`
const iconButtonGroupStyle = css`
background-color: var(--edge-btn-bg, transparent);
border: var(--edge-btn-border, none);
pointer-events: all;
`;
${({ selected, theme }) => {
if (!selected) return '';
const colors = getWorkflowDiagramColors({ theme });
return css`
background-color: ${colors.selected.background};
border: 1px solid ${colors.selected.borderColor};
`;
}}
const StyledWrapper = styled.div`
display: contents;
`;
type WorkflowDiagramEdgeButtonGroupProps = IconButtonGroupProps & {
@@ -24,11 +23,24 @@ export const WorkflowDiagramEdgeButtonGroup = ({
selected = false,
iconButtons,
}: WorkflowDiagramEdgeButtonGroupProps) => {
const { theme } = useContext(ThemeContext);
const dynamicStyles = useMemo(() => {
if (!selected) return {};
const colors = getWorkflowDiagramColors({ theme });
return {
'--edge-btn-bg': colors.selected.background,
// eslint-disable-next-line lingui/no-unlocalized-strings
'--edge-btn-border': `1px solid ${colors.selected.borderColor}`,
} as CSSProperties;
}, [selected, theme]);
return (
<StyledIconButtonGroup
className="nodrag nopan"
iconButtons={iconButtons}
selected={selected}
/>
<StyledWrapper style={dynamicStyles}>
<IconButtonGroup
className={`nodrag nopan ${iconButtonGroupStyle}`}
iconButtons={iconButtons}
/>
</StyledWrapper>
);
};
@@ -1,23 +1,24 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { Label } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.strong};
border-radius: ${({ theme }) => theme.border.radius.sm};
background: ${themeCssVariables.background.secondary};
border: 1px solid ${themeCssVariables.border.color.strong};
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
justify-content: center;
padding-block: ${({ theme }) => theme.spacing(0.5)};
padding-inline: ${({ theme }) => theme.spacing(1)};
padding-block: ${themeCssVariables.spacing[0.5]};
padding-inline: ${themeCssVariables.spacing[1]};
`;
const StyledNumber = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
color: ${themeCssVariables.font.color.tertiary};
font-size: ${themeCssVariables.font.size.sm};
`;
type WorkflowDiagramEdgeLabelProps = {
@@ -1,5 +1,4 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Position } from '@xyflow/react';
import { isDefined } from 'twenty-shared/utils';
@@ -13,28 +12,19 @@ const StyledWorkflowDiagramEdgeLabelContainer = styled.div<{
position: absolute;
width: fit-content;
${({ position, sourceX, sourceY, centerX, centerY }) => {
transform: ${({ position, sourceX, sourceY, centerX, centerY }) => {
if (isDefined(centerX) && isDefined(centerY)) {
return css`
transform: translate(-50%, -50%) translate(${centerX}px, ${centerY}px);
`;
return `translate(-50%, -50%) translate(${centerX}px, ${centerY}px)`;
}
switch (position) {
case Position.Right: {
return css`
transform: translate(0%, -50%)
translate(${Math.floor(sourceX)}px, ${sourceY}px) translateX(10px);
`;
}
case Position.Bottom: {
return css`
transform: translate(-50%, 0%)
translate(${sourceX}px, ${Math.floor(sourceY)}px) translateY(10px);
`;
}
case Position.Right:
return `translate(0%, -50%) translate(${Math.floor(sourceX)}px, ${sourceY}px) translateX(10px)`;
case Position.Bottom:
return `translate(-50%, 0%) translate(${sourceX}px, ${Math.floor(sourceY)}px) translateY(10px)`;
default:
return 'none';
}
}}
}};
`;
export { StyledWorkflowDiagramEdgeLabelContainer as WorkflowDiagramEdgeLabelContainer };
@@ -1,12 +1,11 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { styled } from '@linaria/react';
const StyledContainer = styled.div<{ labelX: number; labelY: number }>`
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
pointer-events: all;
${({ labelX, labelY }) => css`
transform: translate(-50%, -50%) translate(${labelX}px, ${labelY}px);
`}
transform: ${({ labelX, labelY }) =>
`translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`};
position: absolute;
`;
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledContainer = styled.div<{ shouldDisplay: boolean }>`
opacity: ${({ shouldDisplay }) => (shouldDisplay ? 1 : 0)};
@@ -17,9 +17,11 @@ import { useLingui } from '@lingui/react/macro';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramEmptyTriggerEditable = ({ id }: { id: string }) => {
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const { openWorkflowTriggerTypeInCommandMenu } = useWorkflowCommandMenu();
@@ -60,6 +62,7 @@ export const WorkflowDiagramEmptyTriggerEditable = ({ id }: { id: string }) => {
return (
<WorkflowNodeContainer
data-click-outside-id={WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID}
theme={theme}
onClick={handleClick}
selected={selected}
>
@@ -72,7 +75,7 @@ export const WorkflowDiagramEmptyTriggerEditable = ({ id }: { id: string }) => {
</WorkflowNodeLabel>
</WorkflowNodeLabelWithCounterPart>
<WorkflowNodeTitle selected={selected}>
<WorkflowNodeTitle theme={theme} selected={selected}>
{t`Add a Trigger`}
</WorkflowNodeTitle>
</WorkflowNodeRightPart>
@@ -19,10 +19,12 @@ import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { useIcons } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramEmptyTriggerReadonly = ({ id }: { id: string }) => {
const { getIcon } = useIcons();
const { t } = useLingui();
const { theme } = useContext(ThemeContext);
const workflowVisualizerWorkflowId = useAtomComponentStateValue(
workflowVisualizerWorkflowIdComponentState,
@@ -73,6 +75,7 @@ export const WorkflowDiagramEmptyTriggerReadonly = ({ id }: { id: string }) => {
return (
<WorkflowNodeContainer
data-click-outside-id={WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID}
theme={theme}
onClick={handleClick}
selected={selected}
>
@@ -85,7 +88,7 @@ export const WorkflowDiagramEmptyTriggerReadonly = ({ id }: { id: string }) => {
</WorkflowNodeLabel>
</WorkflowNodeLabelWithCounterPart>
<WorkflowNodeTitle selected={selected}>
<WorkflowNodeTitle theme={theme} selected={selected}>
{t`Add a Trigger`}
</WorkflowNodeTitle>
</WorkflowNodeRightPart>
@@ -2,47 +2,23 @@ import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { NODE_HANDLE_HEIGHT_PX } from '@/workflow/workflow-diagram/constants/NodeHandleHeightPx';
import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/NodeHandleWidthPx';
import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Handle, Position, type HandleProps } from '@xyflow/react';
import { useContext, useMemo, type CSSProperties } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const HANDLE_SCALE_ON_HOVER = 1.5;
const StyledHandle = styled(Handle, {
shouldForwardProp: (prop) =>
prop !== 'disableHoverEffect' &&
prop !== 'selected' &&
prop !== 'hovered' &&
prop !== 'runStatus',
})<{
type: HandleProps['type'];
disableHoverEffect?: boolean;
selected: boolean;
hovered?: boolean;
runStatus?: WorkflowRunStepStatus;
}>`
const StyledHandle = styled(Handle)`
&.react-flow__handle {
opacity: ${({ type }) => (type === 'target' ? 0 : 1)};
height: ${NODE_HANDLE_HEIGHT_PX}px;
width: ${NODE_HANDLE_WIDTH_PX}px;
${({ theme, selected, hovered, disableHoverEffect, runStatus }) => {
if (!selected) {
return css`
background: ${theme.background.primary};
border-color: ${hovered && disableHoverEffect !== true
? theme.font.color.light
: theme.border.color.strong};
`;
}
const colors = getWorkflowDiagramColors({ theme, runStatus });
return css`
background: ${colors.selected.background};
border-color: ${colors.selected.borderColor};
`;
}}
opacity: var(--handle-opacity, 1);
background: var(--handle-bg);
border-color: var(--handle-border-color);
transform: var(--handle-transform);
transform-origin: var(--handle-transform-origin);
&::after {
content: '';
@@ -50,8 +26,8 @@ const StyledHandle = styled(Handle, {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% + ${({ theme }) => theme.spacing(4)});
height: calc(100% + ${({ theme }) => theme.spacing(4)});
width: calc(100% + ${themeCssVariables.spacing[4]});
height: calc(100% + ${themeCssVariables.spacing[4]});
}
transition:
@@ -60,55 +36,88 @@ const StyledHandle = styled(Handle, {
border-color 0.1s;
z-index: 1;
${({ position }) => {
if (position === Position.Right) {
return css`
transform: translate(50%, -50%);
transform-origin: top right;
`;
}
return css`
transform: translate(-50%, 50%);
transform-origin: bottom left;
`;
}}
&.connectionindicator {
cursor: pointer;
}
&:hover {
${({ disableHoverEffect, theme }) => {
if (disableHoverEffect === true) {
return undefined;
}
const colors = getWorkflowDiagramColors({ theme });
return css`
background: ${colors.selected.background} !important;
border-color: ${colors.selected.borderColor} !important;
`;
}}
${({ disableHoverEffect, position }) => {
if (disableHoverEffect === true) {
return undefined;
}
if (position === Position.Right) {
return css`
transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(50%, -50%);
`;
}
return css`
transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(-50%, 50%);
`;
}}
background: var(--handle-hover-bg, var(--handle-bg)) !important;
border-color: var(
--handle-hover-border-color,
var(--handle-border-color)
) !important;
transform: var(--handle-hover-transform, var(--handle-transform));
}
}
`;
export { StyledHandle as WorkflowDiagramHandleSource };
type WorkflowDiagramHandleSourceProps = {
id: string;
type: HandleProps['type'];
position: Position;
selected: boolean;
hovered?: boolean;
disableHoverEffect?: boolean;
runStatus?: WorkflowRunStepStatus;
};
export const WorkflowDiagramHandleSource = ({
id,
type,
position,
selected,
hovered,
disableHoverEffect,
runStatus,
}: WorkflowDiagramHandleSourceProps) => {
const { theme } = useContext(ThemeContext);
const dynamicStyles = useMemo(() => {
const isRight = position === Position.Right;
const transform = isRight ? 'translate(50%, -50%)' : 'translate(-50%, 50%)';
// eslint-disable-next-line lingui/no-unlocalized-strings
const transformOrigin = isRight ? 'top right' : 'bottom left';
let bg: string;
let borderColor: string;
if (selected) {
const colors = getWorkflowDiagramColors({ theme, runStatus });
bg = colors.selected.background;
borderColor = colors.selected.borderColor;
} else {
bg = theme.background.primary;
borderColor =
hovered && disableHoverEffect !== true
? theme.font.color.light
: theme.border.color.strong;
}
const styles: Record<string, string> = {
'--handle-opacity': type === 'target' ? '0' : '1',
'--handle-bg': bg,
'--handle-border-color': borderColor,
'--handle-transform': transform,
'--handle-transform-origin': transformOrigin,
};
if (disableHoverEffect !== true) {
const hoverColors = getWorkflowDiagramColors({ theme });
styles['--handle-hover-bg'] = hoverColors.selected.background;
styles['--handle-hover-border-color'] = hoverColors.selected.borderColor;
styles['--handle-hover-transform'] =
`scale(${HANDLE_SCALE_ON_HOVER}) ${transform}`;
}
return styles as CSSProperties;
}, [position, selected, hovered, disableHoverEffect, runStatus, type, theme]);
return (
<StyledHandle
id={id}
type={type}
position={position}
style={dynamicStyles}
/>
);
};
@@ -1,6 +1,7 @@
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_TARGET_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultTargetHandleId';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Handle, Position } from '@xyflow/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowDiagramHandleTargetProps = {
isConnectable?: boolean;
@@ -10,7 +11,7 @@ const StyledHandle = styled(Handle)`
&.react-flow__handle {
opacity: 0;
z-index: 1;
border-radius: ${({ theme }) => theme.border.radius.md};
border-radius: ${themeCssVariables.border.radius.md};
width: 100%;
height: 100%;
left: 0;
@@ -19,11 +19,12 @@ import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workf
import { useConnectionState } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState';
import { isNodeTitleHighlighted } from '@/workflow/workflow-diagram/workflow-nodes/utils/isNodeTitleHighlighted';
import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { Position } from '@xyflow/react';
import { useState } from 'react';
import { useContext, useState } from 'react';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { ThemeContext } from 'twenty-ui/theme';
const StyledAddStepButtonContainer = styled.div<{
shouldDisplay: boolean;
@@ -51,6 +52,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({
onClick?: () => void;
}) => {
const { i18n } = useLingui();
const { theme } = useContext(ThemeContext);
const [isHovered, setIsHovered] = useState(false);
@@ -95,6 +97,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({
<>
<WorkflowNodeContainer
data-click-outside-id={WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID}
theme={theme}
onClick={onClick}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
@@ -115,6 +118,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({
</WorkflowNodeLabelWithCounterPart>
<WorkflowNodeTitle
theme={theme}
highlight={nodeTitleHighlighted}
selected={selected}
>
@@ -1,15 +1,16 @@
import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { useTheme } from '@emotion/react';
import { assertUnreachable } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramStepNodeIcon = ({
data,
}: {
data: WorkflowDiagramStepNodeData;
}) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const Icon = getIcon(getWorkflowNodeIconKey(data));
@@ -25,6 +25,7 @@ import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomStat
import { useContext } from 'react';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
export const WorkflowDiagramStepNodeReadonly = ({
id,
@@ -34,6 +35,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
data: WorkflowDiagramStepNodeData;
}) => {
const { getIcon } = useIcons();
const { theme } = useContext(ThemeContext);
const workflowVisualizerWorkflowId = useAtomComponentStateValue(
workflowVisualizerWorkflowIdComponentState,
@@ -88,6 +90,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
<>
<WorkflowNodeContainer
data-click-outside-id={WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID}
theme={theme}
onClick={handleClick}
selected={selected}
>
@@ -104,6 +107,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
</WorkflowNodeLabelWithCounterPart>
<WorkflowNodeTitle
theme={theme}
highlight={nodeTitleHighlighted}
selected={selected}
>
@@ -1,20 +1,22 @@
import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import type { ThemeType } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledNodeContainer = styled.div<{
theme: ThemeType;
runStatus?: WorkflowRunStepStatus;
isConnectable?: boolean;
selected: boolean;
}>`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
max-width: 240px;
min-width: 44px;
padding: ${({ theme }) => theme.spacing(2)};
border-radius: ${({ theme }) => theme.border.radius.md};
padding: ${themeCssVariables.spacing[2]};
border-radius: ${themeCssVariables.border.radius.md};
border-width: 1px;
border-style: solid;
box-sizing: border-box;
@@ -22,34 +24,35 @@ const StyledNodeContainer = styled.div<{
position: relative;
transition: border-color 0.1s;
${({ theme, runStatus, selected, isConnectable }) => {
background: ${({ theme, runStatus, selected }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
return selected ? colors.selected.background : colors.unselected.background;
}};
const background = selected
? colors.selected.background
: colors.unselected.background;
border-color: ${({ theme, runStatus, selected }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
return selected
? colors.selected.borderColor
: colors.unselected.borderColor;
}};
return css`
background: ${background};
border-color: ${selected
&:hover {
background: ${({ theme, runStatus, selected }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
const bg = selected
? colors.selected.background
: colors.unselected.background;
return `linear-gradient(0deg, ${themeCssVariables.background.transparent.lighter} 0%, ${themeCssVariables.background.transparent.lighter} 100%), ${bg}`;
}};
border-color: ${({ theme, runStatus, selected, isConnectable }) => {
if (isConnectable === true) return themeCssVariables.color.blue;
const colors = getWorkflowDiagramColors({ theme, runStatus });
return selected
? colors.selected.borderColor
: colors.unselected.borderColor};
&:hover {
background: linear-gradient(
0deg,
${theme.background.transparent.lighter} 0%,
${theme.background.transparent.lighter} 100%
),
${background};
${isConnectable &&
css`
border-color: ${theme.color.blue} !important;
`};
}
`;
}}
: colors.unselected.borderColor;
}};
}
`;
export { StyledNodeContainer as WorkflowNodeContainer };
@@ -1,8 +1,9 @@
import styled from '@emotion/styled';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { styled } from '@linaria/react';
const StyledNodeIconContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.transparent.light};
background: ${themeCssVariables.background.transparent.light};
border-radius: 4px;
box-sizing: border-box;
display: flex;
@@ -1,23 +1,40 @@
import { useContext } from 'react';
import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Label } from 'twenty-ui/display';
import { ThemeContext } from 'twenty-ui/theme';
const StyledNodeLabel = styled(Label)<{
type WorkflowNodeLabelProps = {
runStatus?: WorkflowRunStepStatus;
selected: boolean;
}>`
children?: React.ReactNode;
className?: string;
};
const StyledNodeLabelWrapper = styled.div<{ labelColor: string }>`
box-sizing: border-box;
flex: 1 0 0;
${({ theme, runStatus, selected }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
return css`
color: ${selected ? colors.selected.color : colors.unselected.color};
`;
}}
> div {
color: ${({ labelColor }) => labelColor};
}
`;
export { StyledNodeLabel as WorkflowNodeLabel };
export const WorkflowNodeLabel = ({
runStatus,
selected,
children,
className,
}: WorkflowNodeLabelProps) => {
const { theme } = useContext(ThemeContext);
const colors = getWorkflowDiagramColors({ theme, runStatus });
const labelColor = selected ? colors.selected.color : colors.unselected.color;
return (
<StyledNodeLabelWrapper labelColor={labelColor} className={className}>
<Label>{children}</Label>
</StyledNodeLabelWrapper>
);
};
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledNodeLabelWithCounterPart = styled.div`
align-items: center;
@@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledNodeRightPart = styled.div`
align-items: flex-start;
@@ -1,38 +1,29 @@
import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import type { ThemeType } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledNodeTitle = styled.div<{
theme: ThemeType;
highlight?: boolean;
runStatus?: WorkflowRunStepStatus;
selected: boolean;
}>`
box-sizing: border-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
align-self: stretch;
display: -webkit-box;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.medium};
overflow: hidden;
text-overflow: ellipsis;
${({ theme, highlight, runStatus, selected }) => {
box-sizing: border-box;
color: ${({ theme, highlight, runStatus, selected }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
if (highlight === true) return colors.selected.titleColor;
return selected ? colors.selected.titleColor : colors.unselected.titleColor;
}};
display: -webkit-box;
font-size: ${themeCssVariables.font.size.md};
font-weight: ${themeCssVariables.font.weight.medium};
overflow: hidden;
if (true === highlight) {
return css`
color: ${colors.selected.titleColor};
`;
}
return css`
color: ${selected
? colors.selected.titleColor
: colors.unselected.titleColor};
`;
}}
text-overflow: ellipsis;
`;
export { StyledNodeTitle as WorkflowNodeTitle };
@@ -23,8 +23,7 @@ import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-node
import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle';
import { WORKFLOW_DIAGRAM_NODE_DEFAULT_SOURCE_HANDLE_ID } from '@/workflow/workflow-diagram/workflow-nodes/constants/WorkflowDiagramNodeDefaultSourceHandleId';
import { getNodeIterationCount } from '@/workflow/workflow-diagram/workflow-nodes/utils/getNodeIterationCount';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Position } from '@xyflow/react';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useContext } from 'react';
@@ -32,15 +31,17 @@ import { capitalize, isDefined } from 'twenty-shared/utils';
import { StepStatus } from 'twenty-shared/workflow';
import { IconCheck, IconX, useIcons } from 'twenty-ui/display';
import { Loader } from 'twenty-ui/feedback';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext, type ThemeType } from 'twenty-ui/theme';
const StyledNodeLabelWithCounterPart = styled(WorkflowNodeLabelWithCounterPart)`
column-gap: ${({ theme }) => theme.spacing(2)};
column-gap: ${themeCssVariables.spacing[2]};
`;
const StyledStatusIconsContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
justify-content: flex-end;
box-sizing: border-box;
`;
@@ -49,7 +50,7 @@ const StyledColorIcon = styled.div<{
color: string;
}>`
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.sm};
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
display: flex;
height: 14px;
@@ -59,22 +60,18 @@ const StyledColorIcon = styled.div<{
`;
const StyledIterationCounter = styled.div<{
theme: ThemeType;
runStatus?: WorkflowRunStepStatus;
}>`
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
${({ theme, runStatus }) => {
const colors = getWorkflowDiagramColors({ theme, runStatus });
return css`
color: ${colors.unselected.color};
`;
}}
color: ${({ theme, runStatus }) =>
getWorkflowDiagramColors({ theme, runStatus }).unselected.color};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
`;
const StyledRightPartContainer = styled.div`
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const WorkflowRunDiagramStepNode = ({
@@ -85,7 +82,7 @@ export const WorkflowRunDiagramStepNode = ({
data: WorkflowRunDiagramStepNodeData;
}) => {
const { getIcon } = useIcons();
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const workflowVisualizerWorkflowId = useAtomComponentStateValue(
workflowVisualizerWorkflowIdComponentState,
@@ -140,6 +137,7 @@ export const WorkflowRunDiagramStepNode = ({
<>
<WorkflowNodeContainer
data-click-outside-id={WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID}
theme={theme}
runStatus={data.runStatus}
onClick={handleClick}
selected={selected}
@@ -157,7 +155,10 @@ export const WorkflowRunDiagramStepNode = ({
<StyledRightPartContainer>
{iterationCount > 0 && (
<StyledIterationCounter runStatus={data.runStatus}>
<StyledIterationCounter
theme={theme}
runStatus={data.runStatus}
>
{iterationCount}
</StyledIterationCounter>
)}
@@ -189,7 +190,11 @@ export const WorkflowRunDiagramStepNode = ({
</StyledRightPartContainer>
</StyledNodeLabelWithCounterPart>
<WorkflowNodeTitle runStatus={data.runStatus} selected={selected}>
<WorkflowNodeTitle
theme={theme}
runStatus={data.runStatus}
selected={selected}
>
{data.name}
</WorkflowNodeTitle>
</WorkflowNodeRightPart>
@@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { styled } from '@linaria/react';
const StyledStepListContainer = styled.div`
display: flex;
@@ -6,8 +7,8 @@ const StyledStepListContainer = styled.div`
height: 100%;
overflow-y: auto;
padding-block: ${({ theme }) => theme.spacing(1)};
padding-inline: ${({ theme }) => theme.spacing(2)};
padding-block: ${themeCssVariables.spacing[1]};
padding-inline: ${themeCssVariables.spacing[2]};
`;
export { StyledStepListContainer as RightDrawerStepListContainer };
@@ -1,13 +1,14 @@
import styled from '@emotion/styled';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { styled } from '@linaria/react';
const StyledSelectStepTitle = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
padding-top: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(1)};
padding-right: ${({ theme }) => theme.spacing(1)};
color: ${themeCssVariables.font.color.tertiary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
padding-top: ${themeCssVariables.spacing[2]};
padding-bottom: ${themeCssVariables.spacing[1]};
padding-left: ${themeCssVariables.spacing[1]};
padding-right: ${themeCssVariables.spacing[1]};
`;
export { StyledSelectStepTitle as RightDrawerWorkflowSelectStepTitle };
@@ -1,5 +1,5 @@
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledWorkflowRunStepJsonContainer = styled(WorkflowStepBody)`
grid-template-rows: max-content;
@@ -1,18 +1,19 @@
import { AppErrorBoundary } from '@/error-handler/components/AppErrorBoundary';
import { AppErrorDisplay } from '@/error-handler/components/internal/AppErrorDisplay';
import { type AppErrorDisplayProps } from '@/error-handler/types/AppErrorDisplayProps';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledWorkflowStepBody = styled.div`
background: ${({ theme }) => theme.background.primary};
background: ${themeCssVariables.background.primary};
display: flex;
flex: 1 1 auto;
flex-direction: column;
height: 100%;
overflow-y: scroll;
padding-block: ${({ theme }) => theme.spacing(4)};
padding-inline: ${({ theme }) => theme.spacing(3)};
row-gap: ${({ theme }) => theme.spacing(4)};
padding-block: ${themeCssVariables.spacing[4]};
padding-inline: ${themeCssVariables.spacing[3]};
row-gap: ${themeCssVariables.spacing[4]};
`;
export const WorkflowStepBody = ({
@@ -1,13 +1,14 @@
import { useAddRootStepFilter } from '@/workflow/workflow-steps/filters/hooks/useAddRootStepFilter';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import { IconFilter } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledButton = styled(Button)`
margin-top: ${({ theme }) => theme.spacing(2)};
margin-top: ${themeCssVariables.spacing[2]};
`;
export const WorkflowStepFilterAddRootStepFilterButton = () => {
@@ -5,9 +5,10 @@ import { WorkflowStepFilterOperandSelect } from '@/workflow/workflow-steps/filte
import { WorkflowStepFilterOptionsDropdown } from '@/workflow/workflow-steps/filters/components/WorkflowStepFilterOptionsDropdown';
import { WorkflowStepFilterValueInput } from '@/workflow/workflow-steps/filters/components/WorkflowStepFilterValueInput';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { type StepFilter, type StepFilterGroup } from 'twenty-shared/types';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowStepFilterColumnProps = {
stepFilterGroup: StepFilterGroup;
@@ -22,7 +23,7 @@ const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const WorkflowStepFilterColumn = ({
@@ -9,7 +9,6 @@ import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variabl
import { useSearchVariable } from '@/workflow/workflow-variables/hooks/useSearchVariable';
import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2';
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { useContext, useState } from 'react';
import { type StepFilter } from 'twenty-shared/types';
@@ -32,7 +31,6 @@ export const WorkflowStepFilterFieldSelect = ({
}: WorkflowStepFilterFieldSelectProps) => {
const { readonly } = useContext(WorkflowStepFilterContext);
const { t } = useLingui();
const theme = useTheme();
const { closeDropdown } = useCloseDropdown();
const { getIcon } = useIcons();
@@ -153,8 +151,8 @@ export const WorkflowStepFilterFieldSelect = ({
}
dropdownPlacement="bottom-end"
dropdownOffset={{
x: parseInt(theme.spacing(0.5), 10),
y: parseInt(theme.spacing(1), 10),
x: 2,
y: 4,
}}
/>
);
@@ -2,21 +2,24 @@ import { WorkflowStepFilterAddFilterRuleSelect } from '@/workflow/workflow-steps
import { WorkflowStepFilterColumn } from '@/workflow/workflow-steps/filters/components/WorkflowStepFilterColumn';
import { useChildStepFiltersAndChildStepFilterGroups } from '@/workflow/workflow-steps/filters/hooks/useChildStepFiltersAndChildStepFilterGroups';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
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 WorkflowStepFilterGroupChildrenProps = {
@@ -3,9 +3,10 @@ import { WorkflowStepFilterGroupChildren } from '@/workflow/workflow-steps/filte
import { WorkflowStepFilterGroupOptionsDropdown } from '@/workflow/workflow-steps/filters/components/WorkflowStepFilterGroupOptionsDropdown';
import { WorkflowStepFilterLogicalOperatorCell } from '@/workflow/workflow-steps/filters/components/WorkflowStepFilterLogicalOperatorCell';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { type StepFilterGroup } from 'twenty-shared/types';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowStepFilterGroupColumnProps = {
parentStepFilterGroup: StepFilterGroup;
@@ -17,7 +18,7 @@ const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const WorkflowStepFilterGroupColumn = ({
@@ -4,29 +4,30 @@ import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/Gene
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/filters/hooks/useUpsertStepFilterSettings';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext, useMemo } from 'react';
import { StepLogicalOperator, type StepFilterGroup } from 'twenty-shared/types';
import { capitalize, isDefined } 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 StyledNumber = styled.span`
color: ${({ theme }) => theme.font.color.tertiary};
margin-right: ${({ theme }) => theme.spacing(1)};
color: ${themeCssVariables.font.color.tertiary};
margin-right: ${themeCssVariables.spacing[1]};
`;
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 WorkflowStepFilterLogicalOperatorCellProps = {
@@ -1,5 +1,4 @@
import { type SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
import { useTheme } from '@emotion/react';
import { IconTrash } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import {
@@ -9,6 +8,8 @@ import {
StyledRowLeftContent,
StyledText,
} from './WorkflowAiAgentPermissionsStyles';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowAiAgentPermissionsFlagRowProps = {
permission: SettingsRolePermissionsSettingPermission;
@@ -27,7 +28,7 @@ export const WorkflowAiAgentPermissionsFlagRow = ({
onAdd,
onDelete,
}: WorkflowAiAgentPermissionsFlagRowProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const isClickable = !readonly && !isEnabled && Boolean(onAdd);
const isDisabled = isEnabled && !showDeleteButton;
@@ -1,4 +1,3 @@
import { useTheme } from '@emotion/react';
import { useIcons } from 'twenty-ui/display';
import {
StyledIconChevronRight,
@@ -7,6 +6,8 @@ import {
StyledRowLeftContent,
StyledText,
} from './WorkflowAiAgentPermissionsStyles';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowAiAgentPermissionsObjectRowProps = {
objectMetadata: {
@@ -23,7 +24,7 @@ export const WorkflowAiAgentPermissionsObjectRow = ({
onClick,
readonly,
}: WorkflowAiAgentPermissionsObjectRowProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { getIcon } = useIcons();
const IconComponent = getIcon(objectMetadata.icon);
@@ -1,12 +1,14 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { IconChevronRight, Label } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
export const StyledText = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
color: ${themeCssVariables.font.color.secondary};
`;
export const StyledLabel = styled(Label)`
margin: ${({ theme }) => theme.spacing(3, 3, 0)};
margin: ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[3]}
${themeCssVariables.spacing[0]};
`;
export const StyledRow = styled.div<{ isDisabled?: boolean }>`
@@ -14,16 +16,16 @@ export const StyledRow = styled.div<{ isDisabled?: boolean }>`
display: flex;
justify-content: space-between;
width: 100%;
padding: ${({ theme }) => theme.spacing(1)};
padding: ${themeCssVariables.spacing[1]};
box-sizing: border-box;
border-radius: ${({ theme }) => theme.spacing(1)};
border-radius: ${themeCssVariables.spacing[1]};
cursor: ${({ isDisabled }) => (isDisabled ? 'not-allowed' : 'pointer')};
opacity: ${({ isDisabled }) => (isDisabled ? 0.5 : 1)};
pointer-events: ${({ isDisabled }) => (isDisabled ? 'none' : 'auto')};
:hover {
background-color: ${({ theme, isDisabled }) =>
isDisabled ? 'inherit' : theme.background.transparent.light};
background-color: ${({ isDisabled }) =>
isDisabled ? 'inherit' : themeCssVariables.background.transparent.light};
}
:hover [data-delete-button] {
@@ -33,32 +35,32 @@ export const StyledRow = styled.div<{ isDisabled?: boolean }>`
export const StyledDeleteButton = styled.div`
opacity: 0;
transition: opacity ${({ theme }) => theme.animation.duration.fast}ms;
transition: opacity ${themeCssVariables.animation.duration.fast}ms;
`;
export const StyledRowLeftContent = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
export const StyledList = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(0.5)};
padding: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[0.5]};
padding: ${themeCssVariables.spacing[2]};
`;
export const StyledIconContainer = styled.div`
background-color: ${({ theme }) => theme.background.tertiary};
padding: ${({ theme }) => theme.spacing(1)};
border-radius: ${({ theme }) => theme.spacing(1)};
background-color: ${themeCssVariables.background.tertiary};
padding: ${themeCssVariables.spacing[1]};
border-radius: ${themeCssVariables.spacing[1]};
display: flex;
justify-content: center;
align-items: center;
color: ${({ theme }) => theme.font.color.tertiary};
color: ${themeCssVariables.font.color.tertiary};
`;
export const StyledIconChevronRight = styled(IconChevronRight)`
color: ${({ theme }) => theme.font.color.tertiary};
color: ${themeCssVariables.font.color.tertiary};
`;
@@ -8,7 +8,7 @@ import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomState
import { workflowAiAgentActionAgentState } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/states/workflowAiAgentActionAgentState';
import { workflowAiAgentPermissionsIsAddingPermissionState } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/states/workflowAiAgentPermissionsIsAddingPermissionState';
import { workflowAiAgentPermissionsSelectedObjectIdState } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/states/workflowAiAgentPermissionsSelectedObjectIdState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -25,10 +25,11 @@ import { WorkflowAiAgentPermissionsCrudList } from './WorkflowAiAgentPermissions
import { WorkflowAiAgentPermissionsFlagList } from './WorkflowAiAgentPermissionsFlagList';
import { WorkflowAiAgentPermissionsObjectsList } from './WorkflowAiAgentPermissionsObjectsList';
import { getFilteredPermissions } from './workflowAiAgentPermissions.utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledSearchInput = styled(TextInput)`
width: 100%;
height: 40px;
border-block: 1px solid ${({ theme }) => theme.border.color.medium};
border-block: 1px solid ${themeCssVariables.border.color.medium};
input {
height: 40px;
line-height: 40px;
@@ -39,7 +40,7 @@ const StyledSearchInput = styled(TextInput)`
`;
const StyledBackButtonText = styled.span`
color: ${({ theme }) => theme.font.color.secondary};
color: ${themeCssVariables.font.color.secondary};
`;
const StyledBackButton = styled.button`
@@ -47,15 +48,15 @@ const StyledBackButton = styled.button`
align-items: center;
background: none;
border: none;
color: ${({ theme }) => theme.font.color.secondary};
color: ${themeCssVariables.font.color.secondary};
cursor: pointer;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
padding: ${themeCssVariables.spacing[3]};
text-align: left;
&:hover {
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
}
`;
@@ -16,7 +16,7 @@ import { WORKFLOW_AI_AGENT_TABS } from '@/workflow/workflow-steps/workflow-actio
import { useResetWorkflowAiAgentPermissionsStateOnCommandMenuClose } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/hooks/useResetWorkflowAiAgentPermissionsStateOnCommandMenuClose';
import { workflowAiAgentActionAgentState } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/states/workflowAiAgentActionAgentState';
import { workflowAiAgentPermissionsIsAddingPermissionState } from '@/workflow/workflow-steps/workflow-actions/ai-agent-action/states/workflowAiAgentPermissionsIsAddingPermissionState';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import {
@@ -35,6 +35,7 @@ import {
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { RightDrawerSkeletonLoader } from '~/loading/components/RightDrawerSkeletonLoader';
import { WorkflowAiAgentPromptTab } from './WorkflowAiAgentPromptTab';
import { themeCssVariables } from 'twenty-ui/theme-constants';
export type WorkflowAiAgentTabId =
(typeof WORKFLOW_AI_AGENT_TABS)[keyof typeof WORKFLOW_AI_AGENT_TABS];
@@ -50,8 +51,8 @@ type WorkflowEditActionAiAgentProps = {
};
const StyledTabList = styled(TabList)`
background-color: ${({ theme }) => theme.background.secondary};
padding-left: ${({ theme }) => theme.spacing(2)};
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[2]};
`;
const StyledPermissionsStepBody = styled(WorkflowStepBody)`
@@ -3,13 +3,15 @@ import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/c
import { type OutputSchemaField } from '@/ai/constants/OutputFieldTypeOptions';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { IconPlus, IconTrash } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { v4 } from 'uuid';
import { WorkflowOutputFieldTypeSelector } from './WorkflowOutputFieldTypeSelector';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowOutputSchemaBuilderProps = {
fields: OutputSchemaField[];
@@ -25,80 +27,80 @@ const StyledOutputSchemaContainer = styled.div`
const StyledFieldsContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledOutputSchemaFieldContainer = styled.div`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.md};
background-color: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.light};
border-radius: ${themeCssVariables.border.radius.md};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
gap: ${themeCssVariables.spacing[1]};
`;
const StyledSettingsContent = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[3]};
`;
const StyledSettingsHeader = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-bottom: 1px solid ${themeCssVariables.border.color.light};
display: grid;
gap: ${({ theme }) => theme.spacing(1)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
padding-right: ${themeCssVariables.spacing[2]};
padding-left: ${themeCssVariables.spacing[3]};
grid-template-columns: 1fr 24px;
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledTitleContainer = styled.div`
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
padding-top: ${themeCssVariables.spacing[3]};
`;
const StyledCloseButtonContainer = styled.div`
padding-top: ${({ theme }) => theme.spacing(2)};
padding-top: ${themeCssVariables.spacing[2]};
`;
const StyledAddFieldButton = styled.button`
align-items: center;
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.secondary};
border: 1px solid ${themeCssVariables.border.color.light};
border-radius: ${themeCssVariables.border.radius.sm};
color: ${themeCssVariables.font.color.secondary};
cursor: pointer;
display: flex;
font-family: inherit;
font-weight: ${({ theme }) => theme.font.weight.medium};
gap: ${({ theme }) => theme.spacing(1)};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[1]};
justify-content: center;
margin-top: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
margin-top: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[2]};
width: 100%;
background-color: ${({ theme }) => theme.background.transparent.lighter};
background-color: ${themeCssVariables.background.transparent.lighter};
&:hover {
background-color: ${({ theme }) => theme.background.transparent.light};
background-color: ${themeCssVariables.background.transparent.light};
}
`;
const StyledMessageContentContainer = styled.div`
flex-direction: column;
color: ${({ theme }) => theme.font.color.secondary};
color: ${themeCssVariables.font.color.secondary};
display: flex;
gap: ${({ theme }) => theme.spacing(4)};
padding: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
padding: ${themeCssVariables.spacing[4]};
line-height: normal;
`;
const StyledMessageDescription = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
font-weight: ${({ theme }) => theme.font.weight.regular};
color: ${themeCssVariables.font.color.secondary};
font-weight: ${themeCssVariables.font.weight.regular};
`;
export const WorkflowOutputSchemaBuilder = ({
@@ -106,7 +108,7 @@ export const WorkflowOutputSchemaBuilder = ({
onChange,
readonly,
}: WorkflowOutputSchemaBuilderProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const addField = () => {
const newField: OutputSchemaField = {
@@ -1,10 +1,11 @@
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { type Monaco } from '@monaco-editor/react';
import { type editor } from 'monaco-editor';
import { IconMaximize } from 'twenty-ui/display';
import { CodeEditor, LightIconButton } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const CODE_EDITOR_MIN_HEIGHT = 343;
@@ -19,8 +20,8 @@ const StyledCodeEditorContainer = styled.div`
const StyledFullScreenButtonContainer = styled.div`
position: absolute;
top: ${({ theme }) => theme.spacing(2)};
right: ${({ theme }) => theme.spacing(2)};
top: ${themeCssVariables.spacing[2]};
right: ${themeCssVariables.spacing[2]};
z-index: 1;
`;
@@ -28,7 +28,7 @@ import { logicFunctionTestDataFamilyState } from '@/workflow/workflow-steps/work
import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
@@ -48,6 +48,7 @@ import { CodeEditor } from 'twenty-ui/input';
import { useIsMobile } from 'twenty-ui/utilities';
import { useDebouncedCallback } from 'use-debounce';
import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const CODE_EDITOR_MIN_HEIGHT = 343;
@@ -61,8 +62,8 @@ const StyledCodeEditorContainer = styled.div`
`;
const StyledTabList = styled(TabList)`
background-color: ${({ theme }) => theme.background.secondary};
padding-left: ${({ theme }) => theme.spacing(2)};
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[2]};
`;
const StyledFullScreenCodeEditorContainer = styled.div`
@@ -3,13 +3,14 @@ import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/c
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { type FunctionInput } from 'twenty-shared/workflow';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { isObject } from '@sniptt/guards';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ fullWidth?: boolean }>`
display: flex;
gap: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[3]};
flex-wrap: wrap;
> * {
@@ -5,7 +5,7 @@ import { useGetLogicFunctionSourceCode } from '@/logic-functions/hooks/useGetLog
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type Monaco } from '@monaco-editor/react';
import { type editor } from 'monaco-editor';
import { AutoTypings } from 'monaco-editor-auto-typings';
@@ -12,7 +12,6 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow';
@@ -66,8 +65,6 @@ export const WorkflowEditActionCreateRecord = ({
action,
actionOptions,
}: WorkflowEditActionCreateRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
@@ -218,7 +215,7 @@ export const WorkflowEditActionCreateRecord = ({
saveAction(newFormData);
}}
withSearchInput
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
@@ -9,7 +9,6 @@ import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/Gene
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { isDefined } from 'twenty-shared/utils';
import { canObjectBeManagedByWorkflow } from 'twenty-shared/workflow';
import { HorizontalSeparator, useIcons } from 'twenty-ui/display';
@@ -38,8 +37,6 @@ export const WorkflowEditActionDeleteRecord = ({
action,
actionOptions,
}: WorkflowEditActionDeleteRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
@@ -136,7 +133,7 @@ export const WorkflowEditActionDeleteRecord = ({
saveAction(newFormData);
}}
withSearchInput
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
@@ -24,7 +24,6 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { useEmailForm } from '@/workflow/workflow-steps/workflow-actions/hooks/useEmailForm';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useEffect, useState } from 'react';
@@ -56,7 +55,6 @@ export const WorkflowEditActionEmailBase = ({
action,
actionOptions,
}: WorkflowEditActionEmailBaseProps) => {
const theme = useTheme();
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
const { triggerApisOAuth } = useTriggerApisOAuth();
const { enqueueErrorSnackBar } = useSnackBar();
@@ -242,7 +240,7 @@ export const WorkflowEditActionEmailBase = ({
handleConnectedAccountChange(connectedAccountId);
}}
disabled={actionOptions.readonly}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
{isDefined(missingScopes) && (
@@ -12,7 +12,6 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { type UpdateRecordFormData } from '@/workflow/workflow-steps/workflow-actions/types/update-record-form-data.type';
import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -39,8 +38,6 @@ export const WorkflowEditActionUpdateRecord = ({
action,
actionOptions,
}: WorkflowEditActionUpdateRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
@@ -169,7 +166,7 @@ export const WorkflowEditActionUpdateRecord = ({
saveAction(newFormData);
}}
withSearchInput
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
@@ -13,7 +13,6 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
@@ -76,8 +75,6 @@ export const WorkflowEditActionUpsertRecord = ({
action,
actionOptions,
}: WorkflowEditActionUpsertRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
@@ -235,7 +232,7 @@ export const WorkflowEditActionUpsertRecord = ({
saveAction(newFormData);
}}
withSearchInput
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
@@ -11,26 +11,27 @@ import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/sta
import { rootLevelStepFilterGroupComponentSelector } from '@/workflow/workflow-steps/filters/states/rootLevelStepFilterGroupComponentSelector';
import { type FilterSettings } from '@/workflow/workflow-steps/filters/types/FilterSettings';
import { isStepFilterGroupChildAStepFilterGroup } from '@/workflow/workflow-steps/filters/utils/isStepFilterGroupChildAStepFilterGroup';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
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%;
`;
const StyledFilterBodyContainer = styled(WorkflowStepBody)`
gap: ${({ theme }) => theme.spacing(0)};
gap: ${themeCssVariables.spacing[0]};
`;
type WorkflowEditActionFilterBodyProps = {
@@ -1,5 +1,4 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { msg } from '@lingui/core/macro';
import { i18n } from '@lingui/core';
import { useLingui } from '@lingui/react/macro';
@@ -33,13 +32,14 @@ import { WorkflowFindRecordsFilters } from '@/workflow/workflow-steps/workflow-a
import { WorkflowFindRecordsFiltersEffect } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowFindRecordsFiltersEffect';
import { WorkflowFindRecordsSorts } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowFindRecordsSorts';
import { WorkflowObjectDropdownContent } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowObjectDropdownContent';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.light};
color: ${themeCssVariables.font.color.light};
display: block;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
margin-bottom: ${({ theme }) => theme.spacing(1)};
font-size: ${themeCssVariables.font.size.xs};
font-weight: ${themeCssVariables.font.weight.semiBold};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledRecordTypeSelectContainer = styled.div<{ fullWidth?: boolean }>`
@@ -81,7 +81,6 @@ export const WorkflowEditActionFindRecords = ({
action,
actionOptions,
}: WorkflowEditActionFindRecordsProps) => {
const theme = useTheme();
const { t } = useLingui();
const maxRecordsFormatted = QUERY_MAX_RECORDS.toLocaleString();
@@ -202,7 +201,7 @@ export const WorkflowEditActionFindRecords = ({
/>
)
}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
/>
</StyledRecordTypeSelectContainer>
@@ -2,27 +2,28 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
import { filterSortableFieldMetadataItems } from '@/object-metadata/utils/filterSortableFieldMetadataItems';
import { type RecordSort } from '@/object-record/record-sort/types/RecordSort';
import { Select } from '@/ui/input/components/Select';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { IconArrowsSort, IconTrash, useIcons } from 'twenty-ui/display';
import { Button, type SelectOption } from 'twenty-ui/input';
import { v4 as uuidv4 } from 'uuid';
import { ViewSortDirection } from '~/generated-metadata/graphql';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledSortItemContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledAddButtonContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(1)};
margin-top: ${themeCssVariables.spacing[1]};
`;
type WorkflowFindRecordsSortsProps = {
@@ -14,12 +14,11 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { type OnDragEndResponder } from '@hello-pangea/dnd';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
@@ -33,6 +32,8 @@ import {
import { LightIconButton } from 'twenty-ui/input';
import { useDebouncedCallback } from 'use-debounce';
import { v4 } from 'uuid';
import { ThemeContext } from 'twenty-ui/theme';
import { themeCssVariables } from 'twenty-ui/theme-constants';
export type WorkflowEditActionFormBuilderProps = {
triggerType: WorkflowTriggerType | undefined;
@@ -51,13 +52,13 @@ type FormData = WorkflowFormActionField[];
const StyledWorkflowStepBody = styled(WorkflowStepBody)`
display: block;
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-left: ${themeCssVariables.spacing[2]};
padding-right: ${themeCssVariables.spacing[2]};
`;
const StyledFormFieldContainer = styled.div`
align-items: flex-end;
column-gap: ${({ theme }) => theme.spacing(1)};
column-gap: ${themeCssVariables.spacing[1]};
display: grid;
grid-template-areas:
'grip input delete'
@@ -68,19 +69,19 @@ const StyledFormFieldContainer = styled.div`
const StyledDraggingIndicator = styled.div`
position: absolute;
inset: ${({ theme }) => theme.spacing(-2)};
top: ${({ theme }) => theme.spacing(-1)};
background-color: ${({ theme }) => theme.background.transparent.light};
inset: -8px;
top: -4px;
background-color: ${themeCssVariables.background.transparent.light};
`;
const StyledLightGripIconButton = styled(LightIconButton)`
grid-area: grip;
margin-bottom: ${({ theme }) => theme.spacing(1)};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledLightTrashIconButton = styled(LightIconButton)`
grid-area: delete;
margin-bottom: ${({ theme }) => theme.spacing(1)};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledFormFieldInputContainer = styled(FormFieldInputContainer)`
@@ -99,20 +100,19 @@ const StyledFieldContainer = styled.div<{
border: none;
display: flex;
font-family: inherit;
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-left: ${themeCssVariables.spacing[2]};
padding-right: ${themeCssVariables.spacing[2]};
width: 100%;
cursor: ${({ readonly }) => (readonly ? 'default' : 'pointer')};
${({ readonly, theme }) =>
!readonly &&
css`
&:hover,
&[data-open='true'] {
background-color: ${theme.background.transparent.lighter};
}
`}
&:hover,
&[data-open='true'] {
background-color: ${({ readonly }) =>
readonly
? 'transparent'
: themeCssVariables.background.transparent.lighter};
}
`;
const StyledPlaceholder = styled(FormFieldPlaceholder)`
@@ -120,30 +120,30 @@ const StyledPlaceholder = styled(FormFieldPlaceholder)`
`;
const StyledAddFieldButtonContainer = styled.div`
padding-left: ${({ theme }) => theme.spacing(7)};
padding-right: ${({ theme }) => theme.spacing(7)};
padding-top: ${({ theme }) => theme.spacing(2)};
padding-left: ${themeCssVariables.spacing[7]};
padding-right: ${themeCssVariables.spacing[7]};
padding-top: ${themeCssVariables.spacing[2]};
`;
const StyledAddFieldButtonContentContainer = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.secondary};
color: ${themeCssVariables.font.color.secondary};
display: flex;
font-weight: ${({ theme }) => theme.font.weight.medium};
gap: ${({ theme }) => theme.spacing(0.5)};
font-weight: ${themeCssVariables.font.weight.medium};
gap: ${themeCssVariables.spacing[0.5]};
justify-content: center;
width: 100%;
`;
const StyledCalloutContainer = styled.div`
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(7)};
padding-right: ${({ theme }) => theme.spacing(7)};
padding-top: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${themeCssVariables.spacing[2]};
padding-left: ${themeCssVariables.spacing[7]};
padding-right: ${themeCssVariables.spacing[7]};
padding-top: ${themeCssVariables.spacing[2]};
`;
const StyledNotClosableCalloutContainer = styled(StyledCalloutContainer)`
padding-bottom: ${({ theme }) => theme.spacing(4)};
padding-bottom: ${themeCssVariables.spacing[4]};
`;
export const WorkflowEditActionFormBuilder = ({
@@ -151,7 +151,7 @@ export const WorkflowEditActionFormBuilder = ({
action,
actionOptions,
}: WorkflowEditActionFormBuilderProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { t } = useLingui();
const [formData, setFormData] = useState<FormData>(action.settings.input);
@@ -278,7 +278,7 @@ export const WorkflowEditActionFormBuilder = ({
isInsideScrollableContainer
disableDraggingBackground
draggableComponentStyles={{
marginBottom: theme.spacing(4),
marginBottom: themeCssVariables.spacing[4],
}}
itemComponent={({ isDragging }) => {
const showButtons =
@@ -6,11 +6,13 @@ import { FORM_SELECT_FIELD_TYPE_OPTIONS } from '@/workflow/workflow-steps/workfl
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { type WorkflowFormFieldType } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { IconSettingsAutomation, IconX } from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowEditActionFormFieldSettingsProps = {
field: WorkflowFormActionField;
@@ -19,42 +21,42 @@ type WorkflowEditActionFormFieldSettingsProps = {
};
const StyledFormFieldSettingsContainer = styled.div`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.light};
border-radius: ${({ theme }) => theme.border.radius.md};
background-color: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.light};
border-radius: ${themeCssVariables.border.radius.md};
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
margin-top: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
margin-top: ${themeCssVariables.spacing[3]};
`;
const StyledSettingsContent = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[3]};
`;
const StyledSettingsHeader = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
border-bottom: 1px solid ${themeCssVariables.border.color.light};
display: grid;
gap: ${({ theme }) => theme.spacing(1)};
padding-right: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
padding-right: ${themeCssVariables.spacing[2]};
padding-left: ${themeCssVariables.spacing[3]};
grid-template-columns: 1fr 24px;
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledTitleContainer = styled.div`
color: ${({ theme }) => theme.font.color.primary};
color: ${themeCssVariables.font.color.primary};
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(3)};
gap: ${themeCssVariables.spacing[1]};
padding-top: ${themeCssVariables.spacing[3]};
`;
const StyledCloseButtonContainer = styled.div`
padding-top: ${({ theme }) => theme.spacing(2)};
padding-top: ${themeCssVariables.spacing[2]};
`;
export const WorkflowEditActionFormFieldSettings = ({
@@ -62,7 +64,7 @@ export const WorkflowEditActionFormFieldSettings = ({
onChange,
onClose,
}: WorkflowEditActionFormFieldSettingsProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return (
<StyledFormFieldSettingsContainer>
@@ -4,9 +4,10 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import camelCase from 'lodash.camelcase';
import { FieldMetadataType } from 'twenty-shared/types';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowFormFieldSettingsNumberProps = {
field: WorkflowFormActionField;
@@ -16,7 +17,7 @@ type WorkflowFormFieldSettingsNumberProps = {
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
export const WorkflowFormFieldSettingsNumber = ({
@@ -6,12 +6,12 @@ import { Select } from '@/ui/input/components/Select';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { useTheme } from '@emotion/react';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import camelCase from 'lodash.camelcase';
import { useIcons } from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowFormFieldSettingsRecordPickerProps = {
field: WorkflowFormActionField;
@@ -21,15 +21,13 @@ type WorkflowFormFieldSettingsRecordPickerProps = {
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
export const WorkflowFormFieldSettingsRecordPicker = ({
field,
onChange,
}: WorkflowFormFieldSettingsRecordPickerProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { activeNonSystemObjectMetadataItems } =
@@ -67,7 +65,7 @@ export const WorkflowFormFieldSettingsRecordPicker = ({
});
}}
withSearchInput
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
</FormFieldInputContainer>
@@ -7,9 +7,10 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import camelCase from 'lodash.camelcase';
import { FieldMetadataType } from 'twenty-shared/types';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowFormFieldSettingsSelectProps = {
field: WorkflowFormActionField;
@@ -19,13 +20,13 @@ type WorkflowFormFieldSettingsSelectProps = {
const StyledFormFieldSettingsSelect = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledRowContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
export const WorkflowFormFieldSettingsSelect = ({
@@ -4,9 +4,10 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { type WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import camelCase from 'lodash.camelcase';
import { FieldMetadataType } from 'twenty-shared/types';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowFormFieldSettingsTextProps = {
field: WorkflowFormActionField;
@@ -16,7 +17,7 @@ type WorkflowFormFieldSettingsTextProps = {
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
export const WorkflowFormFieldSettingsText = ({
@@ -1,9 +1,10 @@
import { type WorkflowActionType } from '@/workflow/types/Workflow';
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
import { useTheme } from '@emotion/react';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme';
export const useActionIconColorOrThrow = (actionType: WorkflowActionType) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
return getActionIconColorOrThrow({ theme, actionType });
};
@@ -13,7 +13,7 @@ import {
import { getBodyTypeFromHeaders } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/getBodyTypeFromHeaders';
import { parseHttpJsonBodyWithoutVariablesOrThrow } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/parseHttpJsonBodyWithoutVariablesOrThrow';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { isString } from '@sniptt/guards';
import { useState } from 'react';
import { isDefined, parseJson } from 'twenty-shared/utils';
@@ -24,20 +24,21 @@ import {
import { IconFileText, IconKey } from 'twenty-ui/display';
import { type JsonValue } from 'type-fest';
import { KeyValuePairInput } from './KeyValuePairInput';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledSelectDropdown = styled(Select)`
margin-bottom: ${({ theme }) => theme.spacing(2)};
margin-bottom: ${themeCssVariables.spacing[2]};
`;
const StyledNoBodyMessage = styled.div`
color: ${({ theme }) => theme.font.color.light};
font-size: ${({ theme }) => theme.font.size.md};
padding: ${({ theme }) => theme.spacing(2)};
color: ${themeCssVariables.font.color.light};
font-size: ${themeCssVariables.font.size.md};
padding: ${themeCssVariables.spacing[2]};
text-align: left;
`;
@@ -7,12 +7,13 @@ import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAto
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { httpRequestTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/http-request-action/states/httpRequestTestDataFamilyState';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledVariableInputsContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
type HttpRequestTestVariableInputProps = {
@@ -4,32 +4,28 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { AUTO_SET_HEADER_KEYS } from '@/workflow/workflow-steps/workflow-actions/http-request-action/constants/AutoSetHeaderKeys';
import { isAutoSetHeaderKey } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/isReadOnlyHeaderKey';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { css } from '@emotion/react';
import { t } from '@lingui/core/macro';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useState } from 'react';
import { IconTrash } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { v4 } from 'uuid';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
const StyledKeyValueContainer = styled.div<{ readonly: boolean | undefined }>`
display: grid;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
${({ readonly, theme }) =>
grid-template-columns: ${({ readonly }) =>
readonly
? css`
grid-template-columns: repeat(2, minmax(0, 1fr));
`
: css`
grid-template-columns: repeat(2, minmax(0, 1fr)) ${theme.spacing(8)};
`};
? 'repeat(2, minmax(0, 1fr))'
: `repeat(2, minmax(0, 1fr)) ${themeCssVariables.spacing[8]}`};
`;
export type KeyValuePair = {
id: string;
@@ -13,10 +13,9 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { getBodyTypeFromHeaders } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/getBodyTypeFromHeaders';
import { isMethodWithBody } from '@/workflow/workflow-steps/workflow-actions/http-request-action/utils/isMethodWithBody';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useEffect } from 'react';
import { useContext, useEffect } from 'react';
import { IconPlayerPlay, IconSettings } from 'twenty-ui/display';
import {
HTTP_METHODS,
@@ -31,6 +30,8 @@ import { BodyInput } from './BodyInput';
import { HttpRequestExecutionResult } from './HttpRequestExecutionResult';
import { HttpRequestTestVariableInput } from './HttpRequestTestVariableInput';
import { KeyValuePairInput } from './KeyValuePairInput';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowEditActionHttpRequestProps = {
action: WorkflowHttpRequestAction;
@@ -41,15 +42,15 @@ type WorkflowEditActionHttpRequestProps = {
};
const StyledTabList = styled(TabList)`
background-color: ${({ theme }) => theme.background.secondary};
padding-left: ${({ theme }) => theme.spacing(2)};
background-color: ${themeCssVariables.background.secondary};
padding-left: ${themeCssVariables.spacing[2]};
`;
const StyledTestTabContent = styled.div`
display: flex;
flex-direction: column;
flex: 1;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
height: 100%;
min-height: 400px;
`;
@@ -57,7 +58,7 @@ const StyledTestTabContent = styled.div`
const StyledConfigurationTabContent = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
height: 100%;
flex: 1;
`;
@@ -89,7 +90,7 @@ export const WorkflowEditActionHttpRequest = ({
actionOptions,
}: WorkflowEditActionHttpRequestProps) => {
const { t } = useLingui();
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const activeTabId = useAtomComponentStateValue(
activeTabIdComponentState,
WORKFLOW_HTTP_REQUEST_TAB_LIST_COMPONENT_ID,
@@ -117,8 +117,7 @@ export const Configured: Story = {
const canvas = within(canvasElement);
const urlLabel = await canvas.findByText('URL');
const urlInputContainer = urlLabel.closest('div')?.nextElementSibling;
const urlEditor = urlInputContainer?.querySelector('.ProseMirror');
const urlEditor = urlLabel.parentElement?.querySelector('.ProseMirror');
expect(urlEditor).toBeVisible();
expect(urlEditor).toHaveTextContent('https://api.example.com/data');
@@ -138,8 +137,7 @@ export const ReadOnly: Story = {
const canvas = within(canvasElement);
const urlLabel = await canvas.findByText('URL');
const urlInputContainer = urlLabel.closest('div')?.nextElementSibling;
const urlEditor = urlInputContainer?.querySelector('.ProseMirror');
const urlEditor = urlLabel.parentElement?.querySelector('.ProseMirror');
expect(urlEditor).toBeVisible();
expect(urlEditor).toHaveTextContent('https://api.example.com/data');
expect(urlEditor).toHaveAttribute('contenteditable', 'false');
@@ -22,23 +22,24 @@ import { getBranchesToDelete } from '@/workflow/workflow-steps/workflow-actions/
import { getBranchLabel } from '@/workflow/workflow-steps/workflow-actions/if-else-action/utils/getBranchLabel';
import { useStepsOutputSchema } from '@/workflow/workflow-variables/hooks/useStepsOutputSchema';
import { useTidyUpWorkflowVersion } from '@/workflow/workflow-version/hooks/useTidyUpWorkflowVersion';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { Fragment } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { type StepIfElseBranch } from 'twenty-shared/workflow';
import { HorizontalSeparator, IconPlus } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: start;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
`;
const StyledBodyContainer = styled(WorkflowStepBody)`
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
`;
type WorkflowEditActionIfElseBodyProps = {
@@ -6,23 +6,24 @@ import { useChildStepFiltersAndChildStepFilterGroups } from '@/workflow/workflow
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/filters/states/context/WorkflowStepFilterContext';
import { type FilterSettings } from '@/workflow/workflow-steps/filters/types/FilterSettings';
import { isStepFilterGroupChildAStepFilterGroup } from '@/workflow/workflow-steps/filters/utils/isStepFilterGroupChildAStepFilterGroup';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { i18n, type MessageDescriptor } from '@lingui/core';
import { type StepFilter, type StepFilterGroup } from 'twenty-shared/types';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { type StepIfElseBranch } from 'twenty-shared/workflow';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledBranchContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[2]};
width: 100%;
`;
const StyledFiltersContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
width: 100%;
`;
@@ -6,23 +6,24 @@ import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThro
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { getIsDescendantOfIterator } from '@/workflow/workflow-steps/utils/getIsDescendantOfIterator';
import { getWorkflowRunAllStepInfoHistory } from '@/workflow/workflow-steps/utils/getWorkflowRunAllStepInfoHistory';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { plural } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronLeft, IconChevronRight } from 'twenty-ui/display';
import { IconButton } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding-block: ${({ theme }) => theme.spacing(2)};
padding-inline: ${({ theme }) => theme.spacing(3)};
padding-block: ${themeCssVariables.spacing[2]};
padding-inline: ${themeCssVariables.spacing[3]};
`;
const StyledCounter = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.tertiary};
font-weight: ${themeCssVariables.font.weight.medium};
`;
export const WorkflowIteratorSubStepSwitcher = ({
@@ -7,18 +7,19 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields';
import { setNestedValue } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/setNestedValue';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { isObject } from '@sniptt/guards';
import { useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { Callout } from 'twenty-ui/display';
import { useDebouncedCallback } from 'use-debounce';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[4]};
`;
type WorkflowEditActionLogicFunctionProps = {
@@ -1,9 +1,8 @@
import { type WorkflowActionType } from '@/workflow/types/Workflow';
import { type Theme } from '@emotion/react';
import { COLOR_LIGHT, GRAY_SCALE_LIGHT } from 'twenty-ui/theme';
import { COLOR_LIGHT, GRAY_SCALE_LIGHT, type ThemeType } from 'twenty-ui/theme';
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
const mockTheme: Theme = {
const mockTheme: ThemeType = {
color: {
orange: COLOR_LIGHT.orange,
pink: COLOR_LIGHT.pink,
@@ -14,7 +13,7 @@ const mockTheme: Theme = {
tertiary: GRAY_SCALE_LIGHT.gray9,
},
},
} as Theme;
} as ThemeType;
describe('getActionIconColorOrThrow', () => {
describe('action types that return red color', () => {
@@ -91,7 +90,7 @@ describe('getActionIconColorOrThrow', () => {
describe('theme object handling', () => {
it('should use the provided theme colors correctly', () => {
const customTheme: Theme = {
const customTheme: ThemeType = {
color: {
red: COLOR_LIGHT.red,
orange: COLOR_LIGHT.orange,
@@ -102,7 +101,7 @@ describe('getActionIconColorOrThrow', () => {
tertiary: GRAY_SCALE_LIGHT.gray11,
},
},
} as Theme;
} as ThemeType;
expect(
getActionIconColorOrThrow({
@@ -229,7 +228,7 @@ describe('getActionIconColorOrThrow', () => {
});
it('should use the provided theme colors correctly', () => {
const customTheme: Theme = {
const customTheme: ThemeType = {
color: {
red: COLOR_LIGHT.red,
orange: COLOR_LIGHT.orange,
@@ -240,7 +239,7 @@ describe('getActionIconColorOrThrow', () => {
tertiary: GRAY_SCALE_LIGHT.gray11,
},
},
} as Theme;
} as ThemeType;
expect(
getActionIconColorOrThrow({ theme: customTheme, actionType: 'CODE' }),
@@ -266,7 +265,7 @@ describe('getActionIconColorOrThrow', () => {
});
it('should return undefined when red color is missing for SEND_EMAIL action', () => {
const themeWithoutBlue: Theme = {
const themeWithoutBlue: ThemeType = {
color: {
orange: COLOR_LIGHT.orange,
pink: COLOR_LIGHT.pink,
@@ -276,7 +275,7 @@ describe('getActionIconColorOrThrow', () => {
tertiary: GRAY_SCALE_LIGHT.gray9,
},
},
} as Theme;
} as ThemeType;
expect(
getActionIconColorOrThrow({
@@ -289,7 +288,7 @@ describe('getActionIconColorOrThrow', () => {
it('should handle null theme gracefully', () => {
expect(() => {
getActionIconColorOrThrow({
theme: null as unknown as Theme,
theme: null as unknown as ThemeType,
actionType: 'CODE',
});
}).toThrow();
@@ -1,12 +1,12 @@
import { type WorkflowActionType } from '@/workflow/types/Workflow';
import { type Theme } from '@emotion/react';
import { assertUnreachable } from 'twenty-shared/utils';
import { type ThemeType } from 'twenty-ui/theme';
export const getActionIconColorOrThrow = ({
theme,
actionType,
}: {
theme: Theme;
theme: ThemeType;
actionType: WorkflowActionType;
}) => {
switch (actionType) {
@@ -6,11 +6,12 @@ import { convertScheduleToCronExpression } from '@/workflow/workflow-trigger/uti
import { normalizeCronExpression } from '@/workflow/workflow-trigger/utils/cron-to-human/utils/normalizeCronExpression';
import { getTriggerScheduleDescription } from '@/workflow/workflow-trigger/utils/getTriggerScheduleDescription';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { CronExpressionParser } from 'cron-parser';
import { dateLocaleState } from '~/localization/states/dateLocaleState';
import { formatDateTimeString } from '~/utils/string/formatDateTimeString';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const getNextExecutions = (
cronExpression: string,
@@ -44,39 +45,39 @@ const getNextExecutions = (
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
margin-top: ${({ theme }) => theme.spacing(2)};
gap: ${themeCssVariables.spacing[3]};
margin-top: ${themeCssVariables.spacing[2]};
`;
const StyledSection = styled.div`
background-color: ${({ theme }) => theme.background.transparent.lighter};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
background-color: ${themeCssVariables.background.transparent.lighter};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.sm};
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(3)} ${({ theme }) => theme.spacing(4)};
gap: ${themeCssVariables.spacing[1]};
padding: ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[4]};
`;
const StyledScheduleDescription = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
color: ${themeCssVariables.font.color.secondary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
`;
const StyledScheduleTitle = styled.div`
color: ${({ theme }) => theme.font.color.primary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-bottom: ${({ theme }) => theme.spacing(1)};
color: ${themeCssVariables.font.color.primary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledExecutionItem = styled.div`
color: ${({ theme }) => theme.font.color.secondary};
font-size: ${({ theme }) => theme.font.size.sm};
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-top: ${({ theme }) => theme.spacing(0.5)};
color: ${themeCssVariables.font.color.secondary};
font-size: ${themeCssVariables.font.size.sm};
font-weight: ${themeCssVariables.font.weight.medium};
margin-top: ${themeCssVariables.spacing[0.5]};
`;
type CronExpressionHelperProps = {
@@ -15,21 +15,21 @@ import { type WorkflowDatabaseEventTrigger } from '@/workflow/types/Workflow';
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { Trans, useLingui } from '@lingui/react/macro';
import { useCallback, useMemo, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { IconChevronLeft, IconSettings, useIcons } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.light};
color: ${themeCssVariables.font.color.light};
display: block;
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
margin-bottom: ${({ theme }) => theme.spacing(1)};
font-size: ${themeCssVariables.font.size.xs};
font-weight: ${themeCssVariables.font.weight.semiBold};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledRecordTypeSelectContainer = styled.div<{ fullWidth?: boolean }>`
@@ -63,7 +63,6 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
trigger,
triggerOptions,
}: WorkflowEditTriggerDatabaseEventFormProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const { t } = useLingui();
const [searchInputValue, setSearchInputValue] = useState('');
@@ -250,7 +249,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
))}
</>
}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
/>
</StyledRecordTypeSelectContainer>
{isDefined(selectedObjectMetadataItem) && isFieldFilteringSupported && (
@@ -9,13 +9,13 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { MANUAL_TRIGGER_AVAILABILITY_TYPE_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerAvailabilityTypeOptions';
import { MANUAL_TRIGGER_IS_PINNED_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerIsPinnedOptions';
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
import { useIcons } from 'twenty-ui/display';
import { type SelectOption } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
type WorkflowEditTriggerManualProps = {
trigger: WorkflowManualTrigger;
@@ -31,15 +31,15 @@ type WorkflowEditTriggerManualProps = {
};
const StyledLabel = styled.span`
color: ${({ theme }) => theme.font.color.light};
font-size: ${({ theme }) => theme.font.size.xs};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
margin-bottom: ${({ theme }) => theme.spacing(1)};
color: ${themeCssVariables.font.color.light};
font-size: ${themeCssVariables.font.size.xs};
font-weight: ${themeCssVariables.font.weight.semiBold};
margin-bottom: ${themeCssVariables.spacing[1]};
`;
const StyledDescription = styled.span`
color: ${({ theme }) => theme.font.color.light};
font-size: ${({ theme }) => theme.font.size.sm};
color: ${themeCssVariables.font.color.light};
font-size: ${themeCssVariables.font.size.sm};
margin-top: 1px;
`;
@@ -52,8 +52,6 @@ export const WorkflowEditTriggerManual = ({
trigger,
triggerOptions,
}: WorkflowEditTriggerManualProps) => {
const theme = useTheme();
const { t } = useLingui();
const { getIcon } = useIcons();
@@ -107,7 +105,7 @@ export const WorkflowEditTriggerManual = ({
}),
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
@@ -139,7 +137,7 @@ export const WorkflowEditTriggerManual = ({
},
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
) : null}
@@ -147,7 +145,7 @@ export const WorkflowEditTriggerManual = ({
<IconPicker
dropdownId="workflow-edit-manual-trigger-icon"
selectedIconKey={trigger.settings.icon}
dropdownOffset={{ y: -parseInt(theme.spacing(3), 10) }}
dropdownOffset={{ y: -12 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
maxIconsVisible={9 * 8} // 9 columns * 8 lines
disabled={triggerOptions.readonly}
@@ -209,7 +207,7 @@ export const WorkflowEditTriggerManual = ({
},
});
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
</WorkflowStepBody>
@@ -12,10 +12,9 @@ import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/Workflo
import { WEBHOOK_TRIGGER_AUTHENTICATION_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerAuthenticationOptions';
import { WEBHOOK_TRIGGER_HTTP_METHOD_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerHttpMethodOptions';
import { getWebhookTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getWebhookTriggerDefaultSettings';
import { useTheme } from '@emotion/react';
import { isNonEmptyString } from '@sniptt/guards';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useState } from 'react';
import { useContext, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { getOutputSchemaFromValue } from 'twenty-shared/logic-function';
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
@@ -25,6 +24,7 @@ import { IconCopy } from 'twenty-ui/display';
import { useDebouncedCallback } from 'use-debounce';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
import { ThemeContext } from 'twenty-ui/theme';
type WorkflowEditTriggerWebhookFormProps = {
trigger: WorkflowWebhookTrigger;
@@ -50,7 +50,7 @@ export const WorkflowEditTriggerWebhookForm = ({
trigger,
triggerOptions,
}: WorkflowEditTriggerWebhookFormProps) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const { copyToClipboard } = useCopyToClipboard();
const [errorMessages, setErrorMessages] = useState<FormErrorMessages>({});
const [errorMessagesVisible, setErrorMessagesVisible] = useState(false);
@@ -110,7 +110,7 @@ export const WorkflowEditTriggerWebhookForm = ({
{ computeOutputSchema: false },
);
}}
dropdownOffset={{ y: parseInt(theme.spacing(1), 10) }}
dropdownOffset={{ y: 4 }}
dropdownWidth={GenericDropdownContentWidth.ExtraLarge}
/>
{trigger.settings.httpMethod === 'POST' && (
@@ -1,14 +1,13 @@
import { type Theme } from '@emotion/react';
import { COLOR_LIGHT } from 'twenty-ui/theme';
import { COLOR_LIGHT, type ThemeType } from 'twenty-ui/theme';
import { getTriggerIconColor } from '@/workflow/workflow-trigger/utils/getTriggerIconColor';
describe('getTriggerIconColor', () => {
const mockTheme: Theme = {
const mockTheme: ThemeType = {
color: {
blue: COLOR_LIGHT.blue,
purple: COLOR_LIGHT.purple,
},
} as unknown as Theme;
} as unknown as ThemeType;
it('returns the blue color for database event from theme', () => {
const result = getTriggerIconColor({
@@ -29,12 +28,12 @@ describe('getTriggerIconColor', () => {
});
it('works with different theme configurations', () => {
const differentTheme: Theme = {
const differentTheme: ThemeType = {
color: {
blue: COLOR_LIGHT.blue,
purple: COLOR_LIGHT.purple,
},
} as unknown as Theme;
} as unknown as ThemeType;
const result = getTriggerIconColor({
theme: differentTheme,
@@ -45,12 +44,12 @@ describe('getTriggerIconColor', () => {
});
it('maintains reference to theme.color.blue', () => {
const customTheme: Theme = {
const customTheme: ThemeType = {
color: {
blue: COLOR_LIGHT.blue,
purple: COLOR_LIGHT.purple,
},
} as unknown as Theme;
} as unknown as ThemeType;
const result = getTriggerIconColor({
theme: customTheme,
@@ -1,11 +1,11 @@
import { type Theme } from '@emotion/react';
import { type WorkflowTriggerType } from '@/workflow/types/Workflow';
import { type ThemeType } from 'twenty-ui/theme';
export const getTriggerIconColor = ({
theme,
triggerType,
}: {
theme: Theme;
theme: ThemeType;
triggerType: WorkflowTriggerType;
}) => {
switch (triggerType) {
@@ -1,10 +1,11 @@
import { BaseChip } from '@/object-record/record-field/ui/form-types/components/BaseChip';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { NodeViewWrapper, type NodeViewProps } from '@tiptap/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledWrapper = styled.span`
display: inline-block;
padding-inline: ${({ theme }) => theme.spacing(0.5)};
padding-inline: ${themeCssVariables.spacing[0.5]};
`;
type WorkflowTextEditorTextChipProps = NodeViewProps;
@@ -1,10 +1,11 @@
import { VariableChip } from '@/object-record/record-field/ui/form-types/components/VariableChip';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { NodeViewWrapper, type NodeViewProps } from '@tiptap/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledWrapper = styled.span`
display: inline-block;
padding-inline: ${({ theme }) => theme.spacing(0.5)};
padding-inline: ${themeCssVariables.spacing[0.5]};
`;
type WorkflowTextEditorVariableChipProps = NodeViewProps;
@@ -1,39 +1,50 @@
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
export const StyledSearchVariablesDropdownContainer = styled.div<{
const StyledSearchVariablesDropdownContainer = styled.div<{
multiline?: boolean;
readonly?: boolean;
isReadonly?: boolean;
}>`
align-items: center;
display: flex;
justify-content: center;
${({ theme, readonly }) =>
!readonly &&
css`
:hover {
background-color: ${theme.background.transparent.light};
}
`}
${({ theme, multiline }) =>
background-color: ${({ multiline }) =>
multiline
? css`
border-radius: ${theme.border.radius.sm};
padding: ${theme.spacing(0.5)} ${theme.spacing(0)};
position: absolute;
right: ${theme.spacing(0)};
top: ${theme.spacing(0)};
`
: css`
background-color: ${theme.background.transparent.lighter};
border-top-right-radius: ${theme.border.radius.sm};
border-bottom-right-radius: ${theme.border.radius.sm};
border: 1px solid ${theme.border.color.medium};
`}
? 'transparent'
: themeCssVariables.background.transparent.lighter};
border-radius: ${({ multiline }) =>
multiline
? themeCssVariables.border.radius.sm
: `0 ${themeCssVariables.border.radius.sm} ${themeCssVariables.border.radius.sm} 0`};
border: ${({ multiline }) =>
multiline ? 'none' : `1px solid ${themeCssVariables.border.color.medium}`};
padding: ${({ multiline }) =>
multiline
? `${themeCssVariables.spacing[0.5]} ${themeCssVariables.spacing[0]}`
: '0'};
position: ${({ multiline }) => (multiline ? 'absolute' : 'static')};
right: ${({ multiline }) =>
multiline ? themeCssVariables.spacing[0] : 'auto'};
top: ${({ multiline }) =>
multiline ? themeCssVariables.spacing[0] : 'auto'};
&:hover {
background-color: ${({ isReadonly, multiline }) => {
if (isReadonly === true) {
return multiline
? 'transparent'
: themeCssVariables.background.transparent.lighter;
}
return themeCssVariables.background.transparent.light;
}};
}
`;
export const WorkflowVariablePicker: VariablePickerComponent = ({
@@ -47,7 +58,7 @@ export const WorkflowVariablePicker: VariablePickerComponent = ({
return (
<StyledSearchVariablesDropdownContainer
multiline={multiline}
readonly={disabled}
isReadonly={disabled}
>
<WorkflowVariablesDropdown
instanceId={instanceId}
@@ -10,22 +10,23 @@ import { SEARCH_VARIABLES_DROPDOWN_ID } from '@/workflow/workflow-variables/cons
import { useAvailableVariablesInWorkflowStep } from '@/workflow/workflow-variables/hooks/useAvailableVariablesInWorkflowStep';
import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/StepOutputSchemaV2';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useState } from 'react';
import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconVariablePlus } from 'twenty-ui/display';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme';
const StyledDropdownVariableButtonContainer = styled(
StyledDropdownButtonContainer,
)<{ transparentBackground?: boolean; disabled?: boolean }>`
background-color: ${({ theme, transparentBackground }) =>
background-color: ${({ transparentBackground }) =>
transparentBackground
? 'transparent'
: theme.background.transparent.lighter};
: themeCssVariables.background.transparent.lighter};
color: ${({ theme }) => theme.font.color.tertiary};
padding: ${({ theme }) => theme.spacing(2)};
color: ${themeCssVariables.font.color.tertiary};
padding: ${themeCssVariables.spacing[2]};
:hover {
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
}
@@ -50,7 +51,7 @@ export const WorkflowVariablesDropdown = ({
multiline?: boolean;
clickableComponent?: React.ReactNode;
}) => {
const theme = useTheme();
const { theme } = useContext(ThemeContext);
const dropdownId = `${SEARCH_VARIABLES_DROPDOWN_ID}-${instanceId}`;
const isDropdownOpen = useAtomComponentStateValue(
@@ -138,7 +139,7 @@ export const WorkflowVariablesDropdown = ({
}
dropdownPlacement="bottom-end"
dropdownOffset={{
x: parseInt(theme.spacing(0.5), 10),
x: 2,
y: parseInt(theme.spacing(multiline ? 11 : 1), 10),
}}
/>