Remove all styled(Component) patterns in favor of parent wrappers and props (#18430)
## Summary
Eliminates all ~350 `styled(Component)` usages across `twenty-front` and
`twenty-ui` (212 files changed). Each was replaced following these
rules:
- **Margin/layout CSS** (margin, padding, flex, align-self, width) →
wrapped in a `styled.div`/`styled.span` parent container
- **Third-party components** (Link, TextareaAutosize,
ReactPhoneNumberInput, Handle, etc.) → parent container with child CSS
selectors (`> a`, `> textarea`, `> input`, etc.)
- **Intrinsic behavior via existing props** (TableRow
`gridTemplateColumns`, TableCell `color`/`align`) → replaced
`styled(TableRow)` / `styled(TableCell)` with direct prop usage
- **Other visual overrides on twenty-ui components** (Card, Section,
TabList, Button, MenuItem, ScrollWrapper, etc.) → parent wrappers with
`> div` / `> *` child selectors
- **Extending styled.div/span** → merged all CSS into a single
`styled.div`/`styled.span`
Also adds `overflow: hidden` to parent containers wrapping
`ScrollWrapper` so scroll activates correctly with the new wrapper
structure.
### Migration patterns
| Before | After |
|--------|-------|
| `styled(Avatar)` with `margin-right` | `<StyledAvatarContainer><Avatar
/></StyledAvatarContainer>` |
| `styled(Link)` with `text-decoration: none` |
`<StyledLinkContainer><Link /></StyledLinkContainer>` with `> a { ... }`
|
| `styled(TableRow)` with `grid-template-columns` | `<TableRow
gridTemplateColumns="..." />` |
| `styled(TableCell)` with `color` / `align` | `<TableCell color={...}
align="right" />` |
| `styled(Card)` with `margin-top` | `<StyledCardContainer><Card
/></StyledCardContainer>` |
| `styled(TabList)` with `background` |
`<StyledTabListContainer><TabList /></StyledTabListContainer>` with `>
div { ... }` |
| `styled(StyledBase)` extending a `styled.div` | Single merged
`styled.div` with all styles inlined |
This commit is contained in:
+14
-11
@@ -55,7 +55,8 @@ const StyledDialogMessage = styled.span`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledDialogButton = styled(Button)`
|
||||
const StyledDialogButtonContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -157,16 +158,18 @@ export const Dialog = ({
|
||||
{message && <StyledDialogMessage>{message}</StyledDialogMessage>}
|
||||
{children}
|
||||
{buttons.map(({ accent, onClick, role, title: key, variant }) => (
|
||||
<StyledDialogButton
|
||||
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
onClose?.();
|
||||
onClick?.(event);
|
||||
}}
|
||||
fullWidth={true}
|
||||
variant={variant ?? 'secondary'}
|
||||
title={key}
|
||||
{...{ accent, key, role }}
|
||||
/>
|
||||
<StyledDialogButtonContainer key={key}>
|
||||
<Button
|
||||
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
onClose?.();
|
||||
onClick?.(event);
|
||||
}}
|
||||
fullWidth={true}
|
||||
variant={variant ?? 'secondary'}
|
||||
title={key}
|
||||
{...{ accent, role }}
|
||||
/>
|
||||
</StyledDialogButtonContainer>
|
||||
))}
|
||||
</StyledDialogContainer>
|
||||
</StyledDialogOverlay>
|
||||
|
||||
+23
-17
@@ -68,7 +68,7 @@ const StyledContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledProgressBar = styled(ProgressBar)`
|
||||
const StyledProgressBarContainer = styled.div`
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
left: 0;
|
||||
@@ -112,17 +112,19 @@ const StyledDescription = styled.div`
|
||||
width: 200px;
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
display: block;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
padding-left: ${themeCssVariables.spacing[6]};
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
&:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
const StyledLinkContainer = styled.div`
|
||||
> a {
|
||||
display: block;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
padding-left: ${themeCssVariables.spacing[6]};
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
&:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -229,10 +231,12 @@ export const SnackBar = ({
|
||||
role={role}
|
||||
data-globally-prevent-click-outside
|
||||
>
|
||||
<StyledProgressBar
|
||||
barColor={theme.snackBar[variant].backgroundColor}
|
||||
value={progressValue}
|
||||
/>
|
||||
<StyledProgressBarContainer>
|
||||
<ProgressBar
|
||||
barColor={theme.snackBar[variant].backgroundColor}
|
||||
value={progressValue}
|
||||
/>
|
||||
</StyledProgressBarContainer>
|
||||
<StyledHeader>
|
||||
<StyledIcon>{icon}</StyledIcon>
|
||||
<StyledMessage>{sanitizedMessage ?? ''}</StyledMessage>
|
||||
@@ -248,7 +252,9 @@ export const SnackBar = ({
|
||||
<StyledDescription>{sanitizedDetailedMessage}</StyledDescription>
|
||||
)}
|
||||
{actionText && actionTo && (
|
||||
<StyledLink to={actionTo}>{actionText}</StyledLink>
|
||||
<StyledLinkContainer>
|
||||
<Link to={actionTo}>{actionText}</Link>
|
||||
</StyledLinkContainer>
|
||||
)}
|
||||
{actionText && actionOnClick && !actionTo && (
|
||||
<StyledActionButton>
|
||||
|
||||
@@ -9,25 +9,27 @@ import { IMaskInput } from 'react-imask';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledIMaskInput = styled(IMaskInput)`
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1.5]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
export const StyledIMaskInput = styled.div`
|
||||
> input {
|
||||
margin: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[1.5]};
|
||||
|
||||
width: 100%;
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@@ -124,19 +126,21 @@ export const CurrencyInput = ({
|
||||
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||
)}
|
||||
</StyledIcon>
|
||||
<StyledIMaskInput
|
||||
mask={Number}
|
||||
thousandsSeparator=","
|
||||
radix="."
|
||||
scale={decimals}
|
||||
onAccept={(value: string) => handleChange(value)}
|
||||
inputRef={wrapperRef}
|
||||
autoComplete="off"
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
unmask
|
||||
/>
|
||||
<StyledIMaskInput>
|
||||
<IMaskInput
|
||||
mask={Number}
|
||||
thousandsSeparator=","
|
||||
radix="."
|
||||
scale={decimals}
|
||||
onAccept={(value: string) => handleChange(value)}
|
||||
inputRef={wrapperRef}
|
||||
autoComplete="off"
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
unmask
|
||||
/>
|
||||
</StyledIMaskInput>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,31 +25,33 @@ export type TextAreaInputProps = {
|
||||
copyButton?: boolean;
|
||||
};
|
||||
|
||||
const StyledTextArea = styled(TextareaAutosize)`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
const StyledTextAreaContainer = styled.div`
|
||||
> textarea {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
resize: none;
|
||||
max-height: 400px;
|
||||
width: calc(100% - ${themeCssVariables.spacing[7]});
|
||||
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
resize: none;
|
||||
max-height: 400px;
|
||||
width: calc(100% - ${themeCssVariables.spacing[7]});
|
||||
|
||||
line-height: 18px;
|
||||
`;
|
||||
|
||||
const StyledLightIconButtonContainer = styled.div`
|
||||
@@ -111,16 +113,18 @@ export const TextAreaInput = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTextArea
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
ref={wrapperRef}
|
||||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
value={internalText}
|
||||
maxRows={maxRows}
|
||||
/>
|
||||
<StyledTextAreaContainer>
|
||||
<TextareaAutosize
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
ref={wrapperRef}
|
||||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
value={internalText}
|
||||
maxRows={maxRows}
|
||||
/>
|
||||
</StyledTextAreaContainer>
|
||||
{copyButton && (
|
||||
<StyledLightIconButtonContainer ref={copyRef}>
|
||||
<LightCopyIconButton copyText={internalText} />
|
||||
|
||||
@@ -40,34 +40,36 @@ const StyledLabel = styled.label`
|
||||
margin-bottom: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledTextArea = styled(TextareaAutosize)`
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
box-sizing: border-box;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: inherit;
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
line-height: 16px;
|
||||
overflow: auto;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
resize: none;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0px 0px 0px 3px ${themeCssVariables.color.transparent.blue2};
|
||||
border-color: ${themeCssVariables.color.blue};
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
const StyledTextAreaContainer = styled.div`
|
||||
> textarea {
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
box-sizing: border-box;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: inherit;
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
}
|
||||
line-height: 16px;
|
||||
overflow: auto;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
resize: none;
|
||||
width: 100%;
|
||||
|
||||
&:disabled {
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0px 0px 0px 3px ${themeCssVariables.color.transparent.blue2};
|
||||
border-color: ${themeCssVariables.color.blue};
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -115,22 +117,24 @@ export const TextArea = ({
|
||||
<StyledContainer>
|
||||
{label && <StyledLabel htmlFor={instanceId}>{label}</StyledLabel>}
|
||||
|
||||
<StyledTextArea
|
||||
id={instanceId}
|
||||
placeholder={placeholder}
|
||||
maxRows={maxRows}
|
||||
minRows={computedMinRows}
|
||||
value={value}
|
||||
onChange={(event) =>
|
||||
onChange?.(turnIntoEmptyStringIfWhitespacesOnly(event.target.value))
|
||||
}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
readOnly={readOnly}
|
||||
style={isDefined(height) ? { height } : undefined}
|
||||
/>
|
||||
<StyledTextAreaContainer>
|
||||
<TextareaAutosize
|
||||
id={instanceId}
|
||||
placeholder={placeholder}
|
||||
maxRows={maxRows}
|
||||
minRows={computedMinRows}
|
||||
value={value}
|
||||
onChange={(event) =>
|
||||
onChange?.(turnIntoEmptyStringIfWhitespacesOnly(event.target.value))
|
||||
}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
readOnly={readOnly}
|
||||
style={isDefined(height) ? { height } : undefined}
|
||||
/>
|
||||
</StyledTextAreaContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+12
-1
@@ -1,9 +1,20 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { Label } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const StyledDropdownMenuSubheader = styled(Label)`
|
||||
const StyledDropdownMenuSubheaderContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
padding: ${themeCssVariables.spacing[1]} ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const StyledDropdownMenuSubheader = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) => (
|
||||
<StyledDropdownMenuSubheaderContainer>
|
||||
<Label>{children}</Label>
|
||||
</StyledDropdownMenuSubheaderContainer>
|
||||
);
|
||||
|
||||
+8
-6
@@ -29,7 +29,7 @@ const StyledMainContainer = styled.div`
|
||||
${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledPageHeader = styled(PageHeader)`
|
||||
const StyledPageHeaderContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
@@ -42,11 +42,13 @@ export const FullScreenContainer = ({
|
||||
|
||||
return (
|
||||
<StyledFullScreen>
|
||||
<StyledPageHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={!isMobile}
|
||||
onClosePage={exitFullScreen}
|
||||
/>
|
||||
<StyledPageHeaderContainer>
|
||||
<PageHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={!isMobile}
|
||||
onClosePage={exitFullScreen}
|
||||
/>
|
||||
</StyledPageHeaderContainer>
|
||||
<StyledMainContainer>{children}</StyledMainContainer>
|
||||
</StyledFullScreen>
|
||||
);
|
||||
|
||||
+8
-6
@@ -22,7 +22,7 @@ const StyledFullScreenOverlay = styled.div`
|
||||
z-index: ${RootStackingContextZIndices.RootModal};
|
||||
`;
|
||||
|
||||
const StyledFullScreenHeader = styled(PageHeader)`
|
||||
const StyledFullScreenHeaderContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
@@ -59,11 +59,13 @@ export const FullScreenModal = forwardRef<HTMLDivElement, FullScreenModalProps>(
|
||||
data-globally-prevent-click-outside="true"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<StyledFullScreenHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={hasClosePageButton}
|
||||
onClosePage={onClose}
|
||||
/>
|
||||
<StyledFullScreenHeaderContainer>
|
||||
<PageHeader
|
||||
title={<Breadcrumb links={links} />}
|
||||
hasClosePageButton={hasClosePageButton}
|
||||
onClosePage={onClose}
|
||||
/>
|
||||
</StyledFullScreenHeaderContainer>
|
||||
<StyledFullScreenContent>{children}</StyledFullScreenContent>
|
||||
</StyledFullScreenOverlay>
|
||||
);
|
||||
|
||||
+40
-16
@@ -33,30 +33,52 @@ export type ConfirmationModalProps = {
|
||||
overlay?: ModalOverlay;
|
||||
};
|
||||
|
||||
export const StyledCenteredButton = styled(Button)`
|
||||
const StyledCenteredButtonContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const StyledCenteredButton = (
|
||||
props: React.ComponentProps<typeof Button>,
|
||||
) => (
|
||||
<StyledCenteredButtonContainer>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<Button {...props} />
|
||||
</StyledCenteredButtonContainer>
|
||||
);
|
||||
|
||||
const StyledCenteredTitle = styled.div`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const StyledSection = styled(Section)`
|
||||
const StyledSectionContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
export const StyledConfirmationButton = styled(StyledCenteredButton)`
|
||||
border-color: ${themeCssVariables.border.color.danger};
|
||||
box-shadow: none;
|
||||
color: ${themeCssVariables.color.red};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
line-height: ${themeCssVariables.text.lineHeight.lg};
|
||||
:hover {
|
||||
background-color: ${themeCssVariables.color.red3};
|
||||
const StyledConfirmationButtonContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
> button {
|
||||
border-color: ${themeCssVariables.border.color.danger};
|
||||
box-shadow: none;
|
||||
color: ${themeCssVariables.color.red};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
line-height: ${themeCssVariables.text.lineHeight.lg};
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.color.red3};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledConfirmationButton = (
|
||||
props: React.ComponentProps<typeof Button>,
|
||||
) => (
|
||||
<StyledConfirmationButtonContainer>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<Button {...props} />
|
||||
</StyledConfirmationButtonContainer>
|
||||
);
|
||||
|
||||
const defaultConfirmButtonText = msg`Confirm`;
|
||||
|
||||
export const ConfirmationModal = ({
|
||||
@@ -129,12 +151,14 @@ export const ConfirmationModal = ({
|
||||
<StyledCenteredTitle>
|
||||
<H1Title title={title} fontColor={H1TitleFontColor.Primary} />
|
||||
</StyledCenteredTitle>
|
||||
<StyledSection
|
||||
alignment={SectionAlignment.Center}
|
||||
fontColor={SectionFontColor.Primary}
|
||||
>
|
||||
{subtitle}
|
||||
</StyledSection>
|
||||
<StyledSectionContainer>
|
||||
<Section
|
||||
alignment={SectionAlignment.Center}
|
||||
fontColor={SectionFontColor.Primary}
|
||||
>
|
||||
{subtitle}
|
||||
</Section>
|
||||
</StyledSectionContainer>
|
||||
{confirmationValue && (
|
||||
<Section>
|
||||
<SettingsTextInput
|
||||
|
||||
@@ -19,9 +19,10 @@ const StyledInnerContainer = styled.div`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledScrollWrapper = styled(ScrollWrapper)`
|
||||
const StyledScrollWrapperContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export type ShowPageContainerProps = {
|
||||
@@ -40,11 +41,13 @@ export const ShowPageContainer = ({ children }: ShowPageContainerProps) => {
|
||||
|
||||
return isMobile ? (
|
||||
<StyledOuterContainer style={mobileStyle}>
|
||||
<StyledScrollWrapper componentInstanceId="scroll-wrapper-show-page-container">
|
||||
<StyledInnerContainer style={mobileStyle}>
|
||||
{children}
|
||||
</StyledInnerContainer>
|
||||
</StyledScrollWrapper>
|
||||
<StyledScrollWrapperContainer>
|
||||
<ScrollWrapper componentInstanceId="scroll-wrapper-show-page-container">
|
||||
<StyledInnerContainer style={mobileStyle}>
|
||||
{children}
|
||||
</StyledInnerContainer>
|
||||
</ScrollWrapper>
|
||||
</StyledScrollWrapperContainer>
|
||||
</StyledOuterContainer>
|
||||
) : (
|
||||
<StyledOuterContainer>
|
||||
|
||||
@@ -4,8 +4,10 @@ import { IconChevronDown } from 'twenty-ui/display';
|
||||
import { TabButton } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTabMoreButton = styled(TabButton)`
|
||||
height: ${themeCssVariables.spacing[10]};
|
||||
const StyledTabMoreButtonContainer = styled.div`
|
||||
> * {
|
||||
height: ${themeCssVariables.spacing[10]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const TabMoreButton = ({
|
||||
@@ -18,12 +20,14 @@ export const TabMoreButton = ({
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<StyledTabMoreButton
|
||||
id="tab-more-button"
|
||||
active={active}
|
||||
title={`+${hiddenTabsCount} ${t`More`}`}
|
||||
RightIcon={IconChevronDown}
|
||||
className={className}
|
||||
/>
|
||||
<StyledTabMoreButtonContainer>
|
||||
<TabButton
|
||||
id="tab-more-button"
|
||||
active={active}
|
||||
title={`+${hiddenTabsCount} ${t`More`}`}
|
||||
RightIcon={IconChevronDown}
|
||||
className={className}
|
||||
/>
|
||||
</StyledTabMoreButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ const StyledSection = styled.div<{ isExpanded: boolean }>`
|
||||
opacity calc(${themeCssVariables.animation.duration.normal} * 1s);
|
||||
`;
|
||||
|
||||
const StyledSectionContent = styled(TableBody)`
|
||||
const StyledSectionContentContainer = styled.div`
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
`;
|
||||
|
||||
@@ -66,7 +66,9 @@ export const TableSection = ({
|
||||
)}
|
||||
</StyledSectionHeader>
|
||||
<StyledSection isExpanded={isExpanded}>
|
||||
<StyledSectionContent>{children}</StyledSectionContent>
|
||||
<StyledSectionContentContainer>
|
||||
<TableBody>{children}</TableBody>
|
||||
</StyledSectionContentContainer>
|
||||
</StyledSection>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
|
||||
const StyledTableSubRow = styled(TableRow)`
|
||||
const StyledTableSubRowContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
export const TableSubRow = StyledTableSubRow;
|
||||
export const TableSubRow = ({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TableRow> & { children?: ReactNode }) => (
|
||||
<StyledTableSubRowContainer>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<TableRow {...props}>{children}</TableRow>
|
||||
</StyledTableSubRowContainer>
|
||||
);
|
||||
|
||||
+13
-9
@@ -22,12 +22,14 @@ const StyledWrapper = styled.nav`
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
const StyledLinkContainer = styled.div`
|
||||
> a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledText = styled.span`
|
||||
@@ -56,9 +58,11 @@ export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{link.href ? (
|
||||
<StyledLink title={text} to={link.href}>
|
||||
{link.children}
|
||||
</StyledLink>
|
||||
<StyledLinkContainer>
|
||||
<Link title={text} to={link.href}>
|
||||
{link.children}
|
||||
</Link>
|
||||
</StyledLinkContainer>
|
||||
) : (
|
||||
<StyledText title={text}>{link.children}</StyledText>
|
||||
)}
|
||||
|
||||
+13
-9
@@ -24,12 +24,14 @@ const StyledWrapper = styled.nav`
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
const StyledLinkContainer = styled.div`
|
||||
> a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledText = styled.span`
|
||||
@@ -71,9 +73,11 @@ export const MobileBreadcrumb = ({
|
||||
) : previousLink?.href ? (
|
||||
<>
|
||||
<IconChevronLeft size={theme.icon.size.md} />
|
||||
<StyledLink title={text} to={previousLink.href}>
|
||||
{t`Back to ${linkText}`}
|
||||
</StyledLink>
|
||||
<StyledLinkContainer>
|
||||
<Link title={text} to={previousLink.href}>
|
||||
{t`Back to ${linkText}`}
|
||||
</Link>
|
||||
</StyledLinkContainer>
|
||||
</>
|
||||
) : (
|
||||
<StyledText title={text}>{previousLink?.children}</StyledText>
|
||||
|
||||
+11
-3
@@ -30,9 +30,7 @@ export const StyledLabel = styled.div`
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
export const StyledIconChevronDown = styled(IconChevronDown)<{
|
||||
disabled?: boolean;
|
||||
}>`
|
||||
const StyledIconChevronDownContainer = styled.div<{ disabled?: boolean }>`
|
||||
align-items: center;
|
||||
color: ${({ disabled }) =>
|
||||
disabled
|
||||
@@ -40,3 +38,13 @@ export const StyledIconChevronDown = styled(IconChevronDown)<{
|
||||
: themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const StyledIconChevronDown = ({
|
||||
disabled,
|
||||
...props
|
||||
}: { disabled?: boolean } & React.ComponentProps<typeof IconChevronDown>) => (
|
||||
<StyledIconChevronDownContainer disabled={disabled}>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<IconChevronDown {...props} />
|
||||
</StyledIconChevronDownContainer>
|
||||
);
|
||||
|
||||
+9
-7
@@ -33,12 +33,12 @@ const StyledRightActions = styled.div<{ isExpanded: boolean }>`
|
||||
transition: gap calc(${themeCssVariables.animation.duration.normal} * 1s) ease;
|
||||
`;
|
||||
|
||||
const StyledNavigationDrawerCollapseButton = styled(
|
||||
NavigationDrawerCollapseButton,
|
||||
)`
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
const StyledNavigationDrawerCollapseButtonContainer = styled.div`
|
||||
> * {
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledWorkspaceDropdownContainer = styled.div`
|
||||
@@ -76,7 +76,9 @@ export const NavigationDrawerHeader = ({
|
||||
aria-label={t`Search`}
|
||||
/>
|
||||
{isNavigationDrawerExpanded && showCollapseButton && (
|
||||
<StyledNavigationDrawerCollapseButton direction="left" />
|
||||
<StyledNavigationDrawerCollapseButtonContainer>
|
||||
<NavigationDrawerCollapseButton direction="left" />
|
||||
</StyledNavigationDrawerCollapseButtonContainer>
|
||||
)}
|
||||
</StyledRightActions>
|
||||
)}
|
||||
|
||||
+27
-21
@@ -60,11 +60,12 @@ const StyledLargeGrid = styled.div`
|
||||
height: 600px;
|
||||
`;
|
||||
|
||||
const StyledScrollableWrapper = styled(ScrollWrapper)`
|
||||
const StyledScrollableWrapperContainer = styled.div`
|
||||
border: 1px solid ${themeCssVariables.border.color.light};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
height: 300px;
|
||||
width: 600px;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
type SelectableItemProps = {
|
||||
@@ -146,27 +147,32 @@ const ScrollableDragSelectDemo = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledScrollableWrapper componentInstanceId="scrollable-demo">
|
||||
<div ref={containerRef} style={{ position: 'relative', padding: '16px' }}>
|
||||
<StyledLargeGrid>
|
||||
{Array.from({ length: 36 }, (_, index) => (
|
||||
<SelectableItem
|
||||
key={index}
|
||||
id={`scroll-item-${index}`}
|
||||
selected={selectedItems.has(`scroll-item-${index}`)}
|
||||
>
|
||||
Item {index + 1}
|
||||
</SelectableItem>
|
||||
))}
|
||||
</StyledLargeGrid>
|
||||
<StyledScrollableWrapperContainer>
|
||||
<ScrollWrapper componentInstanceId="scrollable-demo">
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{ position: 'relative', padding: '16px' }}
|
||||
>
|
||||
<StyledLargeGrid>
|
||||
{Array.from({ length: 36 }, (_, index) => (
|
||||
<SelectableItem
|
||||
key={index}
|
||||
id={`scroll-item-${index}`}
|
||||
selected={selectedItems.has(`scroll-item-${index}`)}
|
||||
>
|
||||
Item {index + 1}
|
||||
</SelectableItem>
|
||||
))}
|
||||
</StyledLargeGrid>
|
||||
|
||||
<DragSelect
|
||||
selectableItemsContainerRef={containerRef}
|
||||
onDragSelectionChange={handleSelectionChange}
|
||||
scrollWrapperComponentInstanceId="scrollable-demo"
|
||||
/>
|
||||
</div>
|
||||
</StyledScrollableWrapper>
|
||||
<DragSelect
|
||||
selectableItemsContainerRef={containerRef}
|
||||
onDragSelectionChange={handleSelectionChange}
|
||||
scrollWrapperComponentInstanceId="scrollable-demo"
|
||||
/>
|
||||
</div>
|
||||
</ScrollWrapper>
|
||||
</StyledScrollableWrapperContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user