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:
Charles Bochet
2026-03-05 18:16:25 +01:00
committed by GitHub
parent e5e3132ddd
commit c53a13417e
224 changed files with 4765 additions and 3837 deletions
@@ -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>
@@ -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>
);
};
@@ -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>
);
@@ -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>
);
@@ -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>
);
@@ -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>
);
@@ -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>
)}
@@ -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>
@@ -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>
);
@@ -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>
)}
@@ -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>
);
};