Migrate twenty ui to linaria (#18307)
## Migrate twenty-ui from Emotion to Linaria
Completes the migration of all `twenty-ui` components from Emotion
(runtime CSS-in-JS) to Linaria (zero-runtime, CSS extracted at build
time).
- Replaced `@emotion/styled` with `@linaria/react` across ~170 files
- Removed all Emotion dependencies from `twenty-ui`
- Introduced a CSS custom properties-based theme system:
`themeCssVariables` where every leaf is a `var(--t-xxx)` reference,
injected onto `document.documentElement` by
`ThemeCssVariableInjectorEffect`
- No more `theme` prop threading — styled components reference
`themeCssVariables.x.y` directly at build time
- Updated `twenty-front` consumers to remove `theme={theme}` prop
passing
**Before / After:**
```tsx
// Emotion
color: ${({ theme }) => theme.font.color.primary};
padding: ${({ theme }) => theme.spacing(4)};
// Linaria
color: ${themeCssVariables.font.color.primary};
padding: ${themeCssVariables.spacing[4]};
```
### Theme architecture
Two build-time utilities produce the theme system:
- **`buildThemeReferencingRootCssVariables`** — walks the theme object
and builds a nested mirror where every leaf is a `var(--t-xxx)` string
(evaluated at build time by wyw-in-js)
- **`prepareThemeForRootCssVariableInjection`** — walks the runtime
theme and collects flat `[--css-variable-name, value]` pairs, injected
onto `document.documentElement` by `ThemeCssVariableInjectorEffect`
Both share naming conventions (`camelToKebab`, `SPACING_VALUES`,
`formatSpacingKey`) and are unit tested.
### Spacing cleanup
Spacing scale now uses integers 0–32 (generated via loop), with `0.5`
and `1.5` as the only fractional exceptions. All other fractional
spacing usages (`0.25`, `0.75`, `1.25`, `2.5`, `3.5`) were replaced with
literal pixel values across ~20 twenty-front files.
### Framer Motion integration
Linaria doesn't support `styled(motion.div)` — wrapping a motion element
with `styled()` causes the component body to be stripped at build time.
Instead, we define the styled component first, then wrap it with
`motion.create()`:
```tsx
const StyledBarBase = styled.div`
background-color: ${themeCssVariables.font.color.primary};
height: 100%;
`;
const StyledBar = motion.create(StyledBarBase);
```
### Block interpolations
Linaria doesn't support interpolations that return multiple CSS
declarations (Linaria wraps the entire block in a single `var()`,
producing invalid CSS). These were split into individual property
interpolations:
```tsx
// Emotion — single interpolation returning multiple declarations
border-left: ${({ divider, theme }) => {
const border = `1px solid ${theme.border.color.light}`;
return divider ? `border-${divider}: ${border}` : '';
}}
// Linaria — one interpolation per property
border-left: ${({ divider }) =>
divider === 'left' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
border-right: ${({ divider }) =>
divider === 'right' ? `1px solid ${themeCssVariables.border.color.light}` : 'none'};
```
### Dynamic styles via CSS variables
When a component needs to compute styles from multiple props with
complex branching logic (e.g. `Button` combining `variant`, `accent`,
`inverted`, `disabled`, `focus`, `position`), Linaria's prop
interpolations become unwieldy. In those cases we use a
`computeDynamicStyles` function that returns a `CSSProperties` object
injected via `style={}`, referenced from the static CSS with `var()`:
```tsx
const StyledButton = styled.button`
background: var(--btn-bg);
border-color: var(--btn-border-color);
&:hover { background: var(--btn-hover-bg); }
`;
const dynamicStyles = useMemo(() => {
const s = computeButtonDynamicStyles(variant, accent, ...);
return { '--btn-bg': s.background, '--btn-hover-bg': s.hoverBackground } as CSSProperties;
}, [variant, accent, ...]);
return <StyledButton style={dynamicStyles} />;
```
### CSS var + unit concatenation
CSS custom properties can't be concatenated with unit suffixes directly
(`var(--x)px` is invalid). Values that need units use `calc()`:
```tsx
// Broken
transition: background ${themeCssVariables.animation.duration.instant}s ease;
// Fixed
transition: background calc(${themeCssVariables.animation.duration.instant} * 1s) ease;
```
This commit is contained in:
@@ -24,7 +24,7 @@ const StyledTaskBody = styled.div`
|
||||
max-width: calc(80% - ${({ theme }) => theme.spacing(2)});
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(0.25)};
|
||||
padding-bottom: 1px;
|
||||
`;
|
||||
|
||||
const StyledTaskTitle = styled.div<{
|
||||
@@ -33,7 +33,7 @@ const StyledTaskTitle = styled.div<{
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(0.25)};
|
||||
padding-bottom: 1px;
|
||||
text-decoration: ${({ completed }) => (completed ? 'line-through' : 'none')};
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -49,7 +49,7 @@ const StyledMessageText = styled.div<{ isUser?: boolean }>`
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
line-height: 1.4;
|
||||
padding: ${({ theme }) => `${theme.spacing(0.25)} ${theme.spacing(0.75)}`};
|
||||
padding: 1px 3px;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ const StyledThreadItem = styled.div<{ isSelected?: boolean }>`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
border-left: 3px solid transparent;
|
||||
cursor: pointer;
|
||||
padding: ${({ theme }) => theme.spacing(1, 0.25)};
|
||||
right: ${({ theme }) => theme.spacing(0.75)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} 1px;
|
||||
right: 3px;
|
||||
position: relative;
|
||||
width: calc(100% + ${({ theme }) => theme.spacing(0.25)});
|
||||
width: calc(100% + 1px);
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
|
||||
+4
-7
@@ -1,9 +1,6 @@
|
||||
import { ThemeProvider as EmotionThemeProvider } from '@emotion/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import {
|
||||
THEME_LIGHT,
|
||||
ThemeContextProvider,
|
||||
ThemeProvider,
|
||||
} from 'twenty-ui/theme';
|
||||
import { THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme';
|
||||
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
|
||||
|
||||
import { AIChatAssistantMessageRenderer } from '@/ai/components/AIChatAssistantMessageRenderer';
|
||||
@@ -46,14 +43,14 @@ jest.mock('@/ai/components/CodeExecutionDisplay', () => ({
|
||||
|
||||
const renderAssistantRenderer = (messageParts: ExtendedUIMessagePart[]) => {
|
||||
return render(
|
||||
<ThemeProvider theme={THEME_LIGHT}>
|
||||
<EmotionThemeProvider theme={THEME_LIGHT}>
|
||||
<ThemeContextProvider theme={THEME_LIGHT}>
|
||||
<AIChatAssistantMessageRenderer
|
||||
messageParts={messageParts}
|
||||
isLastMessageStreaming={false}
|
||||
/>
|
||||
</ThemeContextProvider>
|
||||
</ThemeProvider>,
|
||||
</EmotionThemeProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+4
-7
@@ -1,10 +1,7 @@
|
||||
import { ThemeProvider as EmotionThemeProvider } from '@emotion/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {
|
||||
THEME_LIGHT,
|
||||
ThemeContextProvider,
|
||||
ThemeProvider,
|
||||
} from 'twenty-ui/theme';
|
||||
import { THEME_LIGHT, ThemeContextProvider } from 'twenty-ui/theme';
|
||||
|
||||
import { ThinkingStepsDisplay } from '@/ai/components/ThinkingStepsDisplay';
|
||||
import { type ThinkingStepPart } from '@/ai/utils/thinkingStepPart';
|
||||
@@ -84,7 +81,7 @@ const renderThinkingStepsDisplay = ({
|
||||
hasAssistantTextResponseStarted?: boolean;
|
||||
}) => {
|
||||
return render(
|
||||
<ThemeProvider theme={THEME_LIGHT}>
|
||||
<EmotionThemeProvider theme={THEME_LIGHT}>
|
||||
<ThemeContextProvider theme={THEME_LIGHT}>
|
||||
<ThinkingStepsDisplay
|
||||
parts={parts}
|
||||
@@ -92,7 +89,7 @@ const renderThinkingStepsDisplay = ({
|
||||
hasAssistantTextResponseStarted={hasAssistantTextResponseStarted}
|
||||
/>
|
||||
</ThemeContextProvider>
|
||||
</ThemeProvider>,
|
||||
</EmotionThemeProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ const StyledIconContainer = styled.div<{
|
||||
flex-shrink: 0;
|
||||
height: ${({ size }) => (size === 'small' ? '14px' : 'auto')};
|
||||
justify-content: center;
|
||||
padding: ${({ theme, size }) =>
|
||||
size === 'small' ? '0' : theme.spacing(1.25)};
|
||||
padding: ${({ size }) => (size === 'small' ? '0' : '5px')};
|
||||
width: ${({ size }) => (size === 'small' ? '14px' : 'auto')};
|
||||
`;
|
||||
|
||||
|
||||
+4
-4
@@ -38,11 +38,11 @@ const StyledViewOverlay = styled.div<{ $backgroundColor: string }>`
|
||||
border-radius: 4px;
|
||||
bottom: -7px;
|
||||
display: flex;
|
||||
height: ${({ theme }) => theme.spacing(3.5)};
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
width: ${({ theme }) => theme.spacing(3.5)};
|
||||
width: 14px;
|
||||
`;
|
||||
|
||||
export type ObjectIconWithViewOverlayProps = {
|
||||
@@ -69,14 +69,14 @@ export const ObjectIconWithViewOverlay = ({
|
||||
$borderColor={objectStyle.borderColor}
|
||||
>
|
||||
<ObjectIcon
|
||||
size={theme.spacing(3.5)}
|
||||
size="14px"
|
||||
stroke={theme.icon.stroke.md}
|
||||
color={objectStyle.iconColor}
|
||||
/>
|
||||
</StyledObjectIconWrapper>
|
||||
<StyledViewOverlay $backgroundColor={theme.grayScale.gray4}>
|
||||
<ViewIcon
|
||||
size={theme.spacing(2.5)}
|
||||
size="10px"
|
||||
stroke={theme.icon.stroke.lg}
|
||||
color={theme.grayScale.gray10}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { themeColorSchema, type ThemeColor } from 'twenty-ui/theme';
|
||||
import { type ThemeColor } from 'twenty-ui/theme';
|
||||
import { themeColorSchema } from 'twenty-ui/utilities';
|
||||
|
||||
import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant';
|
||||
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ const StyledTabsPill = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.pill};
|
||||
padding: ${({ theme }) => theme.spacing(0.75)};
|
||||
padding: 3px;
|
||||
height: ${({ theme }) => theme.spacing(7)};
|
||||
display: flex;
|
||||
width: ${({ theme }) => theme.spacing(18)};
|
||||
@@ -85,7 +85,7 @@ const StyledNewChatButtonWrapper = styled.div<{ isExpanded: boolean }>`
|
||||
isExpanded ? theme.spacing(7) : theme.spacing(6)};
|
||||
justify-content: center;
|
||||
padding: ${({ theme, isExpanded }) =>
|
||||
isExpanded ? theme.spacing(0.75) : theme.spacing(0.5)};
|
||||
isExpanded ? '3px' : theme.spacing(0.5)};
|
||||
width: ${({ theme, isExpanded }) =>
|
||||
isExpanded ? theme.spacing(25.75) : theme.spacing(6)};
|
||||
transition:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { metadataLabelSchema } from '@/object-metadata/validation-schemas/metadataLabelSchema';
|
||||
import { themeColorSchema } from 'twenty-ui/theme';
|
||||
import { themeColorSchema } from 'twenty-ui/utilities';
|
||||
import { FieldMetadataType, RelationType } from '~/generated-metadata/graphql';
|
||||
import { camelCaseStringSchema } from '~/utils/validation-schemas/camelCaseStringSchema';
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { themeColorSchema } from 'twenty-ui/theme';
|
||||
import { themeColorSchema } from 'twenty-ui/utilities';
|
||||
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/computeOptionValueFromLabel';
|
||||
|
||||
const selectOptionSchema = z
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ const StyledCardBodyContainer = styled.div`
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(0.5)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-left: ${({ theme }) => theme.spacing(2.5)};
|
||||
padding-left: 10px;
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
span {
|
||||
align-items: center;
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ import { useContext, type ReactNode } from 'react';
|
||||
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
|
||||
import { isFieldIdentifierDisplay } from '@/object-record/record-field/ui/meta-types/display/utils/isFieldIdentifierDisplay';
|
||||
import { RECORD_CHIP_CLICK_OUTSIDE_ID } from '@/object-record/record-table/constants/RecordChipClickOutsideId';
|
||||
import { RECORD_TABLE_ROW_HEIGHT } from '@/object-record/record-table/constants/RecordTableRowHeight';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { useOpenRecordTableCellFromCell } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellFromCell';
|
||||
import { ThemeContext } from 'twenty-ui/theme';
|
||||
@@ -21,7 +20,7 @@ const StyledBaseContainer = styled.div<{
|
||||
box-sizing: border-box;
|
||||
cursor: ${({ isReadOnly }) => (isReadOnly ? 'default' : 'pointer')};
|
||||
display: flex;
|
||||
height: ${RECORD_TABLE_ROW_HEIGHT}px;
|
||||
height: 32px;
|
||||
user-select: none;
|
||||
|
||||
position: relative;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
const StyledVirtualizedRowContainer = styled.div<{
|
||||
pixelsFromTop: number;
|
||||
}>`
|
||||
height: ${RECORD_TABLE_ROW_HEIGHT + 1};
|
||||
height: 33px;
|
||||
position: absolute;
|
||||
top: ${({ pixelsFromTop }) => pixelsFromTop}px;
|
||||
`;
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
const StyledDebugRow = styled.div`
|
||||
position: absolute;
|
||||
left: 250px;
|
||||
top: ${({ theme }) => theme.spacing(1.25)};
|
||||
top: 5px;
|
||||
z-index: 20;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
background-color: ${({ theme }) => theme.color.gray3};
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ const StyledGraphContainer = styled.div`
|
||||
height: 240px;
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
padding-top: ${({ theme }) => theme.spacing(2.5)};
|
||||
padding-top: 10px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ type SettingsDataModelFieldSelectFormProps = {
|
||||
};
|
||||
|
||||
const StyledContainer = styled(CardContent)`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3.5)};
|
||||
padding-bottom: 14px;
|
||||
`;
|
||||
|
||||
const StyledOptionsLabel = styled.div<{
|
||||
|
||||
+3
-3
@@ -79,8 +79,8 @@ const StyledColorSample = styled(ColorSample)`
|
||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
margin-right: ${({ theme }) => theme.spacing(3.5)};
|
||||
margin-left: ${({ theme }) => theme.spacing(3.5)};
|
||||
margin-right: 14px;
|
||||
margin-left: 14px;
|
||||
`;
|
||||
|
||||
const StyledOptionInput = styled(SettingsTextInput)`Chip
|
||||
@@ -92,7 +92,7 @@ const StyledOptionInput = styled(SettingsTextInput)`Chip
|
||||
`;
|
||||
|
||||
const StyledIconGripVertical = styled(IconGripVertical)`
|
||||
margin-right: ${({ theme }) => theme.spacing(0.75)};
|
||||
margin-right: 3px;
|
||||
`;
|
||||
|
||||
const StyledLightIconButton = styled(LightIconButton)`
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
const StyledFooter = styled(Modal.Footer)`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
gap: ${({ theme }) => theme.spacing(2.5)};
|
||||
gap: 10px;
|
||||
justify-content: space-between;
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
height: auto;
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ const StyledBanner = styled(Banner, {
|
||||
background: ${({ allMatched, theme }) =>
|
||||
allMatched ? theme.accent.secondary : theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing(2.5)};
|
||||
padding: ${({ theme }) => theme.spacing(2) + ' 10px'};
|
||||
`;
|
||||
|
||||
const StyledText = styled('div', {
|
||||
|
||||
@@ -3,11 +3,10 @@ import { t } from '@lingui/core/macro';
|
||||
import { IconCheck, IconX } from 'twenty-ui/display';
|
||||
import { THEME_COMMON } from 'twenty-ui/theme';
|
||||
|
||||
const spacing = THEME_COMMON.spacingMultiplicator * 1;
|
||||
const iconSizeSm = THEME_COMMON.icon.size.sm;
|
||||
|
||||
const StyledBooleanFieldValue = styled.div`
|
||||
margin-left: ${spacing}px;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
|
||||
type BooleanDisplayProps = {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ExpandableList } from '@/ui/layout/expandable-list/components/Expandabl
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { RoundedLink } from 'twenty-ui/navigation';
|
||||
import { THEME_COMMON } from 'twenty-ui/theme';
|
||||
|
||||
type EmailsDisplayProps = {
|
||||
value?: FieldEmailsValue;
|
||||
@@ -13,12 +12,10 @@ type EmailsDisplayProps = {
|
||||
onEmailClick?: (email: string, event: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const themeSpacing = THEME_COMMON.spacingMultiplicator;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeSpacing * 1}px;
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
+1
-4
@@ -2,14 +2,11 @@ import { type FieldMultiSelectValue } from '@/object-record/record-field/ui/type
|
||||
import { styled } from '@linaria/react';
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
import { THEME_COMMON } from 'twenty-ui/theme';
|
||||
|
||||
const spacing1 = THEME_COMMON.spacing(1);
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${spacing1};
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
@@ -8,7 +8,6 @@ import { styled } from '@linaria/react';
|
||||
import { parsePhoneNumber } from 'libphonenumber-js';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { RoundedLink } from 'twenty-ui/navigation';
|
||||
import { THEME_COMMON } from 'twenty-ui/theme';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
type PhonesDisplayProps = {
|
||||
@@ -20,12 +19,10 @@ type PhonesDisplayProps = {
|
||||
) => void;
|
||||
};
|
||||
|
||||
const themeSpacing = THEME_COMMON.spacingMultiplicator;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeSpacing * 1}px;
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
|
||||
max-width: 100%;
|
||||
|
||||
@@ -5,7 +5,7 @@ const StyledInputErrorHelper = styled.div`
|
||||
color: ${({ theme }) => theme.color.red};
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
position: absolute;
|
||||
margin-top: ${({ theme }) => theme.spacing(0.25)};
|
||||
margin-top: 1px;
|
||||
`;
|
||||
|
||||
export const InputErrorHelper = ({
|
||||
|
||||
@@ -409,7 +409,7 @@ const StyledAutogrowWrapper = styled(AutogrowWrapper)<{
|
||||
: sizeVariant === 'md'
|
||||
? '28px'
|
||||
: '32px'};
|
||||
padding: 0 ${({ theme }) => theme.spacing(1.25)};
|
||||
padding: 0 5px;
|
||||
`;
|
||||
|
||||
const TextInputWithAutoGrowWrapper = forwardRef<
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ const StyledLabel = styled.span`
|
||||
const StyledDescription = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
margin-top: ${({ theme }) => theme.spacing(0.25)};
|
||||
margin-top: 1px;
|
||||
`;
|
||||
|
||||
const StyledIconPickerContainer = styled.div`
|
||||
|
||||
@@ -111,32 +111,14 @@ export default defineConfig(({ command, mode }) => {
|
||||
{
|
||||
...wyw({
|
||||
include: [
|
||||
'**/CurrencyDisplay.tsx',
|
||||
'**/twenty-ui/src/**/*.{ts,tsx}',
|
||||
'**/EllipsisDisplay.tsx',
|
||||
'**/ContactLink.tsx',
|
||||
'**/BooleanDisplay.tsx',
|
||||
'**/LinksDisplay.tsx',
|
||||
'**/RoundedLink.tsx',
|
||||
'**/OverflowingTextWithTooltip.tsx',
|
||||
'**/Chip.tsx',
|
||||
'**/Tag.tsx',
|
||||
'**/MultiSelectFieldDisplay.tsx',
|
||||
'**/RatingInput.tsx',
|
||||
'**/RecordTableCellContainer.tsx',
|
||||
'**/RecordTableCellDisplayContainer.tsx',
|
||||
'**/Avatar.tsx',
|
||||
'**/RecordTableBodyDroppable.tsx',
|
||||
'**/RecordTableCellBaseContainer.tsx',
|
||||
'**/RecordTableCellTd.tsx',
|
||||
'**/RecordTableCellStyleWrapper.tsx',
|
||||
'**/RecordTableHeaderDragDropColumn.tsx',
|
||||
'**/ActorDisplay.tsx',
|
||||
'**/BooleanDisplay.tsx',
|
||||
'**/CurrencyDisplay.tsx',
|
||||
'**/TextDisplay.tsx',
|
||||
'**/EllipsisDisplay.tsx',
|
||||
'**/AvatarChip.tsx',
|
||||
'**/URLDisplay.tsx',
|
||||
'**/EmailsDisplay.tsx',
|
||||
'**/PhonesDisplay.tsx',
|
||||
'**/MultiSelectDisplay.tsx',
|
||||
|
||||
@@ -147,6 +147,7 @@ export { safeParseRelativeDateFilterJSONStringified } from './safeParseRelativeD
|
||||
export { getGenericOperationName } from './sentry/getGenericOperationName';
|
||||
export { getHumanReadableNameFromCode } from './sentry/getHumanReadableNameFromCode';
|
||||
export { appendCopySuffix } from './strings/appendCopySuffix';
|
||||
export { camelToKebab } from './strings/camelToKebab';
|
||||
export { camelToSnakeCase } from './strings/camelToSnakeCase';
|
||||
export { capitalize } from './strings/capitalize';
|
||||
export { pascalCase } from './strings/pascalCase';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const camelToKebab = (str: string): string =>
|
||||
str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ThemeProvider } from '@emotion/react';
|
||||
import { type Preview } from '@storybook/react-vite';
|
||||
import { THEME_LIGHT, ThemeContextProvider } from '@ui/theme';
|
||||
|
||||
@@ -6,20 +5,12 @@ const preview: Preview = {
|
||||
tags: ['autodocs'],
|
||||
decorators: [
|
||||
(Story) => {
|
||||
// const mode = useDarkMode() ? 'Dark' : 'Light';
|
||||
|
||||
const theme = THEME_LIGHT;
|
||||
|
||||
/* useEffect(() => {
|
||||
document.documentElement.className = mode === 'Dark' ? 'dark' : 'light';
|
||||
}, [mode]);*/
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ThemeContextProvider theme={theme}>
|
||||
<Story />
|
||||
</ThemeContextProvider>
|
||||
</ThemeProvider>
|
||||
<ThemeContextProvider theme={theme}>
|
||||
<Story />
|
||||
</ThemeContextProvider>
|
||||
);
|
||||
},
|
||||
],
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@prettier/sync": "^0.5.2",
|
||||
"@swc/plugin-emotion": "14.6.0",
|
||||
"@types/babel__preset-env": "^7",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
@@ -26,9 +25,6 @@
|
||||
"vite-plugin-svgr": "^4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/is-prop-valid": "^1.3.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@linaria/react": "^6.2.1",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@sniptt/guards": "^0.2.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { VISIBILITY_HIDDEN } from '@ui/accessibility/utils/visibility-hidden';
|
||||
|
||||
const StyledSpan = styled.span`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { VISIBILITY_HIDDEN } from '@ui/accessibility/utils/visibility-hidden';
|
||||
|
||||
// eslint-disable-next-line twenty/styled-components-prefixed-with-styled
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
export const VISIBILITY_HIDDEN = css`
|
||||
export const VISIBILITY_HIDDEN = `
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type IconComponent } from '@ui/display';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
type PillProps = {
|
||||
className?: string;
|
||||
@@ -9,18 +10,18 @@ type PillProps = {
|
||||
|
||||
const StyledPill = styled.span`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.pill};
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
border-radius: ${themeCssVariables.border.radius.pill};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: inline-flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
font-size: ${themeCssVariables.font.size.xs};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
height: ${themeCssVariables.spacing[4]};
|
||||
justify-content: flex-end;
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
padding: ${({ theme }) => `0 ${theme.spacing(2)}`};
|
||||
line-height: ${themeCssVariables.text.lineHeight.lg};
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const Pill = ({ className, label, Icon }: PillProps) => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Avatar } from '@ui/display/avatar/components/Avatar';
|
||||
import { type AvatarType } from '@ui/display/avatar/types/AvatarType';
|
||||
import { type IconComponent } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext, themeCssVariables } from '@ui/theme';
|
||||
import { type Nullable } from '@ui/utilities';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledIconWithBackgroundContainer = styled.div<{
|
||||
@@ -21,13 +22,15 @@ const StyledIconWithBackgroundContainer = styled.div<{
|
||||
const StyledAvatarChipWrapper = styled.div<{
|
||||
isClickable: boolean;
|
||||
divider: AvatarChipProps['divider'];
|
||||
theme: any;
|
||||
}>`
|
||||
${({ divider, theme }) => {
|
||||
const borderStyle = (side: 'left' | 'right') =>
|
||||
`border-${side}: 1px solid ${theme.border.color.light};`;
|
||||
return divider ? borderStyle(divider) : '';
|
||||
}}
|
||||
border-left: ${({ divider }) =>
|
||||
divider === 'left'
|
||||
? `1px solid ${themeCssVariables.border.color.light}`
|
||||
: 'none'};
|
||||
border-right: ${({ divider }) =>
|
||||
divider === 'right'
|
||||
? `1px solid ${themeCssVariables.border.color.light}`
|
||||
: 'none'};
|
||||
|
||||
cursor: ${({ isClickable }) => (isClickable ? 'pointer' : 'inherit')};
|
||||
display: flex;
|
||||
@@ -58,7 +61,7 @@ export const AvatarChip = ({
|
||||
onClick,
|
||||
divider,
|
||||
}: AvatarChipProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
if (!isDefined(Icon)) {
|
||||
return (
|
||||
<Avatar
|
||||
@@ -79,7 +82,6 @@ export const AvatarChip = ({
|
||||
<StyledAvatarChipWrapper
|
||||
isClickable={isClickable}
|
||||
divider={divider}
|
||||
theme={theme}
|
||||
onClick={onClick}
|
||||
>
|
||||
<StyledIconWithBackgroundContainer
|
||||
@@ -101,7 +103,6 @@ export const AvatarChip = ({
|
||||
<StyledAvatarChipWrapper
|
||||
isClickable={isClickable}
|
||||
divider={divider}
|
||||
theme={theme}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { Fragment } from 'react/jsx-runtime';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { Chip, ChipVariant } from '@ui/components/chip/Chip';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledIconsContainer = styled.div`
|
||||
align-items: center;
|
||||
@@ -12,7 +13,7 @@ const StyledIconsContainer = styled.div`
|
||||
|
||||
const StyledChipContainer = styled.div`
|
||||
display: inline-flex;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
export type MultipleAvatarChipProps = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type Theme, withTheme } from '@emotion/react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
export enum ChipSize {
|
||||
Large = 'large',
|
||||
@@ -38,28 +38,28 @@ export type ChipProps = {
|
||||
emptyLabel?: string;
|
||||
};
|
||||
|
||||
const StyledDiv = withTheme(styled.div<{ theme: Theme }>`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`);
|
||||
const StyledDiv = styled.div`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledContainer = withTheme(styled.div<
|
||||
const StyledContainer = styled.div<
|
||||
Pick<
|
||||
ChipProps,
|
||||
'accent' | 'clickable' | 'disabled' | 'maxWidth' | 'size' | 'variant'
|
||||
> & { theme: Theme }
|
||||
>
|
||||
>`
|
||||
--chip-horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
||||
--chip-vertical-padding: ${({ theme }) => theme.spacing(1)};
|
||||
--chip-horizontal-padding: ${themeCssVariables.spacing[1]};
|
||||
--chip-vertical-padding: ${themeCssVariables.spacing[1]};
|
||||
|
||||
text-decoration: none;
|
||||
align-items: center;
|
||||
|
||||
color: ${({ theme, accent, disabled }) =>
|
||||
color: ${({ accent, disabled }) =>
|
||||
disabled
|
||||
? theme.font.color.light
|
||||
? themeCssVariables.font.color.light
|
||||
: accent === ChipAccent.TextPrimary
|
||||
? theme.font.color.primary
|
||||
: theme.font.color.secondary};
|
||||
? themeCssVariables.font.color.primary
|
||||
: themeCssVariables.font.color.secondary};
|
||||
|
||||
cursor: ${({ clickable, disabled, variant }) =>
|
||||
variant === ChipVariant.Transparent
|
||||
@@ -72,9 +72,11 @@ const StyledContainer = withTheme(styled.div<
|
||||
|
||||
display: inline-flex;
|
||||
justify-content: flex-start;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${({ theme, size }) =>
|
||||
size === ChipSize.Large ? theme.spacing(4) : theme.spacing(3)};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
height: ${({ size }) =>
|
||||
size === ChipSize.Large
|
||||
? themeCssVariables.spacing[4]
|
||||
: themeCssVariables.spacing[3]};
|
||||
max-width: ${({ maxWidth }) =>
|
||||
maxWidth
|
||||
? `calc(${maxWidth}px - 2 * var(--chip-horizontal-padding))`
|
||||
@@ -83,51 +85,52 @@ const StyledContainer = withTheme(styled.div<
|
||||
padding: var(--chip-vertical-padding) var(--chip-horizontal-padding);
|
||||
user-select: none;
|
||||
|
||||
font-weight: ${({ theme, accent }) =>
|
||||
accent === ChipAccent.TextSecondary ? theme.font.weight.medium : 'inherit'};
|
||||
font-weight: ${({ accent }) =>
|
||||
accent === ChipAccent.TextSecondary
|
||||
? themeCssVariables.font.weight.medium
|
||||
: 'inherit'};
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme, variant, disabled }) =>
|
||||
background-color: ${({ variant, disabled }) =>
|
||||
variant === ChipVariant.Regular && !disabled
|
||||
? theme.background.transparent.light
|
||||
? themeCssVariables.background.transparent.light
|
||||
: variant === ChipVariant.Highlighted
|
||||
? theme.background.transparent.medium
|
||||
? themeCssVariables.background.transparent.medium
|
||||
: variant === ChipVariant.Static
|
||||
? theme.background.transparent.light
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'inherit'};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${({ theme, disabled, variant }) =>
|
||||
background-color: ${({ disabled, variant }) =>
|
||||
variant === ChipVariant.Regular && !disabled
|
||||
? theme.background.transparent.medium
|
||||
? themeCssVariables.background.transparent.medium
|
||||
: variant === ChipVariant.Highlighted
|
||||
? theme.background.transparent.strong
|
||||
? themeCssVariables.background.transparent.strong
|
||||
: variant === ChipVariant.Static
|
||||
? theme.background.transparent.light
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'inherit'};
|
||||
}
|
||||
|
||||
background-color: ${({ theme, variant }) =>
|
||||
background-color: ${({ variant }) =>
|
||||
variant === ChipVariant.Highlighted || variant === ChipVariant.Static
|
||||
? theme.background.transparent.light
|
||||
? themeCssVariables.background.transparent.light
|
||||
: 'inherit'};
|
||||
|
||||
border: none;
|
||||
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
|
||||
& > svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
padding-left: ${({ theme, variant }) =>
|
||||
padding-left: ${({ variant }) =>
|
||||
variant === ChipVariant.Transparent
|
||||
? theme.spacing(0)
|
||||
? themeCssVariables.spacing[0]
|
||||
: 'var(--chip-horizontal-padding)'};
|
||||
`);
|
||||
`;
|
||||
|
||||
// TODO: refactor this
|
||||
const renderRightComponent = (
|
||||
rightComponent: (() => ReactNode) | ReactNode | null,
|
||||
) => {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { ComponentDecorator } from '@ui/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { ChipAccent, ChipSize, ChipVariant } from '../Chip';
|
||||
import { LinkChip } from '../LinkChip';
|
||||
|
||||
const meta: Meta<typeof LinkChip> = {
|
||||
title: 'UI/Display/Chip/LinkChip',
|
||||
component: LinkChip,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
),
|
||||
ComponentDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof LinkChip>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
label: 'Link Chip',
|
||||
to: '/example',
|
||||
size: ChipSize.Small,
|
||||
variant: ChipVariant.Regular,
|
||||
accent: ChipAccent.TextPrimary,
|
||||
},
|
||||
};
|
||||
@@ -1,24 +1,12 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
// TODO prastoin We should forbid barrel import within the twenty-ui package
|
||||
import { type IconComponent } from '@ui/display/icon/types/IconComponent';
|
||||
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import {
|
||||
BORDER_COMMON,
|
||||
THEME_COMMON,
|
||||
type ThemeColor,
|
||||
ThemeContext,
|
||||
type ThemeType,
|
||||
} from '@ui/theme';
|
||||
import { type ThemeColor, ThemeContext, themeCssVariables } from '@ui/theme';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const spacing5 = THEME_COMMON.spacing(5);
|
||||
const spacing2 = THEME_COMMON.spacing(2);
|
||||
const spacing1 = THEME_COMMON.spacing(1);
|
||||
|
||||
const StyledTag = styled.h3<{
|
||||
theme: ThemeType;
|
||||
color: TagColor;
|
||||
weight: TagWeight;
|
||||
variant: TagVariant;
|
||||
@@ -26,43 +14,35 @@ const StyledTag = styled.h3<{
|
||||
preventPadding?: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
background: ${({ color, theme }) => {
|
||||
if (color === 'transparent') {
|
||||
return 'transparent';
|
||||
} else {
|
||||
const themeColor = theme.tag.background[color];
|
||||
|
||||
if (!isDefined(themeColor)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Tag color ${color} is not defined in the theme`);
|
||||
return theme.tag.background.gray;
|
||||
} else {
|
||||
return themeColor;
|
||||
}
|
||||
}
|
||||
}};
|
||||
border-radius: ${BORDER_COMMON.radius.sm};
|
||||
color: ${({ color, theme }) =>
|
||||
background: ${({ color }) =>
|
||||
color === 'transparent'
|
||||
? theme.font.color.secondary
|
||||
: theme.tag.text[color]};
|
||||
? 'transparent'
|
||||
: (themeCssVariables.tag.background[color] ??
|
||||
themeCssVariables.tag.background.gray)};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
color: ${({ color }) =>
|
||||
color === 'transparent'
|
||||
? themeCssVariables.font.color.secondary
|
||||
: (themeCssVariables.tag.text[color] ??
|
||||
themeCssVariables.font.color.secondary)};
|
||||
display: inline-flex;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme, weight }) =>
|
||||
font-weight: ${({ weight }) =>
|
||||
weight === 'regular'
|
||||
? theme.font.weight.regular
|
||||
: theme.font.weight.medium};
|
||||
height: ${spacing5};
|
||||
? themeCssVariables.font.weight.regular
|
||||
: themeCssVariables.font.weight.medium};
|
||||
height: ${themeCssVariables.spacing[5]};
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: ${({ preventPadding }) => (preventPadding ? '0' : `0 ${spacing2}`)};
|
||||
border: ${({ variant, theme }) =>
|
||||
padding: ${({ preventPadding }) =>
|
||||
preventPadding ? '0' : `0 ${themeCssVariables.spacing[2]}`};
|
||||
border: ${({ variant }) =>
|
||||
variant === 'outline' || variant === 'border'
|
||||
? `1px ${variant === 'border' ? 'solid' : 'dashed'} ${theme.border.color.strong}`
|
||||
? `1px ${variant === 'border' ? 'solid' : 'dashed'} ${themeCssVariables.border.color.strong}`
|
||||
: 'none'};
|
||||
|
||||
gap: ${spacing1};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
|
||||
min-width: ${({ preventShrink }) => (preventShrink ? 'fit-content' : 'none')};
|
||||
`;
|
||||
@@ -98,7 +78,6 @@ type TagProps = {
|
||||
preventPadding?: boolean;
|
||||
};
|
||||
|
||||
// TODO: Find a way to have ellipsis and shrinkable tag in tag list while keeping good perf for table cells
|
||||
export const Tag = ({
|
||||
className,
|
||||
color,
|
||||
@@ -114,7 +93,6 @@ export const Tag = ({
|
||||
|
||||
return (
|
||||
<StyledTag
|
||||
theme={theme}
|
||||
className={className}
|
||||
color={color}
|
||||
onClick={onClick}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
export type AvatarGroupProps = {
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledBanner = styled.div<{ variant?: BannerVariant }>`
|
||||
align-items: center;
|
||||
backdrop-filter: blur(5px);
|
||||
background: ${({ theme, variant }) =>
|
||||
variant === 'danger' ? theme.color.red : theme.color.blue};
|
||||
background: ${({ variant }) =>
|
||||
variant === 'danger'
|
||||
? themeCssVariables.color.red
|
||||
: themeCssVariables.color.blue};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing(3)};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
|
||||
width: 100%;
|
||||
color: ${({ theme }) => theme.font.color.inverted};
|
||||
color: ${themeCssVariables.font.color.inverted};
|
||||
font-family: Inter;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
line-height: 150%;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
@@ -32,8 +35,10 @@ export const Banner = ({
|
||||
variant = 'default',
|
||||
className,
|
||||
children,
|
||||
}: BannerProps) => (
|
||||
<StyledBanner variant={variant} className={className}>
|
||||
{children}
|
||||
</StyledBanner>
|
||||
);
|
||||
}: BannerProps) => {
|
||||
return (
|
||||
<StyledBanner variant={variant} className={className}>
|
||||
{children}
|
||||
</StyledBanner>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
IconInfoCircle,
|
||||
@@ -8,18 +9,18 @@ import { AppTooltip } from '../../tooltip/AppTooltip';
|
||||
|
||||
const StyledBanner = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.accent.secondary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
background-color: ${themeCssVariables.accent.secondary};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.color.blue};
|
||||
color: ${themeCssVariables.color.blue};
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
height: 16px;
|
||||
@@ -28,12 +29,12 @@ const StyledIconContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledMessage = styled.p`
|
||||
color: ${({ theme }) => theme.color.blue};
|
||||
color: ${themeCssVariables.color.blue};
|
||||
flex-grow: 1;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { IconHelp, IconX } from '@ui/display/icon/components/TablerIcons';
|
||||
import { type IconComponent } from '@ui/display/icon/types/IconComponent';
|
||||
import { LightButton, LightIconButton } from '@ui/input';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
export type CalloutVariant =
|
||||
| 'info'
|
||||
@@ -12,38 +13,40 @@ export type CalloutVariant =
|
||||
| 'neutral'
|
||||
| 'success';
|
||||
|
||||
const StyledCalloutContainer = styled.div<{ variant: CalloutVariant }>`
|
||||
const StyledCalloutContainer = styled.div<{
|
||||
variant: CalloutVariant;
|
||||
}>`
|
||||
align-items: flex-start;
|
||||
background-color: ${({ theme, variant }) =>
|
||||
background-color: ${({ variant }) =>
|
||||
variant === 'info'
|
||||
? theme.accent.accent1
|
||||
? themeCssVariables.accent.accent1
|
||||
: variant === 'warning'
|
||||
? theme.color.orange1
|
||||
? themeCssVariables.color.orange1
|
||||
: variant === 'success'
|
||||
? theme.color.turquoise1
|
||||
? themeCssVariables.color.turquoise1
|
||||
: variant === 'error'
|
||||
? theme.color.red1
|
||||
: theme.color.gray1};
|
||||
? themeCssVariables.color.red1
|
||||
: themeCssVariables.color.gray1};
|
||||
border: 1px solid
|
||||
${({ theme, variant }) =>
|
||||
${({ variant }) =>
|
||||
variant === 'info'
|
||||
? theme.accent.accent6
|
||||
? themeCssVariables.accent.accent6
|
||||
: variant === 'warning'
|
||||
? theme.color.orange6
|
||||
? themeCssVariables.color.orange6
|
||||
: variant === 'success'
|
||||
? theme.color.turquoise6
|
||||
? themeCssVariables.color.turquoise6
|
||||
: variant === 'error'
|
||||
? theme.color.red6
|
||||
: theme.color.gray6};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
? themeCssVariables.color.red6
|
||||
: themeCssVariables.color.gray6};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
max-width: 512px;
|
||||
overflow: hidden;
|
||||
padding: ${({ theme }) =>
|
||||
`${theme.spacing(3)} ${theme.spacing(3)} ${theme.spacing(2)}`};
|
||||
padding: ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[3]}
|
||||
${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -52,56 +55,60 @@ const StyledHeader = styled.div`
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
min-height: ${({ theme }) => theme.spacing(6)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div<{ variant: CalloutVariant }>`
|
||||
const StyledIconContainer = styled.div<{
|
||||
variant: CalloutVariant;
|
||||
}>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
width: ${({ theme }) => theme.spacing(4)};
|
||||
color: ${({ theme, variant }) =>
|
||||
height: ${themeCssVariables.spacing[4]};
|
||||
width: ${themeCssVariables.spacing[4]};
|
||||
color: ${({ variant }) =>
|
||||
variant === 'info'
|
||||
? theme.accent.accent9
|
||||
? themeCssVariables.accent.accent9
|
||||
: variant === 'warning'
|
||||
? theme.color.orange9
|
||||
? themeCssVariables.color.orange9
|
||||
: variant === 'success'
|
||||
? theme.color.turquoise9
|
||||
? themeCssVariables.color.turquoise9
|
||||
: variant === 'error'
|
||||
? theme.color.red9
|
||||
: theme.color.gray9};
|
||||
? themeCssVariables.color.red9
|
||||
: themeCssVariables.color.gray9};
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
flex: 1;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledDescriptionWrapper = styled.div<{ hasAction: boolean }>`
|
||||
const StyledDescriptionWrapper = styled.div<{
|
||||
hasAction: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
padding-bottom: ${({ hasAction, theme }) =>
|
||||
hasAction ? 0 : theme.spacing(2)};
|
||||
padding-left: ${({ theme }) => theme.spacing(6)};
|
||||
padding-bottom: ${({ hasAction }) =>
|
||||
hasAction ? 0 : themeCssVariables.spacing[2]};
|
||||
padding-left: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
flex: 1;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
line-height: 1.4;
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
export type AnimatedCheckmarkProps = React.ComponentProps<
|
||||
typeof motion.path
|
||||
@@ -16,7 +17,7 @@ export const AnimatedCheckmark = ({
|
||||
duration = 0.5,
|
||||
size = 28,
|
||||
}: AnimatedCheckmarkProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { IconCheck } from '@ui/display/icon/components/TablerIcons';
|
||||
import { ThemeContext, themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.color.blue};
|
||||
background-color: ${themeCssVariables.color.blue};
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
height: 20px;
|
||||
@@ -19,7 +20,7 @@ export type CheckmarkProps = React.ComponentPropsWithoutRef<'div'> & {
|
||||
};
|
||||
|
||||
export const Checkmark = ({ className }: CheckmarkProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<StyledContainer className={className}>
|
||||
|
||||
@@ -1,56 +1,63 @@
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { type ThemeColor, type ThemeType } from '@ui/theme';
|
||||
import { type ThemeColor, themeCssVariables } from '@ui/theme';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type ColorSampleVariant = 'default' | 'pipeline';
|
||||
|
||||
export type ColorSampleProps = {
|
||||
type StyledColorSampleProps = {
|
||||
colorName: ThemeColor;
|
||||
color?: string;
|
||||
variant?: ColorSampleVariant;
|
||||
};
|
||||
|
||||
const getColor = (theme: ThemeType, colorName: ThemeColor, color?: string) => {
|
||||
export type ColorSampleProps = StyledColorSampleProps;
|
||||
|
||||
const getColor = (colorName: ThemeColor, color?: string) => {
|
||||
if (isDefined(color)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
return theme.tag.background[colorName];
|
||||
return themeCssVariables.tag.background[colorName];
|
||||
};
|
||||
|
||||
const getBorderColor = (theme: ThemeType, colorName: ThemeColor) => {
|
||||
return theme.tag.text[colorName];
|
||||
const getBorderColor = (colorName: ThemeColor) => {
|
||||
return themeCssVariables.tag.text[colorName];
|
||||
};
|
||||
|
||||
const StyledColorSample = styled.div<ColorSampleProps>`
|
||||
background-color: ${({ theme, colorName, color }) =>
|
||||
getColor(theme, colorName, color)};
|
||||
border: 1px solid
|
||||
${({ theme, colorName }) => getBorderColor(theme, colorName)};
|
||||
const StyledColorSample = styled.div<StyledColorSampleProps>`
|
||||
background-color: ${({ colorName, color }) => getColor(colorName, color)};
|
||||
border: ${({ variant, colorName }) =>
|
||||
variant === 'pipeline' ? '0' : `1px solid ${getBorderColor(colorName)}`};
|
||||
border-radius: 60px;
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
width: ${({ theme }) => theme.spacing(3)};
|
||||
height: ${themeCssVariables.spacing[4]};
|
||||
width: ${themeCssVariables.spacing[3]};
|
||||
align-items: ${({ variant }) =>
|
||||
variant === 'pipeline' ? 'center' : 'initial'};
|
||||
display: ${({ variant }) => (variant === 'pipeline' ? 'flex' : 'block')};
|
||||
justify-content: ${({ variant }) =>
|
||||
variant === 'pipeline' ? 'center' : 'initial'};
|
||||
|
||||
${({ colorName, color, theme, variant }) => {
|
||||
if (variant === 'pipeline')
|
||||
return css`
|
||||
align-items: center;
|
||||
border: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
&:after {
|
||||
background-color: ${getColor(theme, colorName, color)};
|
||||
border-radius: ${theme.border.radius.rounded};
|
||||
content: '';
|
||||
display: block;
|
||||
height: ${theme.spacing(1)};
|
||||
width: ${theme.spacing(1)};
|
||||
}
|
||||
`;
|
||||
}}
|
||||
&:after {
|
||||
background-color: ${({ colorName, color, variant }) =>
|
||||
variant === 'pipeline' ? getColor(colorName, color) : 'transparent'};
|
||||
border-radius: ${({ variant }) =>
|
||||
variant === 'pipeline' ? themeCssVariables.border.radius.rounded : '0'};
|
||||
content: ${({ variant }) => (variant === 'pipeline' ? "''" : 'none')};
|
||||
display: ${({ variant }) => (variant === 'pipeline' ? 'block' : 'none')};
|
||||
height: ${({ variant }) =>
|
||||
variant === 'pipeline' ? themeCssVariables.spacing[1] : '0'};
|
||||
width: ${({ variant }) =>
|
||||
variant === 'pipeline' ? themeCssVariables.spacing[1] : '0'};
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledColorSample as ColorSample };
|
||||
export const ColorSample = ({
|
||||
colorName,
|
||||
color,
|
||||
variant,
|
||||
}: ColorSampleProps) => {
|
||||
return (
|
||||
<StyledColorSample colorName={colorName} color={color} variant={variant} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,10 +9,6 @@ const meta: Meta<typeof ColorSample> = {
|
||||
component: ColorSample,
|
||||
decorators: [ComponentDecorator],
|
||||
args: { colorName: 'green' },
|
||||
argTypes: {
|
||||
as: { control: false },
|
||||
theme: { control: false },
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import styled from '@emotion/styled';
|
||||
import type { ReactElement } from 'react';
|
||||
import { type ReactElement } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
background: ${({ theme }) => theme.background.transparent.secondary};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
background: ${themeCssVariables.background.transparent.secondary};
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: ${({ theme }) => theme.spacing(3)};
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
padding: ${themeCssVariables.spacing[3]};
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledCommandContain = styled.div`
|
||||
font-family: ${({ theme }) => theme.code.font.family};
|
||||
font-family: ${themeCssVariables.code.font.family};
|
||||
`;
|
||||
|
||||
const StyledButtonContainer = styled.div`
|
||||
@@ -22,11 +23,11 @@ const StyledButtonContainer = styled.div`
|
||||
const StyledLineContainer = styled.div``;
|
||||
|
||||
const StyledLineStartSpan = styled.span`
|
||||
color: ${({ theme }) => theme.code.text.orange};
|
||||
color: ${themeCssVariables.code.text.orange};
|
||||
`;
|
||||
|
||||
const StyledLineSpan = styled.span`
|
||||
color: ${({ theme }) => theme.code.text.green};
|
||||
color: ${themeCssVariables.code.text.green};
|
||||
`;
|
||||
|
||||
type CommandBlockProps = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import IconAddressBookRaw from '@assets/icons/address-book.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconAddressBookProps = Pick<
|
||||
IconComponentProps,
|
||||
@@ -8,7 +9,7 @@ type IconAddressBookProps = Pick<
|
||||
>;
|
||||
|
||||
export const IconAddressBook = (props: IconAddressBookProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconAnthropicRaw from '@assets/icons/anthropic.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconBrandAnthropicProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandAnthropic = (props: IconBrandAnthropicProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconBrandGroqRaw from '@assets/icons/groq.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconBrandGroqProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandGroq = (props: IconBrandGroqProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconBrandMistralRaw from '@assets/icons/mistral.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconBrandMistralProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandMistral = (props: IconBrandMistralProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconBrandXaiRaw from '@assets/icons/xai.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconBrandXaiProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandXai = (props: IconBrandXaiProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { IconChartBar } from '@ui/display/icon/components/TablerIcons';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
const StyledRotatedIconWrapper = styled.div`
|
||||
display: inline-flex;
|
||||
@@ -15,7 +17,7 @@ type IconChartBarHorizontalProps = Pick<
|
||||
>;
|
||||
|
||||
export const IconChartBarHorizontal = (props: IconChartBarHorizontalProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.md;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.sm;
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconGmailRaw from '@assets/icons/gmail.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconGmailProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGmail = (props: IconGmailProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGmailRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconGoogleRaw from '@assets/icons/google.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconGoogleProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGoogle = (props: IconGoogleProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGoogleRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconGoogleCalendarRaw from '@assets/icons/google-calendar.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconGoogleCalendarProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconGoogleCalendar = (props: IconGoogleCalendarProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconGoogleCalendarRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconLockRaw from '@assets/icons/lock.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconLockCustomProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IconLockCustom = (props: IconLockCustomProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconLockRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconMicrosoftRaw from '@assets/icons/microsoft.svg?react';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
interface IconMicrosoftProps {
|
||||
size?: number | string;
|
||||
}
|
||||
|
||||
export const IconMicrosoft = (props: IconMicrosoftProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconMicrosoftRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconMicrosoftCalendarRaw from '@assets/icons/microsoft-calendar.svg?react';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
interface IconMicrosoftCalendarProps {
|
||||
size?: number | string;
|
||||
}
|
||||
|
||||
export const IconMicrosoftCalendar = (props: IconMicrosoftCalendarProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconMicrosoftCalendarRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconMicrosoftOutlookRaw from '@assets/icons/microsoft-outlook.svg?react';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
interface IconMicrosoftOutlookProps {
|
||||
size?: number | string;
|
||||
}
|
||||
|
||||
export const IconMicrosoftOutlook = (props: IconMicrosoftOutlookProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return <IconMicrosoftOutlookRaw height={size} width={size} />;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconRelationManyToOneRaw from '@assets/icons/many-to-one.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconRelationManyToOneProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconRelationManyToOne = (props: IconRelationManyToOneProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconTrashXOffRaw from '@assets/icons/trash-x-off.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconTrashXOffProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconTrashXOff = (props: IconTrashXOffProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import IconTwentyStarRaw from '@assets/icons/twenty-star.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IconTwentyStarProps = Pick<IconComponentProps, 'size' | 'stroke'>;
|
||||
|
||||
export const IconTwentyStar = (props: IconTwentyStarProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? 24;
|
||||
const stroke = props.stroke ?? theme.icon.stroke.md;
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import IllustrationIconArrayRaw from '@assets/icons/illustration-array.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
type IllustrationIconArrayProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconArray = (props: IllustrationIconArrayProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import IllustrationIconCalendarEventRaw from '@assets/icons/illustration-calendar-event.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
type IllustrationIconCalendarEventProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconCalendarEvent = (
|
||||
props: IllustrationIconCalendarEventProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import IllustrationIconCalendarTimeRaw from '@assets/icons/illustration-calendar-time.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconCalendarTimeProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconCalendarTime = (
|
||||
props: IllustrationIconCalendarTimeProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconCurrencyRaw from '@assets/icons/illustration-currency.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconCurrencyProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconCurrency = (
|
||||
props: IllustrationIconCurrencyProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconFileRaw from '@assets/icons/illustration-file.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconFileProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconFile = (props: IllustrationIconFileProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconJsonRaw from '@assets/icons/illustration-json.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconJsonProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconJson = (props: IllustrationIconJsonProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconLinkRaw from '@assets/icons/illustration-link.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconLinkProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconLink = (props: IllustrationIconLinkProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import IllustrationIconMailRaw from '@assets/icons/illustration-mail.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconMailProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconMail = (props: IllustrationIconMailProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import IllustrationIconManyToManyRaw from '@assets/icons/illustration-many-to-many.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconManyToManyProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconManyToMany = (
|
||||
props: IllustrationIconManyToManyProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import IllustrationIconMapRaw from '@assets/icons/illustration-map.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconMapProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconMap = (props: IllustrationIconMapProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import IllustrationIconNumbersRaw from '@assets/icons/illustration-numbers.svg?react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconNumbersProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconNumbers = (
|
||||
props: IllustrationIconNumbersProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconOneToManyRaw from '@assets/icons/illustration-one-to-many.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconOneToManyProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconOneToMany = (
|
||||
props: IllustrationIconOneToManyProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconOneToOneRaw from '@assets/icons/illustration-one-to-one.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconOneToOneProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconOneToOne = (
|
||||
props: IllustrationIconOneToOneProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconPhoneRaw from '@assets/icons/illustration-phone.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconPhoneProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconPhone = (props: IllustrationIconPhoneProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconSettingRaw from '@assets/icons/illustration-setting.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconSettingProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconSetting = (
|
||||
props: IllustrationIconSettingProps,
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconStarRaw from '@assets/icons/illustration-star.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconStarProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconStar = (props: IllustrationIconStarProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconTagRaw from '@assets/icons/illustration-tag.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconTagProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconTag = (props: IllustrationIconTagProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconTagsRaw from '@assets/icons/illustration-tags.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconTagsProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconTags = (props: IllustrationIconTagsProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconTextRaw from '@assets/icons/illustration-text.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconTextProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconText = (props: IllustrationIconTextProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconToggleRaw from '@assets/icons/illustration-toggle.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconToggleProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconToggle = (props: IllustrationIconToggleProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconUidRaw from '@assets/icons/illustration-uid.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconUidProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconUid = (props: IllustrationIconUidProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useContext } from 'react';
|
||||
import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper';
|
||||
|
||||
import IllustrationIconUserRaw from '@assets/icons/illustration-user.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
import { ThemeContext } from '@ui/theme';
|
||||
|
||||
type IllustrationIconUserProps = Pick<IconComponentProps, 'size'>;
|
||||
|
||||
export const IllustrationIconUser = (props: IllustrationIconUserProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
return (
|
||||
<IllustrationIconWrapper>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledRectangleIllustrationIcon = styled('div')`
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border: 0.75px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
// eslint-disable-next-line twenty/styled-components-prefixed-with-styled
|
||||
export const IllustrationIconWrapper = styled.div`
|
||||
background-color: ${themeCssVariables.background.primary};
|
||||
border: 0.75px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const IllustrationIconWrapper = StyledRectangleIllustrationIcon;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { IconInfoCircle } from '@ui/display/icon/components/TablerIcons';
|
||||
import { ThemeContext, themeCssVariables } from '@ui/theme';
|
||||
|
||||
import { Button } from '@ui/input/button/components/Button/Button';
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export type InfoAccent = 'blue' | 'danger';
|
||||
@@ -18,7 +18,7 @@ export type InfoProps = {
|
||||
const StyledTextContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledIconInfoCircle = styled(IconInfoCircle)`
|
||||
@@ -27,27 +27,33 @@ const StyledIconInfoCircle = styled(IconInfoCircle)`
|
||||
|
||||
const StyledInfo = styled.div<Pick<InfoProps, 'accent'>>`
|
||||
align-items: center;
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
display: flex;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
justify-content: space-between;
|
||||
max-width: 512px;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
${({ theme, accent }) => {
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
background: ${({ accent }) => {
|
||||
switch (accent) {
|
||||
case 'blue':
|
||||
return css`
|
||||
background: ${theme.color.blue5};
|
||||
color: ${theme.color.blue10};
|
||||
`;
|
||||
return themeCssVariables.color.blue5;
|
||||
case 'danger':
|
||||
return css`
|
||||
background: ${theme.color.red3};
|
||||
color: ${theme.color.red};
|
||||
`;
|
||||
return themeCssVariables.color.red3;
|
||||
default:
|
||||
return 'transparent';
|
||||
}
|
||||
}}
|
||||
}};
|
||||
color: ${({ accent }) => {
|
||||
switch (accent) {
|
||||
case 'blue':
|
||||
return themeCssVariables.color.blue10;
|
||||
case 'danger':
|
||||
return themeCssVariables.color.red;
|
||||
default:
|
||||
return 'inherit';
|
||||
}
|
||||
}};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
@@ -61,7 +67,7 @@ export const Info = ({
|
||||
onClick,
|
||||
to,
|
||||
}: InfoProps) => {
|
||||
const theme = useTheme();
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<StyledInfo accent={accent}>
|
||||
<StyledTextContainer>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { MAIN_COLOR_NAMES, type ThemeColor } from '@ui/theme';
|
||||
import { styled } from '@linaria/react';
|
||||
import {
|
||||
MAIN_COLOR_NAMES,
|
||||
themeCssVariables,
|
||||
type ThemeColor,
|
||||
} from '@ui/theme';
|
||||
|
||||
import { Loader } from '@ui/feedback/loader/components/Loader';
|
||||
|
||||
@@ -14,30 +18,32 @@ const StyledStatus = styled.h3<{
|
||||
isLoaderVisible: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
background: ${({ color, theme }) => theme.tag.background[color]};
|
||||
border-radius: ${({ theme }) => theme.border.radius.pill};
|
||||
color: ${({ color, theme }) => theme.tag.text[color]};
|
||||
background: ${({ color }) => themeCssVariables.tag.background[color]};
|
||||
border-radius: ${themeCssVariables.border.radius.pill};
|
||||
color: ${({ color }) => themeCssVariables.tag.text[color]};
|
||||
display: inline-flex;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme, weight }) => theme.font.weight[weight]};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${({ theme }) => theme.spacing(5)};
|
||||
font-weight: ${({ weight }) => themeCssVariables.font.weight[weight]};
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
height: ${themeCssVariables.spacing[5]};
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0
|
||||
${({ theme, isLoaderVisible }) =>
|
||||
isLoaderVisible ? theme.spacing(1) : theme.spacing(2)}
|
||||
0 ${({ theme }) => theme.spacing(2)};
|
||||
${({ isLoaderVisible }) =>
|
||||
isLoaderVisible
|
||||
? themeCssVariables.spacing[1]
|
||||
: themeCssVariables.spacing[2]}
|
||||
0 ${themeCssVariables.spacing[2]};
|
||||
|
||||
&:before {
|
||||
background-color: ${({ color, theme }) => theme.tag.text[color]};
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
background-color: ${({ color }) => themeCssVariables.tag.text[color]};
|
||||
border-radius: ${themeCssVariables.border.radius.rounded};
|
||||
content: '';
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
height: ${({ theme }) => theme.spacing(1)};
|
||||
width: ${({ theme }) => theme.spacing(1)};
|
||||
height: ${themeCssVariables.spacing[1]};
|
||||
width: ${themeCssVariables.spacing[1]};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -63,15 +69,17 @@ export const Status = ({
|
||||
text,
|
||||
onClick,
|
||||
weight = 'regular',
|
||||
}: StatusProps) => (
|
||||
<StyledStatus
|
||||
className={className}
|
||||
color={parseThemeColor(color)}
|
||||
onClick={onClick}
|
||||
weight={weight}
|
||||
isLoaderVisible={isLoaderVisible}
|
||||
>
|
||||
<StyledContent>{text}</StyledContent>
|
||||
{isLoaderVisible ? <Loader color={color} /> : null}
|
||||
</StyledStatus>
|
||||
);
|
||||
}: StatusProps) => {
|
||||
return (
|
||||
<StyledStatus
|
||||
className={className}
|
||||
color={parseThemeColor(color)}
|
||||
onClick={onClick}
|
||||
weight={weight}
|
||||
isLoaderVisible={isLoaderVisible}
|
||||
>
|
||||
<StyledContent>{text}</StyledContent>
|
||||
{isLoaderVisible ? <Loader color={color} /> : null}
|
||||
</StyledStatus>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { type JSX } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type JSX, useContext } from 'react';
|
||||
import { Label } from '@ui/display';
|
||||
import { THEME_COMMON, ThemeContext } from '@ui/theme';
|
||||
|
||||
const spacing3 = THEME_COMMON.spacing(3);
|
||||
const spacing2 = THEME_COMMON.spacing(2);
|
||||
|
||||
type HorizontalSeparatorProps = {
|
||||
visible?: boolean;
|
||||
@@ -8,31 +12,39 @@ type HorizontalSeparatorProps = {
|
||||
noMargin?: boolean;
|
||||
color?: string;
|
||||
};
|
||||
const StyledSeparator = styled.div<HorizontalSeparatorProps>`
|
||||
background-color: ${({ theme, color }) => color ?? theme.border.color.medium};
|
||||
height: ${({ visible }) => (visible ? '1px' : 0)};
|
||||
|
||||
const StyledSeparator = styled.div<{
|
||||
visible: boolean;
|
||||
noMargin: boolean;
|
||||
backgroundColor: string;
|
||||
}>`
|
||||
background-color: ${({ backgroundColor }) => backgroundColor};
|
||||
height: ${({ visible }) => (visible ? '1px' : '0')};
|
||||
flex-shrink: 0;
|
||||
margin-bottom: ${({ theme, noMargin }) => (noMargin ? 0 : theme.spacing(3))};
|
||||
margin-top: ${({ theme, noMargin }) => (noMargin ? 0 : theme.spacing(3))};
|
||||
margin-bottom: ${({ noMargin }) => (noMargin ? '0' : spacing3)};
|
||||
margin-top: ${({ noMargin }) => (noMargin ? '0' : spacing3)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledSeparatorContainer = styled.div<{ noMargin: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: ${({ theme, noMargin }) => (noMargin ? 0 : theme.spacing(3))};
|
||||
margin-top: ${({ theme, noMargin }) => (noMargin ? 0 : theme.spacing(3))};
|
||||
margin-bottom: ${({ noMargin }) => (noMargin ? '0' : spacing3)};
|
||||
margin-top: ${({ noMargin }) => (noMargin ? '0' : spacing3)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledLine = styled.div<HorizontalSeparatorProps>`
|
||||
background-color: ${({ theme }) => theme.border.color.medium};
|
||||
height: ${({ visible }) => (visible ? '1px' : 0)};
|
||||
const StyledLine = styled.div<{
|
||||
visible: boolean;
|
||||
backgroundColor: string;
|
||||
}>`
|
||||
background-color: ${({ backgroundColor }) => backgroundColor};
|
||||
height: ${({ visible }) => (visible ? '1px' : '0')};
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const StyledText = styled.span`
|
||||
margin: 0 ${({ theme }) => theme.spacing(2)};
|
||||
margin: 0 ${spacing2};
|
||||
`;
|
||||
|
||||
export const HorizontalSeparator = ({
|
||||
@@ -40,18 +52,27 @@ export const HorizontalSeparator = ({
|
||||
text = '',
|
||||
noMargin = false,
|
||||
color,
|
||||
}: HorizontalSeparatorProps): JSX.Element => (
|
||||
<>
|
||||
{text ? (
|
||||
<StyledSeparatorContainer noMargin={noMargin}>
|
||||
<StyledLine visible={visible} />
|
||||
<Label>
|
||||
<StyledText>{text}</StyledText>
|
||||
</Label>
|
||||
<StyledLine visible={visible} />
|
||||
</StyledSeparatorContainer>
|
||||
) : (
|
||||
<StyledSeparator visible={visible} noMargin={noMargin} color={color} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}: HorizontalSeparatorProps): JSX.Element => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const borderColor = color ?? theme.border.color.medium;
|
||||
|
||||
return (
|
||||
<>
|
||||
{text ? (
|
||||
<StyledSeparatorContainer noMargin={noMargin}>
|
||||
<StyledLine visible={visible} backgroundColor={borderColor} />
|
||||
<Label>
|
||||
<StyledText>{text}</StyledText>
|
||||
</Label>
|
||||
<StyledLine visible={visible} backgroundColor={borderColor} />
|
||||
</StyledSeparatorContainer>
|
||||
) : (
|
||||
<StyledSeparator
|
||||
visible={visible}
|
||||
noMargin={noMargin}
|
||||
backgroundColor={borderColor}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
color: ${({ theme }) => theme.font.color.extraLight};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
color: ${themeCssVariables.font.color.extraLight};
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
flex-grow: 1;
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
}
|
||||
|
||||
&:before {
|
||||
margin: 0 ${({ theme }) => theme.spacing(4)} 0 0;
|
||||
margin: 0 ${themeCssVariables.spacing[4]} 0 0;
|
||||
}
|
||||
&:after {
|
||||
margin: 0 0 0 ${({ theme }) => theme.spacing(4)};
|
||||
margin: 0 0 0 ${themeCssVariables.spacing[4]};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { type PlacesType, type PositionStrategy, Tooltip } from 'react-tooltip';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
export enum TooltipPosition {
|
||||
Top = 'top',
|
||||
@@ -16,24 +17,24 @@ export enum TooltipDelay {
|
||||
}
|
||||
|
||||
const StyledAppTooltip = styled(Tooltip)<{ width?: string }>`
|
||||
backdrop-filter: ${({ theme }) => theme.blur.strong};
|
||||
background-color: ${({ theme }) => theme.color.transparent.gray11};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
backdrop-filter: ${themeCssVariables.blur.strong};
|
||||
background-color: ${themeCssVariables.color.transparent.gray11};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.light};
|
||||
color: ${({ theme }) => theme.grayScale.gray1};
|
||||
box-shadow: ${themeCssVariables.boxShadow.light};
|
||||
color: ${themeCssVariables.grayScale.gray1};
|
||||
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
|
||||
max-width: ${({ width }) => width || '40%'};
|
||||
overflow: visible;
|
||||
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
|
||||
word-break: break-word;
|
||||
|
||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||
z-index: ${themeCssVariables.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
export type AppTooltipProps = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
type H1TitleProps = {
|
||||
title: ReactNode;
|
||||
@@ -16,12 +17,12 @@ export enum H1TitleFontColor {
|
||||
const StyledTitle = styled.h2<{
|
||||
fontColor: H1TitleFontColor;
|
||||
}>`
|
||||
color: ${({ theme, fontColor }) => theme.font.color[fontColor]};
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.md};
|
||||
color: ${({ fontColor }) => themeCssVariables.font.color[fontColor]};
|
||||
font-size: ${themeCssVariables.font.size.lg};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
line-height: ${themeCssVariables.text.lineHeight.md};
|
||||
margin: 0;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
margin-bottom: ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
export const H1Title = ({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
type H2TitleProps = {
|
||||
title: string;
|
||||
@@ -11,7 +12,7 @@ type H2TitleProps = {
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
margin-bottom: ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
const StyledTitleContainer = styled.div`
|
||||
@@ -21,18 +22,18 @@ const StyledTitleContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.h2`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.h3`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
margin: 0;
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const H2Title = ({
|
||||
@@ -40,20 +41,22 @@ export const H2Title = ({
|
||||
description,
|
||||
adornment,
|
||||
className,
|
||||
}: H2TitleProps) => (
|
||||
<StyledContainer className={className}>
|
||||
<StyledTitleContainer>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
{adornment}
|
||||
</StyledTitleContainer>
|
||||
{description && (
|
||||
<StyledDescription>
|
||||
<OverflowingTextWithTooltip
|
||||
text={description}
|
||||
displayedMaxRows={5}
|
||||
isTooltipMultiline={true}
|
||||
/>
|
||||
</StyledDescription>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
}: H2TitleProps) => {
|
||||
return (
|
||||
<StyledContainer className={className}>
|
||||
<StyledTitleContainer>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
{adornment}
|
||||
</StyledTitleContainer>
|
||||
{description && (
|
||||
<StyledDescription>
|
||||
<OverflowingTextWithTooltip
|
||||
text={description}
|
||||
displayedMaxRows={5}
|
||||
isTooltipMultiline={true}
|
||||
/>
|
||||
</StyledDescription>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { type ReactNode } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { OverflowingTextWithTooltip } from '@ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
type H3TitleProps = {
|
||||
title: ReactNode;
|
||||
@@ -14,18 +15,18 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
const StyledH3Title = styled.h3`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-size: ${themeCssVariables.font.size.lg};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.h4`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
margin: 0;
|
||||
margin-top: ${({ theme }) => theme.spacing(2)};
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const H3Title = ({ title, description, className }: H3TitleProps) => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
export type LabelVariant = 'default' | 'small';
|
||||
|
||||
const StyledLabel = styled.div<{ variant?: LabelVariant }>`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-size: ${({ variant = 'default' }) => {
|
||||
switch (variant) {
|
||||
case 'default':
|
||||
@@ -12,7 +13,19 @@ const StyledLabel = styled.div<{ variant?: LabelVariant }>`
|
||||
return '9px';
|
||||
}
|
||||
}};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
`;
|
||||
|
||||
export { StyledLabel as Label };
|
||||
type LabelProps = {
|
||||
variant?: LabelVariant;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const Label = ({ variant, children, className }: LabelProps) => {
|
||||
return (
|
||||
<StyledLabel variant={variant} className={className}>
|
||||
{children}
|
||||
</StyledLabel>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type ReactElement, type ReactNode } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from '@ui/theme';
|
||||
|
||||
type StyledTextProps = {
|
||||
PrefixComponent?: ReactElement;
|
||||
@@ -8,8 +9,8 @@ type StyledTextProps = {
|
||||
};
|
||||
|
||||
export const StyledTextContent = styled.div`
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
|
||||
overflow: hidden;
|
||||
padding-left: 0;
|
||||
@@ -20,8 +21,8 @@ export const StyledTextContent = styled.div`
|
||||
export const StyledTextWrapper = styled.div<{
|
||||
color?: string;
|
||||
}>`
|
||||
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
||||
--vertical-padding: ${({ theme }) => theme.spacing(2)};
|
||||
--horizontal-padding: ${themeCssVariables.spacing[1]};
|
||||
--vertical-padding: ${themeCssVariables.spacing[2]};
|
||||
|
||||
cursor: initial;
|
||||
|
||||
@@ -29,13 +30,13 @@ export const StyledTextWrapper = styled.div<{
|
||||
|
||||
flex-direction: row;
|
||||
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
|
||||
padding: var(--vertical-padding) 0;
|
||||
|
||||
color: ${({ theme, color }) => color ?? theme.font.color.primary};
|
||||
color: ${({ color }) => color ?? themeCssVariables.font.color.primary};
|
||||
`;
|
||||
|
||||
export const StyledText = ({
|
||||
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
import { type ThemeType } from './theme';
|
||||
|
||||
declare module '@emotion/react' {
|
||||
export interface Theme extends ThemeType {}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user