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:
+18
-10
@@ -19,12 +19,15 @@ const StyledMainContainerLayoutForDesktop = styled.div`
|
||||
padding-right: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledPageBodyForDesktop = styled(PageBody)`
|
||||
const StyledPageBodyForDesktopContainer = styled.div`
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
width: 0;
|
||||
padding-bottom: 0;
|
||||
padding-right: 0;
|
||||
|
||||
> * {
|
||||
padding-bottom: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledMainContainerLayoutForMobile = styled.div`
|
||||
@@ -34,11 +37,12 @@ const StyledMainContainerLayoutForMobile = styled.div`
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const StyledPageBodyForMobile = styled(PageBody)`
|
||||
padding-bottom: 0;
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
|
||||
padding-right: ${themeCssVariables.spacing['1.5']};
|
||||
const StyledPageBodyForMobileContainer = styled.div`
|
||||
> * {
|
||||
padding-bottom: 0;
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
padding-right: ${themeCssVariables.spacing['1.5']};
|
||||
}
|
||||
`;
|
||||
|
||||
export const MainContainerLayoutWithSidePanel = ({
|
||||
@@ -51,7 +55,9 @@ export const MainContainerLayoutWithSidePanel = ({
|
||||
if (isMobile) {
|
||||
return (
|
||||
<StyledMainContainerLayoutForMobile>
|
||||
<StyledPageBodyForMobile>{children}</StyledPageBodyForMobile>
|
||||
<StyledPageBodyForMobileContainer>
|
||||
<PageBody>{children}</PageBody>
|
||||
</StyledPageBodyForMobileContainer>
|
||||
<CommandMenuForMobile />
|
||||
</StyledMainContainerLayoutForMobile>
|
||||
);
|
||||
@@ -59,7 +65,9 @@ export const MainContainerLayoutWithSidePanel = ({
|
||||
|
||||
return (
|
||||
<StyledMainContainerLayoutForDesktop>
|
||||
<StyledPageBodyForDesktop>{children}</StyledPageBodyForDesktop>
|
||||
<StyledPageBodyForDesktopContainer>
|
||||
<PageBody>{children}</PageBody>
|
||||
</StyledPageBodyForDesktopContainer>
|
||||
<SidePanelForDesktop />
|
||||
</StyledMainContainerLayoutForDesktop>
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`generateDepthRecordGqlFieldsFromRecord should generate depth one record gql fields from empty record 1`] = `
|
||||
{
|
||||
|
||||
+6
-2
@@ -3,7 +3,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { styled } from '@linaria/react';
|
||||
import { NotificationCounter } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledNotificationCounter = styled(NotificationCounter)`
|
||||
const StyledNotificationCounterContainer = styled.div`
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: -7px;
|
||||
@@ -23,5 +23,9 @@ export const RecordBoardCardMultiDragCounterChip = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <StyledNotificationCounter count={selectedCount} />;
|
||||
return (
|
||||
<StyledNotificationCounterContainer>
|
||||
<NotificationCounter count={selectedCount} />
|
||||
</StyledNotificationCounterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+24
-19
@@ -68,7 +68,7 @@ const StyledColumn = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledTag = styled(Tag)`
|
||||
const StyledTagContainer = styled.div`
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
@@ -139,24 +139,29 @@ export const RecordBoardColumnHeader = () => {
|
||||
y: 10,
|
||||
}}
|
||||
clickableComponent={
|
||||
<StyledTag
|
||||
variant={
|
||||
columnDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
columnDefinition.type === RecordGroupDefinitionType.Value
|
||||
? columnDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={columnDefinition.title}
|
||||
weight={
|
||||
columnDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'regular'
|
||||
: 'medium'
|
||||
}
|
||||
/>
|
||||
<StyledTagContainer>
|
||||
<Tag
|
||||
variant={
|
||||
columnDefinition.type ===
|
||||
RecordGroupDefinitionType.Value
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
columnDefinition.type ===
|
||||
RecordGroupDefinitionType.Value
|
||||
? columnDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={columnDefinition.title}
|
||||
weight={
|
||||
columnDefinition.type ===
|
||||
RecordGroupDefinitionType.Value
|
||||
? 'regular'
|
||||
: 'medium'
|
||||
}
|
||||
/>
|
||||
</StyledTagContainer>
|
||||
}
|
||||
dropdownComponents={<RecordBoardColumnDropdownMenu />}
|
||||
/>
|
||||
|
||||
+28
-22
@@ -6,12 +6,14 @@ import { type Nullable } from 'twenty-shared/types';
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
import { AppTooltip, TooltipDelay } from 'twenty-ui/display';
|
||||
|
||||
const StyledTag = styled(Tag)`
|
||||
const StyledTagContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledHeader = styled(StyledHeaderDropdownButton)`
|
||||
padding: 0;
|
||||
const StyledHeaderContainer = styled.div`
|
||||
> * {
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RecordBoardColumnHeaderAggregateDropdownButton = ({
|
||||
@@ -29,24 +31,28 @@ export const RecordBoardColumnHeaderAggregateDropdownButton = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledHeader id={dropdownId} isUnfolded={isDropdownOpen}>
|
||||
<>
|
||||
<StyledTag
|
||||
text={value ? value.toString() : '-'}
|
||||
color="transparent"
|
||||
weight="regular"
|
||||
/>
|
||||
{!isDropdownOpen && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${dropdownId}`}
|
||||
content={tooltip ?? ''}
|
||||
noArrow
|
||||
place="right"
|
||||
positionStrategy="fixed"
|
||||
delay={TooltipDelay.mediumDelay}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</StyledHeader>
|
||||
<StyledHeaderContainer>
|
||||
<StyledHeaderDropdownButton id={dropdownId} isUnfolded={isDropdownOpen}>
|
||||
<>
|
||||
<StyledTagContainer>
|
||||
<Tag
|
||||
text={value ? value.toString() : '-'}
|
||||
color="transparent"
|
||||
weight="regular"
|
||||
/>
|
||||
</StyledTagContainer>
|
||||
{!isDropdownOpen && (
|
||||
<AppTooltip
|
||||
anchorSelect={`#${dropdownId}`}
|
||||
content={tooltip ?? ''}
|
||||
noArrow
|
||||
place="right"
|
||||
positionStrategy="fixed"
|
||||
delay={TooltipDelay.mediumDelay}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</StyledHeaderDropdownButton>
|
||||
</StyledHeaderContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+15
-13
@@ -14,7 +14,7 @@ import { IconPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
const StyledButtonContainer = styled.div`
|
||||
padding: ${themeCssVariables.spacing['0.5']};
|
||||
min-width: unset;
|
||||
height: auto;
|
||||
@@ -69,17 +69,19 @@ export const RecordCalendarAddNew = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledButton
|
||||
onClick={async () => {
|
||||
await createNewIndexRecord({
|
||||
[calendarFieldMetadataItem.name]: cardDate
|
||||
.toZonedDateTime(userTimezone)
|
||||
.toInstant()
|
||||
.toString(),
|
||||
});
|
||||
}}
|
||||
variant="tertiary"
|
||||
Icon={() => <IconPlus size={theme.icon.size.sm} />}
|
||||
/>
|
||||
<StyledButtonContainer>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await createNewIndexRecord({
|
||||
[calendarFieldMetadataItem.name]: cardDate
|
||||
.toZonedDateTime(userTimezone)
|
||||
.toInstant()
|
||||
.toString(),
|
||||
});
|
||||
}}
|
||||
variant="tertiary"
|
||||
Icon={() => <IconPlus size={theme.icon.size.sm} />}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+17
-13
@@ -30,7 +30,7 @@ const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledNavigationButton = styled(Button)`
|
||||
const StyledNavigationButtonContainer = styled.div`
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
@@ -120,24 +120,28 @@ export const RecordCalendarTopBar = () => {
|
||||
</StyledLeftSection>
|
||||
|
||||
<StyledNavigationSection>
|
||||
<StyledNavigationButton
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronLeft}
|
||||
onClick={handlePreviousMonth}
|
||||
/>
|
||||
<StyledNavigationButtonContainer>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronLeft}
|
||||
onClick={handlePreviousMonth}
|
||||
/>
|
||||
</StyledNavigationButtonContainer>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
title={t`Today`}
|
||||
onClick={handleTodayClick}
|
||||
/>
|
||||
<StyledNavigationButton
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronRight}
|
||||
onClick={handleNextMonth}
|
||||
/>
|
||||
<StyledNavigationButtonContainer>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronRight}
|
||||
onClick={handleNextMonth}
|
||||
/>
|
||||
</StyledNavigationButtonContainer>
|
||||
</StyledNavigationSection>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
+15
-13
@@ -24,7 +24,7 @@ const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledRecordCard = styled(RecordCard)`
|
||||
const StyledRecordCardContainer = styled.div`
|
||||
width: calc(100% - 2px);
|
||||
`;
|
||||
|
||||
@@ -84,18 +84,20 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
|
||||
value={{ scopeInstanceId: RECORD_CALENDAR_CARD_INPUT_ID_PREFIX }}
|
||||
>
|
||||
<StyledContainer onContextMenu={handleContextMenuOpen}>
|
||||
<StyledRecordCard
|
||||
data-selected={isRecordCalendarCardSelected}
|
||||
data-click-outside-id={RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<RecordCalendarCardHeader recordId={recordId} />
|
||||
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
|
||||
<RecordCalendarCardBody
|
||||
recordId={recordId}
|
||||
isRecordReadOnly={false}
|
||||
/>
|
||||
</AnimatedEaseInOut>
|
||||
</StyledRecordCard>
|
||||
<StyledRecordCardContainer>
|
||||
<RecordCard
|
||||
data-selected={isRecordCalendarCardSelected}
|
||||
data-click-outside-id={RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<RecordCalendarCardHeader recordId={recordId} />
|
||||
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
|
||||
<RecordCalendarCardBody
|
||||
recordId={recordId}
|
||||
isRecordReadOnly={false}
|
||||
/>
|
||||
</AnimatedEaseInOut>
|
||||
</RecordCard>
|
||||
</StyledRecordCardContainer>
|
||||
<RecordCalendarCardCellHoveredPortal recordId={recordId} />
|
||||
<RecordCalendarCardCellEditModePortal recordId={recordId} />
|
||||
</StyledContainer>
|
||||
|
||||
+32
-24
@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRecordDetailSectionContainer = styled(Section)`
|
||||
const StyledRecordDetailSectionWrapper = styled.div`
|
||||
border-top: 1px solid ${themeCssVariables.border.color.light};
|
||||
padding-top: ${themeCssVariables.spacing[3]};
|
||||
padding-bottom: ${themeCssVariables.spacing[3]};
|
||||
@@ -36,13 +36,15 @@ const StyledTitleLabel = styled.div`
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
text-decoration: none;
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
const StyledLinkContainer = styled.span`
|
||||
& > a {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
text-decoration: none;
|
||||
|
||||
:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
&:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -67,22 +69,28 @@ export const RecordDetailSectionContainer = ({
|
||||
}: RecordDetailSectionContainerProps) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
return (
|
||||
<StyledRecordDetailSectionContainer>
|
||||
<StyledHeader
|
||||
areRecordsAvailable={areRecordsAvailable}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
<StyledTitle>
|
||||
<StyledTitleLabel>{title}</StyledTitleLabel>
|
||||
{link && <StyledLink to={link.to}>{link.label}</StyledLink>}
|
||||
</StyledTitle>
|
||||
{hideRightAdornmentOnMouseLeave && !isHovered && areRecordsAvailable
|
||||
? null
|
||||
: rightAdornment}
|
||||
</StyledHeader>
|
||||
{children}
|
||||
</StyledRecordDetailSectionContainer>
|
||||
<StyledRecordDetailSectionWrapper>
|
||||
<Section>
|
||||
<StyledHeader
|
||||
areRecordsAvailable={areRecordsAvailable}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
<StyledTitle>
|
||||
<StyledTitleLabel>{title}</StyledTitleLabel>
|
||||
{link && (
|
||||
<StyledLinkContainer>
|
||||
<Link to={link.to}>{link.label}</Link>
|
||||
</StyledLinkContainer>
|
||||
)}
|
||||
</StyledTitle>
|
||||
{hideRightAdornmentOnMouseLeave && !isHovered && areRecordsAvailable
|
||||
? null
|
||||
: rightAdornment}
|
||||
</StyledHeader>
|
||||
{children}
|
||||
</Section>
|
||||
</StyledRecordDetailSectionWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
+51
-45
@@ -22,7 +22,7 @@ import { IconMaximize } from 'twenty-ui/display';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledAdvancedTextFieldContainer = styled(FormFieldInputContainer)`
|
||||
const StyledAdvancedTextFieldContainerWrapper = styled.div`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
@@ -63,14 +63,16 @@ const StyledFullScreenEditorContainer = styled.div`
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
const StyledFullScreenButtonContainer = styled(StyledDropdownButtonContainer)`
|
||||
background-color: transparent;
|
||||
const StyledFullScreenButtonContainerWrapper = styled.div`
|
||||
> * {
|
||||
background-color: transparent;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -209,46 +211,50 @@ export const FormAdvancedTextFieldInput = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledAdvancedTextFieldContainer>
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
<StyledAdvancedTextFieldContainerWrapper>
|
||||
<FormFieldInputContainer>
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
|
||||
<StyledAdvancedTextFieldFieldContainer>
|
||||
<StyledAdvancedTextFieldInnerContainer>
|
||||
{!isFullScreen && (
|
||||
<AdvancedTextEditor
|
||||
editor={editor}
|
||||
readonly={readonly}
|
||||
minHeight={minHeight}
|
||||
maxWidth={maxWidth}
|
||||
/>
|
||||
)}
|
||||
<StyledAdvancedTextFieldFieldContainer>
|
||||
<StyledAdvancedTextFieldInnerContainer>
|
||||
{!isFullScreen && (
|
||||
<AdvancedTextEditor
|
||||
editor={editor}
|
||||
readonly={readonly}
|
||||
minHeight={minHeight}
|
||||
maxWidth={maxWidth}
|
||||
/>
|
||||
)}
|
||||
|
||||
{enableFullScreen && (
|
||||
<StyledEditorActionButtonContainer>
|
||||
{!readonly && !isFullScreen && (
|
||||
<StyledFullScreenButtonContainer
|
||||
isUnfolded={false}
|
||||
transparentBackground
|
||||
onClick={handleEnterFullScreen}
|
||||
>
|
||||
<IconMaximize size={theme.icon.size.md} />
|
||||
</StyledFullScreenButtonContainer>
|
||||
)}
|
||||
</StyledEditorActionButtonContainer>
|
||||
)}
|
||||
{enableFullScreen && (
|
||||
<StyledEditorActionButtonContainer>
|
||||
{!readonly && !isFullScreen && (
|
||||
<StyledFullScreenButtonContainerWrapper>
|
||||
<StyledDropdownButtonContainer
|
||||
isUnfolded={false}
|
||||
transparentBackground
|
||||
onClick={handleEnterFullScreen}
|
||||
>
|
||||
<IconMaximize size={theme.icon.size.md} />
|
||||
</StyledDropdownButtonContainer>
|
||||
</StyledFullScreenButtonContainerWrapper>
|
||||
)}
|
||||
</StyledEditorActionButtonContainer>
|
||||
)}
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
instanceId={instanceId}
|
||||
multiline={true}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
</StyledAdvancedTextFieldInnerContainer>
|
||||
</StyledAdvancedTextFieldFieldContainer>
|
||||
{hint && <InputHint>{hint}</InputHint>}
|
||||
{error && <InputErrorHelper>{error}</InputErrorHelper>}
|
||||
</StyledAdvancedTextFieldContainer>
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
instanceId={instanceId}
|
||||
multiline={true}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
</StyledAdvancedTextFieldInnerContainer>
|
||||
</StyledAdvancedTextFieldFieldContainer>
|
||||
{hint && <InputHint>{hint}</InputHint>}
|
||||
{error && <InputErrorHelper>{error}</InputErrorHelper>}
|
||||
</FormFieldInputContainer>
|
||||
</StyledAdvancedTextFieldContainerWrapper>
|
||||
|
||||
{fullScreenOverlay}
|
||||
</>
|
||||
|
||||
+24
-13
@@ -52,7 +52,14 @@ const StyledDisplayModeReadonlyContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDisplayModeContainer = styled(StyledDisplayModeReadonlyContainer)`
|
||||
const StyledDisplayModeContainer = styled.div`
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-family: inherit;
|
||||
padding-inline: ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
@@ -63,11 +70,11 @@ const StyledDisplayModeContainer = styled(StyledDisplayModeReadonlyContainer)`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledInput = styled(TextInput)`
|
||||
const StyledInputContainer = styled.div`
|
||||
padding: ${themeCssVariables.spacing[1]} ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledPlaceholder = styled(FormFieldPlaceholder)`
|
||||
const StyledPlaceholderContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -303,19 +310,23 @@ export const FormArrayFieldInput = ({
|
||||
{draftValue.value.length > 0 ? (
|
||||
<ArrayDisplay value={draftValue.value} />
|
||||
) : (
|
||||
<StyledPlaceholder />
|
||||
<StyledPlaceholderContainer>
|
||||
<FormFieldPlaceholder />
|
||||
</StyledPlaceholderContainer>
|
||||
)}
|
||||
</StyledDisplayModeReadonlyContainer>
|
||||
) : draftValue.value.length === 0 ? (
|
||||
<StyledInput
|
||||
instanceId={formFieldInputInstanceId}
|
||||
placeholder={t`Enter an item`}
|
||||
value={newItemDraftValue}
|
||||
copyButton={false}
|
||||
onChange={handleFirstItemInputChange}
|
||||
onEnter={handleFirstItemInputEnter}
|
||||
shouldTrim={false}
|
||||
/>
|
||||
<StyledInputContainer>
|
||||
<TextInput
|
||||
instanceId={formFieldInputInstanceId}
|
||||
placeholder={t`Enter an item`}
|
||||
value={newItemDraftValue}
|
||||
copyButton={false}
|
||||
onChange={handleFirstItemInputChange}
|
||||
onEnter={handleFirstItemInputEnter}
|
||||
shouldTrim={false}
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
) : (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
|
||||
+44
-43
@@ -33,7 +33,7 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { type Nullable } from 'twenty-ui/utilities';
|
||||
import { getDateFormatStringForDatePickerInputMask } from '~/utils/date-utils';
|
||||
|
||||
const StyledInputContainer = styled(FormFieldInputInnerContainer)`
|
||||
const StyledInputContainerWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 0;
|
||||
@@ -327,49 +327,50 @@ export const FormDateFieldInput = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<StyledInputContainer
|
||||
formFieldInputInstanceId={instanceId}
|
||||
ref={datePickerWrapperRef}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<>
|
||||
<StyledDateInput
|
||||
type="text"
|
||||
placeholder={placeholderToDisplay}
|
||||
value={inputDate}
|
||||
onFocus={handleInputFocus}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleInputKeydown}
|
||||
disabled={readonly}
|
||||
/>
|
||||
<StyledInputContainerWrapper ref={datePickerWrapperRef}>
|
||||
<FormFieldInputInnerContainer
|
||||
formFieldInputInstanceId={instanceId}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<>
|
||||
<StyledDateInput
|
||||
type="text"
|
||||
placeholder={placeholderToDisplay}
|
||||
value={inputDate}
|
||||
onFocus={handleInputFocus}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleInputKeydown}
|
||||
disabled={readonly}
|
||||
/>
|
||||
|
||||
{draftValue.mode === 'edit' ? (
|
||||
<StyledDateInputContainer>
|
||||
<StyledDateInputAbsoluteContainer>
|
||||
<OverlayContainer>
|
||||
<DatePicker
|
||||
instanceId={instanceId}
|
||||
plainDateString={pickerDate}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
/>
|
||||
</OverlayContainer>
|
||||
</StyledDateInputAbsoluteContainer>
|
||||
</StyledDateInputContainer>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<VariableChipStandalone
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</StyledInputContainer>
|
||||
{draftValue.mode === 'edit' ? (
|
||||
<StyledDateInputContainer>
|
||||
<StyledDateInputAbsoluteContainer>
|
||||
<OverlayContainer>
|
||||
<DatePicker
|
||||
instanceId={instanceId}
|
||||
plainDateString={pickerDate}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
/>
|
||||
</OverlayContainer>
|
||||
</StyledDateInputAbsoluteContainer>
|
||||
</StyledDateInputContainer>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<VariableChipStandalone
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInnerContainer>
|
||||
</StyledInputContainerWrapper>
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
|
||||
+45
-44
@@ -26,7 +26,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { type Nullable } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledInputContainer = styled(FormFieldInputInnerContainer)`
|
||||
const StyledInputContainerWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 0;
|
||||
@@ -243,49 +243,50 @@ export const FormDateTimeFieldInput = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<StyledInputContainer
|
||||
formFieldInputInstanceId={instanceId}
|
||||
ref={datePickerWrapperRef}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<>
|
||||
<StyledDateInputTextContainer>
|
||||
<DateTimePickerInput
|
||||
date={dateValue}
|
||||
onChange={handleInputChange}
|
||||
onFocus={handleInputFocus}
|
||||
readonly={readonly}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</StyledDateInputTextContainer>
|
||||
{draftValue.mode === 'edit' ? (
|
||||
<StyledDateInputContainer>
|
||||
<StyledDateInputAbsoluteContainer>
|
||||
<OverlayContainer>
|
||||
<DateTimePicker
|
||||
instanceId={instanceId}
|
||||
date={dateValue}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</OverlayContainer>
|
||||
</StyledDateInputAbsoluteContainer>
|
||||
</StyledDateInputContainer>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<VariableChipStandalone
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</StyledInputContainer>
|
||||
<StyledInputContainerWrapper ref={datePickerWrapperRef}>
|
||||
<FormFieldInputInnerContainer
|
||||
formFieldInputInstanceId={instanceId}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<>
|
||||
<StyledDateInputTextContainer>
|
||||
<DateTimePickerInput
|
||||
date={dateValue}
|
||||
onChange={handleInputChange}
|
||||
onFocus={handleInputFocus}
|
||||
readonly={readonly}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</StyledDateInputTextContainer>
|
||||
{draftValue.mode === 'edit' ? (
|
||||
<StyledDateInputContainer>
|
||||
<StyledDateInputAbsoluteContainer>
|
||||
<OverlayContainer>
|
||||
<DateTimePicker
|
||||
instanceId={instanceId}
|
||||
date={dateValue}
|
||||
onChange={handlePickerChange}
|
||||
onClose={handlePickerMouseSelect}
|
||||
onEnter={handlePickerEnter}
|
||||
onEscape={handlePickerEscape}
|
||||
onClear={handlePickerClear}
|
||||
hideHeaderInput
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</OverlayContainer>
|
||||
</StyledDateInputAbsoluteContainer>
|
||||
</StyledDateInputContainer>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<VariableChipStandalone
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInnerContainer>
|
||||
</StyledInputContainerWrapper>
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
instanceId={instanceId}
|
||||
|
||||
+17
-4
@@ -50,7 +50,14 @@ const StyledDisplayModeReadonlyContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDisplayModeContainer = styled(StyledDisplayModeReadonlyContainer)`
|
||||
const StyledDisplayModeContainer = styled.div`
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-family: inherit;
|
||||
padding-inline: ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
@@ -65,7 +72,7 @@ const StyledSelectInputContainer = styled.div`
|
||||
top: ${themeCssVariables.spacing[9]};
|
||||
`;
|
||||
|
||||
const StyledPlaceholder = styled(FormFieldPlaceholder)`
|
||||
const StyledPlaceholderContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -223,7 +230,9 @@ export const FormMultiSelectFieldInput = ({
|
||||
options={selectedOptions}
|
||||
/>
|
||||
) : (
|
||||
<StyledPlaceholder />
|
||||
<StyledPlaceholderContainer>
|
||||
<FormFieldPlaceholder />
|
||||
</StyledPlaceholderContainer>
|
||||
)}
|
||||
<IconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
@@ -243,7 +252,11 @@ export const FormMultiSelectFieldInput = ({
|
||||
options={selectedOptions}
|
||||
/>
|
||||
) : (
|
||||
<StyledPlaceholder>{placeholderText}</StyledPlaceholder>
|
||||
<StyledPlaceholderContainer>
|
||||
<FormFieldPlaceholder>
|
||||
{placeholderText}
|
||||
</FormFieldPlaceholder>
|
||||
</StyledPlaceholderContainer>
|
||||
)}
|
||||
<IconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
|
||||
+35
-34
@@ -22,9 +22,7 @@ import { CustomError, isDefined, isValidUuid } from 'twenty-shared/utils';
|
||||
import { IconChevronDown, IconForbid } from 'twenty-ui/display';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledFormSelectContainer = styled(FormFieldInputInnerContainer)<{
|
||||
readonly?: boolean;
|
||||
}>`
|
||||
const StyledFormSelectContainerWrapper = styled.div<{ readonly?: boolean }>`
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
justify-content: space-between;
|
||||
@@ -166,19 +164,20 @@ export const FormSingleRecordPicker = ({
|
||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||
<FormFieldInputRowContainer>
|
||||
{disabled ? (
|
||||
<StyledFormSelectContainer
|
||||
formFieldInputInstanceId={componentId}
|
||||
hasRightElement={false}
|
||||
readonly
|
||||
>
|
||||
<FormSingleRecordFieldChip
|
||||
draftValue={draftValue}
|
||||
selectedRecord={selectedRecord}
|
||||
objectNameSingular={objectNameSingulars[0]}
|
||||
onRemove={handleUnlinkVariable}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</StyledFormSelectContainer>
|
||||
<StyledFormSelectContainerWrapper readonly>
|
||||
<FormFieldInputInnerContainer
|
||||
formFieldInputInstanceId={componentId}
|
||||
hasRightElement={false}
|
||||
>
|
||||
<FormSingleRecordFieldChip
|
||||
draftValue={draftValue}
|
||||
selectedRecord={selectedRecord}
|
||||
objectNameSingular={objectNameSingulars[0]}
|
||||
onRemove={handleUnlinkVariable}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</FormFieldInputInnerContainer>
|
||||
</StyledFormSelectContainerWrapper>
|
||||
) : (
|
||||
<Dropdown
|
||||
dropdownId={dropdownId}
|
||||
@@ -190,25 +189,27 @@ export const FormSingleRecordPicker = ({
|
||||
y: parseInt(theme.spacing[1], 10),
|
||||
}}
|
||||
clickableComponent={
|
||||
<StyledFormSelectContainer
|
||||
formFieldInputInstanceId={componentId}
|
||||
hasRightElement={isDefined(VariablePicker) && !disabled}
|
||||
preventFocusStackUpdate={true}
|
||||
>
|
||||
<FormSingleRecordFieldChip
|
||||
draftValue={draftValue}
|
||||
selectedRecord={selectedRecord}
|
||||
objectNameSingular={objectNameSingulars[0]}
|
||||
onRemove={handleUnlinkVariable}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<StyledIconButton>
|
||||
<IconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.light}
|
||||
<StyledFormSelectContainerWrapper>
|
||||
<FormFieldInputInnerContainer
|
||||
formFieldInputInstanceId={componentId}
|
||||
hasRightElement={isDefined(VariablePicker) && !disabled}
|
||||
preventFocusStackUpdate={true}
|
||||
>
|
||||
<FormSingleRecordFieldChip
|
||||
draftValue={draftValue}
|
||||
selectedRecord={selectedRecord}
|
||||
objectNameSingular={objectNameSingulars[0]}
|
||||
onRemove={handleUnlinkVariable}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</StyledIconButton>
|
||||
</StyledFormSelectContainer>
|
||||
<StyledIconButton>
|
||||
<IconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.light}
|
||||
/>
|
||||
</StyledIconButton>
|
||||
</FormFieldInputInnerContainer>
|
||||
</StyledFormSelectContainerWrapper>
|
||||
}
|
||||
dropdownComponents={
|
||||
<SingleRecordPicker
|
||||
|
||||
+14
-21
@@ -43,23 +43,15 @@ const StyledCustomPhoneInputContainer = styled.div<{
|
||||
height: ${({ hasItem }) => (hasItem ? '30px' : 'auto')};
|
||||
`;
|
||||
|
||||
const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
|
||||
const StyledCustomPhoneInputWrapper = styled.div`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
|
||||
&::placeholder,
|
||||
&::-webkit-input-placeholder {
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-family: ${themeCssVariables.font.family};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
}
|
||||
height: 100%;
|
||||
width: calc(100% - ${themeCssVariables.spacing[8]});
|
||||
|
||||
.PhoneInputInput {
|
||||
background: none;
|
||||
@@ -83,7 +75,6 @@ const StyledCustomPhoneInput = styled(ReactPhoneNumberInput)`
|
||||
border-radius: ${themeCssVariables.border.radius.xs};
|
||||
height: 12px;
|
||||
}
|
||||
width: calc(100% - ${themeCssVariables.spacing[8]});
|
||||
`;
|
||||
|
||||
export const PhonesFieldInput = () => {
|
||||
@@ -219,16 +210,18 @@ export const PhonesFieldInput = () => {
|
||||
hasItem={!!phones.length}
|
||||
hasError={hasError}
|
||||
>
|
||||
<StyledCustomPhoneInput
|
||||
autoFocus={autoFocus}
|
||||
placeholder={placeholder}
|
||||
value={value as E164Number}
|
||||
onChange={onChange as unknown as (newValue: E164Number) => void}
|
||||
international={true}
|
||||
withCountryCallingCode={true}
|
||||
countrySelectComponent={PhoneCountryPickerDropdownButton}
|
||||
defaultCountry={defaultCountry}
|
||||
/>
|
||||
<StyledCustomPhoneInputWrapper>
|
||||
<ReactPhoneNumberInput
|
||||
autoFocus={autoFocus}
|
||||
placeholder={placeholder}
|
||||
value={value as E164Number}
|
||||
onChange={onChange as unknown as (newValue: E164Number) => void}
|
||||
international={true}
|
||||
withCountryCallingCode={true}
|
||||
countrySelectComponent={PhoneCountryPickerDropdownButton}
|
||||
defaultCountry={defaultCountry}
|
||||
/>
|
||||
</StyledCustomPhoneInputWrapper>
|
||||
</StyledCustomPhoneInputContainer>
|
||||
);
|
||||
}}
|
||||
|
||||
+26
-24
@@ -6,7 +6,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { Tag } from 'twenty-ui/components';
|
||||
|
||||
const StyledMenuItem = styled(MenuItem)`
|
||||
const StyledMenuItemContainer = styled.div`
|
||||
width: calc(100% - 2 * var(--horizontal-padding));
|
||||
`;
|
||||
|
||||
@@ -29,28 +29,30 @@ export const RecordIndexPageKanbanAddMenuItem = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledMenuItem
|
||||
text={
|
||||
<Tag
|
||||
variant={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? recordGroupDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={recordGroupDefinition.title}
|
||||
weight={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'regular'
|
||||
: 'medium'
|
||||
}
|
||||
/>
|
||||
}
|
||||
onClick={() => onItemClick(recordGroupDefinition)}
|
||||
/>
|
||||
<StyledMenuItemContainer>
|
||||
<MenuItem
|
||||
text={
|
||||
<Tag
|
||||
variant={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? recordGroupDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={recordGroupDefinition.title}
|
||||
weight={
|
||||
recordGroupDefinition.type === RecordGroupDefinitionType.Value
|
||||
? 'regular'
|
||||
: 'medium'
|
||||
}
|
||||
/>
|
||||
}
|
||||
onClick={() => onItemClick(recordGroupDefinition)}
|
||||
/>
|
||||
</StyledMenuItemContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+8
-6
@@ -27,7 +27,7 @@ const StyledShowPageRightContainer = styled.div`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const StyledTabList = styled(TabList)`
|
||||
const StyledTabListContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -65,11 +65,13 @@ export const MergeRecordsContainer = ({
|
||||
<TabListComponentInstanceContext.Provider
|
||||
value={{ instanceId: instanceId }}
|
||||
>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={instanceId}
|
||||
/>
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={instanceId}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
</TabListComponentInstanceContext.Provider>
|
||||
<StyledContentContainer>
|
||||
{activeTabId === MergeRecordsTabId.MERGE_PREVIEW && (
|
||||
|
||||
+12
-10
@@ -8,7 +8,7 @@ import { getPositionNumberIcon } from '@/object-record/record-merge/utils/getPos
|
||||
import { getPositionWordLabel } from '@/object-record/record-merge/utils/getPositionWordLabel';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSection = styled(Section)`
|
||||
const StyledSectionContainer = styled.div`
|
||||
margin: ${themeCssVariables.spacing[4]};
|
||||
width: auto;
|
||||
`;
|
||||
@@ -37,14 +37,16 @@ export const MergeSettingsTab = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledSection>
|
||||
<Select
|
||||
dropdownId="merge-settings-priority-select"
|
||||
options={priorityOptions}
|
||||
value={mergeSettings.conflictPriorityIndex}
|
||||
onChange={handleSelectionChange}
|
||||
label={t`Fields conflicts`}
|
||||
/>
|
||||
</StyledSection>
|
||||
<StyledSectionContainer>
|
||||
<Section>
|
||||
<Select
|
||||
dropdownId="merge-settings-priority-select"
|
||||
options={priorityOptions}
|
||||
value={mergeSettings.conflictPriorityIndex}
|
||||
onChange={handleSelectionChange}
|
||||
label={t`Fields conflicts`}
|
||||
/>
|
||||
</Section>
|
||||
</StyledSectionContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+4
-4
@@ -16,10 +16,10 @@ import {
|
||||
type AnimatedPlaceholderType,
|
||||
} from 'twenty-ui/layout';
|
||||
|
||||
const StyledEmptyPlaceholderOuterContainer = styled(
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
)`
|
||||
align-items: flex-start;
|
||||
const StyledEmptyPlaceholderOuterContainer = styled.div`
|
||||
> * {
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
|
||||
type RecordTableEmptyStateDisplayButtonComponentProps = {
|
||||
|
||||
+12
-11
@@ -22,8 +22,7 @@ const StyledContainer = styled.div`
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
// TODO: refactor
|
||||
const StyledRecordTableTd = styled(RecordTableCellStyleWrapper)`
|
||||
const StyledRecordTableTdContainer = styled.div`
|
||||
border-left: 1px solid transparent;
|
||||
`;
|
||||
|
||||
@@ -43,14 +42,16 @@ export const RecordTableCellCheckbox = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRecordTableTd
|
||||
isSelected={isSelected}
|
||||
hasRightBorder={false}
|
||||
widthClassName={RECORD_TABLE_COLUMN_CHECKBOX_WIDTH_CLASS_NAME}
|
||||
>
|
||||
<StyledContainer onClick={handleClick} data-select-disable>
|
||||
<Checkbox hoverable checked={isSelected} />
|
||||
</StyledContainer>
|
||||
</StyledRecordTableTd>
|
||||
<StyledRecordTableTdContainer>
|
||||
<RecordTableCellStyleWrapper
|
||||
isSelected={isSelected}
|
||||
hasRightBorder={false}
|
||||
widthClassName={RECORD_TABLE_COLUMN_CHECKBOX_WIDTH_CLASS_NAME}
|
||||
>
|
||||
<StyledContainer onClick={handleClick} data-select-disable>
|
||||
<Checkbox hoverable checked={isSelected} />
|
||||
</StyledContainer>
|
||||
</RecordTableCellStyleWrapper>
|
||||
</StyledRecordTableTdContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+12
-11
@@ -19,8 +19,7 @@ const StyledContainer = styled.div`
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
// TODO: refactor
|
||||
const StyledRecordTableTd = styled(RecordTableCellStyleWrapper)`
|
||||
const StyledRecordTableTdContainer = styled.div`
|
||||
border-left: 1px solid transparent;
|
||||
`;
|
||||
|
||||
@@ -28,14 +27,16 @@ export const RecordTableCellCheckboxPlaceholder = () => {
|
||||
const { hasUserSelectedAllRows } = useRecordTableBodyContextOrThrow();
|
||||
|
||||
return (
|
||||
<StyledRecordTableTd
|
||||
isSelected={hasUserSelectedAllRows}
|
||||
hasRightBorder={false}
|
||||
widthClassName={RECORD_TABLE_COLUMN_CHECKBOX_WIDTH_CLASS_NAME}
|
||||
>
|
||||
<StyledContainer data-select-disable>
|
||||
<Checkbox hoverable checked={hasUserSelectedAllRows === true} />
|
||||
</StyledContainer>
|
||||
</StyledRecordTableTd>
|
||||
<StyledRecordTableTdContainer>
|
||||
<RecordTableCellStyleWrapper
|
||||
isSelected={hasUserSelectedAllRows}
|
||||
hasRightBorder={false}
|
||||
widthClassName={RECORD_TABLE_COLUMN_CHECKBOX_WIDTH_CLASS_NAME}
|
||||
>
|
||||
<StyledContainer data-select-disable>
|
||||
<Checkbox hoverable checked={hasUserSelectedAllRows === true} />
|
||||
</StyledContainer>
|
||||
</RecordTableCellStyleWrapper>
|
||||
</StyledRecordTableTdContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+24
-2
@@ -1,5 +1,4 @@
|
||||
import { TABLE_Z_INDEX } from '@/object-record/record-table/constants/TableZIndex';
|
||||
import { StyledCell } from '@/object-record/record-table/record-table-cell/components/RecordTableCellStyleWrapper';
|
||||
import { isRecordTableScrolledVerticallyComponentState } from '@/object-record/record-table/states/isRecordTableScrolledVerticallyComponentState';
|
||||
import { recordTableFocusPositionComponentState } from '@/object-record/record-table/states/recordTableFocusPositionComponentState';
|
||||
import { recordTableHoverPositionComponentState } from '@/object-record/record-table/states/recordTableHoverPositionComponentState';
|
||||
@@ -12,7 +11,30 @@ import { cx } from '@linaria/core';
|
||||
import { useContext, type ReactNode } from 'react';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRecordTableTd = styled(StyledCell)<{ zIndex: number }>`
|
||||
const StyledRecordTableTd = styled.div<{
|
||||
backgroundColor: string;
|
||||
borderColor: string;
|
||||
isDragging?: boolean;
|
||||
fontColor: string;
|
||||
hasRightBorder?: boolean;
|
||||
hasBottomBorder?: boolean;
|
||||
zIndex: number;
|
||||
}>`
|
||||
border-bottom: 1px solid
|
||||
${({ borderColor, hasBottomBorder, isDragging }) =>
|
||||
hasBottomBorder && !isDragging ? borderColor : 'transparent'};
|
||||
|
||||
color: ${({ fontColor }) => fontColor};
|
||||
border-right: ${({ borderColor, hasRightBorder }) =>
|
||||
hasRightBorder ? `1px solid ${borderColor}` : 'none'};
|
||||
|
||||
padding: 0;
|
||||
|
||||
text-align: left;
|
||||
|
||||
background: ${({ backgroundColor, isDragging }) =>
|
||||
isDragging ? 'transparent' : backgroundColor};
|
||||
|
||||
z-index: ${({ zIndex }) => zIndex};
|
||||
`;
|
||||
|
||||
|
||||
+1
-3
@@ -20,7 +20,7 @@ const StyledText = styled.span`
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
const StyledScrollableContainer = styled.div`
|
||||
const StyledValueContainer = styled.div`
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
scrollbar-width: none;
|
||||
@@ -29,9 +29,7 @@ const StyledScrollableContainer = styled.div`
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledValueContainer = styled(StyledScrollableContainer)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
|
||||
+4
-2
@@ -38,7 +38,7 @@ const StyledCell = styled.div<{ isUnfolded: boolean; isFirstCell: boolean }>`
|
||||
: '0'};
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(IconChevronDown)`
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 20px;
|
||||
@@ -94,7 +94,9 @@ export const RecordTableColumnAggregateFooterValueCell = ({
|
||||
dropdownId={dropdownId}
|
||||
/>
|
||||
{!hasAggregateOperationForViewField && (
|
||||
<StyledIcon fontWeight="light" size={theme.icon.size.sm} />
|
||||
<StyledIconContainer>
|
||||
<IconChevronDown fontWeight="light" size={theme.icon.size.sm} />
|
||||
</StyledIconContainer>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
||||
+41
-39
@@ -30,7 +30,7 @@ export type RecordTableColumnHeadDropdownMenuProps = {
|
||||
objectMetadataId: string;
|
||||
};
|
||||
|
||||
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
const StyledDropdownMenuItemsContainerWrapper = styled.div`
|
||||
z-index: ${themeCssVariables.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
@@ -136,44 +136,46 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
|
||||
return (
|
||||
<DropdownContent>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text={t`Filter`}
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text={t`Sort`}
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text={t`Move left`}
|
||||
/>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text={t`Move right`}
|
||||
/>
|
||||
)}
|
||||
{canHide && (
|
||||
<MenuItem
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={async () => await handleColumnVisibility()}
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
<StyledDropdownMenuItemsContainerWrapper>
|
||||
<DropdownMenuItemsContainer>
|
||||
{isFilterable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text={t`Filter`}
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text={t`Sort`}
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text={t`Move left`}
|
||||
/>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text={t`Move right`}
|
||||
/>
|
||||
)}
|
||||
{canHide && (
|
||||
<MenuItem
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={async () => await handleColumnVisibility()}
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
</StyledDropdownMenuItemsContainerWrapper>
|
||||
</DropdownContent>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-8
@@ -20,13 +20,6 @@ import {
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { useIsMobile } from 'twenty-ui/utilities';
|
||||
|
||||
const StyledDragDropPlaceholderCell = styled(
|
||||
RecordTableDragAndDropPlaceholderCell,
|
||||
)`
|
||||
left: 0;
|
||||
position: sticky;
|
||||
`;
|
||||
|
||||
const StyledFieldPlaceholderCell = styled.div<{ widthOfFields: number }>`
|
||||
height: ${RECORD_TABLE_ROW_HEIGHT}px;
|
||||
min-width: ${({ widthOfFields }) => widthOfFields}px;
|
||||
@@ -137,7 +130,7 @@ export const RecordTableActionRow = ({
|
||||
|
||||
return (
|
||||
<StyledRecordTableDraggableTr onClick={onClick}>
|
||||
<StyledDragDropPlaceholderCell />
|
||||
<RecordTableDragAndDropPlaceholderCell />
|
||||
<StyledIconContainer>
|
||||
<LeftIcon
|
||||
stroke={theme.icon.stroke.sm}
|
||||
|
||||
+6
-2
@@ -3,7 +3,7 @@ import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/use
|
||||
import { styled } from '@linaria/react';
|
||||
import { NotificationCounter } from 'twenty-ui/navigation';
|
||||
|
||||
const StyledNotificationCounter = styled(NotificationCounter)`
|
||||
const StyledNotificationCounterContainer = styled.div`
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: -7px;
|
||||
@@ -23,5 +23,9 @@ export const RecordTableRowMultiDragCounterChip = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <StyledNotificationCounter count={selectedCount} />;
|
||||
return (
|
||||
<StyledNotificationCounterContainer>
|
||||
<NotificationCounter count={selectedCount} />
|
||||
</StyledNotificationCounterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+27
-23
@@ -64,7 +64,7 @@ const StyledChevronContainer = styled.div`
|
||||
z-index: ${TABLE_Z_INDEX.groupSection.stickyCell};
|
||||
`;
|
||||
|
||||
const StyledAnimatedLightIconButton = styled(AnimatedLightIconButton)`
|
||||
const StyledAnimatedLightIconButtonContainer = styled.div`
|
||||
display: block;
|
||||
margin: auto;
|
||||
|
||||
@@ -88,7 +88,7 @@ const StyledRecordGroupSection = styled.div<{ width: number }>`
|
||||
z-index: ${TABLE_Z_INDEX.groupSection.stickyCell};
|
||||
`;
|
||||
|
||||
const StyledTag = styled(Tag)`
|
||||
const StyledTagContainer = styled.div`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
@@ -195,32 +195,36 @@ export const RecordTableRecordGroupSection = () => {
|
||||
<StyledTrContainer onClick={handleDropdownToggle}>
|
||||
<StyledRecordTableDragAndDropPlaceholderCell />
|
||||
<StyledChevronContainer>
|
||||
<StyledAnimatedLightIconButton
|
||||
Icon={IconChevronDown}
|
||||
size="small"
|
||||
accent="secondary"
|
||||
animate={{ rotate: !isRecordGroupTableSectionToggled ? -90 : 0 }}
|
||||
transition={{ duration: theme.animation.duration.normal }}
|
||||
/>
|
||||
<StyledAnimatedLightIconButtonContainer>
|
||||
<AnimatedLightIconButton
|
||||
Icon={IconChevronDown}
|
||||
size="small"
|
||||
accent="secondary"
|
||||
animate={{ rotate: !isRecordGroupTableSectionToggled ? -90 : 0 }}
|
||||
transition={{ duration: theme.animation.duration.normal }}
|
||||
/>
|
||||
</StyledAnimatedLightIconButtonContainer>
|
||||
</StyledChevronContainer>
|
||||
<StyledRecordGroupSection
|
||||
className="disable-shadow"
|
||||
width={widthOfLabelIdentifierRecordField}
|
||||
>
|
||||
<StyledTag
|
||||
variant={
|
||||
recordGroupDefinition.type !== RecordGroupDefinitionType.NoValue
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
recordGroupDefinition.type !== RecordGroupDefinitionType.NoValue
|
||||
? recordGroupDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={recordGroupDefinition.title}
|
||||
weight="medium"
|
||||
/>
|
||||
<StyledTagContainer>
|
||||
<Tag
|
||||
variant={
|
||||
recordGroupDefinition.type !== RecordGroupDefinitionType.NoValue
|
||||
? 'solid'
|
||||
: 'outline'
|
||||
}
|
||||
color={
|
||||
recordGroupDefinition.type !== RecordGroupDefinitionType.NoValue
|
||||
? recordGroupDefinition.color
|
||||
: 'transparent'
|
||||
}
|
||||
text={recordGroupDefinition.title}
|
||||
weight="medium"
|
||||
/>
|
||||
</StyledTagContainer>
|
||||
<RecordBoardColumnHeaderAggregateDropdown
|
||||
aggregateValue={recordIndexAggregateDisplayValueForGroupValue}
|
||||
dropdownId={`record-group-section-aggregate-dropdown-${currentRecordGroupId}`}
|
||||
|
||||
+37
-33
@@ -10,12 +10,14 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSection = styled(Section)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[6]};
|
||||
padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[3]};
|
||||
width: auto;
|
||||
const StyledSectionContainer = styled.div`
|
||||
> * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[6]};
|
||||
padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[3]};
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export type UpdateMultipleRecordsFormProps = {
|
||||
@@ -50,35 +52,37 @@ export const UpdateMultipleRecordsForm = ({
|
||||
}));
|
||||
|
||||
return (
|
||||
<StyledSection>
|
||||
{fieldsWithDefinitions.map(({ fieldMetadataItem, fieldDefinition }) => {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const isRelation = isFieldRelation(fieldDefinition);
|
||||
const fieldNameOrRelationIdName =
|
||||
isRelation && fieldMetadataItem.type === FieldMetadataType.RELATION
|
||||
? (fieldMetadataItem.settings?.joinColumnName as string)
|
||||
: fieldName;
|
||||
<StyledSectionContainer>
|
||||
<Section>
|
||||
{fieldsWithDefinitions.map(({ fieldMetadataItem, fieldDefinition }) => {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const isRelation = isFieldRelation(fieldDefinition);
|
||||
const fieldNameOrRelationIdName =
|
||||
isRelation && fieldMetadataItem.type === FieldMetadataType.RELATION
|
||||
? (fieldMetadataItem.settings?.joinColumnName as string)
|
||||
: fieldName;
|
||||
|
||||
const value = values[fieldNameOrRelationIdName];
|
||||
const value = values[fieldNameOrRelationIdName];
|
||||
|
||||
const handleValueChange = (newValue: any) => {
|
||||
if (isUpdateRecordValueEmpty(newValue)) {
|
||||
onChange(fieldNameOrRelationIdName, undefined);
|
||||
} else {
|
||||
onChange(fieldNameOrRelationIdName, newValue);
|
||||
}
|
||||
};
|
||||
const handleValueChange = (newValue: any) => {
|
||||
if (isUpdateRecordValueEmpty(newValue)) {
|
||||
onChange(fieldNameOrRelationIdName, undefined);
|
||||
} else {
|
||||
onChange(fieldNameOrRelationIdName, newValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FormFieldInput
|
||||
key={fieldDefinition.metadata.fieldName}
|
||||
readonly={disabled}
|
||||
field={fieldDefinition}
|
||||
defaultValue={value}
|
||||
onChange={handleValueChange}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledSection>
|
||||
return (
|
||||
<FormFieldInput
|
||||
key={fieldDefinition.metadata.fieldName}
|
||||
readonly={disabled}
|
||||
field={fieldDefinition}
|
||||
defaultValue={value}
|
||||
onChange={handleValueChange}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Section>
|
||||
</StyledSectionContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user