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:
+33
-30
@@ -13,12 +13,14 @@ type CalendarDayCardContentProps = {
|
||||
divider?: boolean;
|
||||
};
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDayContainer = styled.div`
|
||||
@@ -44,7 +46,7 @@ const StyledEvents = styled.div`
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledEventRow = styled(CalendarEventRow)`
|
||||
const StyledEventRowContainer = styled.div`
|
||||
flex: 1 0 auto;
|
||||
`;
|
||||
|
||||
@@ -67,28 +69,29 @@ export const CalendarDayCardContent = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledCardContent
|
||||
divider={divider}
|
||||
initial="upcoming"
|
||||
animate="ended"
|
||||
variants={upcomingDayCardContentVariants}
|
||||
transition={{
|
||||
delay: Math.max(0, dayEndsIn),
|
||||
duration: theme.animation.duration.fast,
|
||||
}}
|
||||
>
|
||||
<StyledDayContainer>
|
||||
<StyledWeekDay>{weekDayLabel}</StyledWeekDay>
|
||||
<StyledMonthDay>{monthDayLabel}</StyledMonthDay>
|
||||
</StyledDayContainer>
|
||||
<StyledEvents>
|
||||
{calendarEvents.map((calendarEvent) => (
|
||||
<StyledEventRow
|
||||
key={calendarEvent.id}
|
||||
calendarEvent={calendarEvent}
|
||||
/>
|
||||
))}
|
||||
</StyledEvents>
|
||||
</StyledCardContent>
|
||||
<StyledCardContentContainer>
|
||||
<CardContent
|
||||
divider={divider}
|
||||
initial="upcoming"
|
||||
animate="ended"
|
||||
variants={upcomingDayCardContentVariants}
|
||||
transition={{
|
||||
delay: Math.max(0, dayEndsIn),
|
||||
duration: theme.animation.duration.fast,
|
||||
}}
|
||||
>
|
||||
<StyledDayContainer>
|
||||
<StyledWeekDay>{weekDayLabel}</StyledWeekDay>
|
||||
<StyledMonthDay>{monthDayLabel}</StyledMonthDay>
|
||||
</StyledDayContainer>
|
||||
<StyledEvents>
|
||||
{calendarEvents.map((calendarEvent) => (
|
||||
<StyledEventRowContainer key={calendarEvent.id}>
|
||||
<CalendarEventRow calendarEvent={calendarEvent} />
|
||||
</StyledEventRowContainer>
|
||||
))}
|
||||
</StyledEvents>
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+28
-26
@@ -85,7 +85,7 @@ const StyledFields = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledPropertyBox = styled(PropertyBox)`
|
||||
const StyledPropertyBoxContainer = styled.div`
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -165,35 +165,37 @@ export const CalendarEventDetails = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledPropertyBox key={fieldMetadataItem.id}>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: calendarEvent.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
|
||||
field: fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
showLabel: true,
|
||||
labelWidth: 72,
|
||||
}),
|
||||
useUpdateRecord: useUpdateOneCalendarEventRecordMutation,
|
||||
maxWidth: 300,
|
||||
isRecordFieldReadOnly: isReadOnly,
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
<StyledPropertyBoxContainer key={fieldMetadataItem.id}>
|
||||
<PropertyBox>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: calendarEvent.id,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: INPUT_ID_PREFIX,
|
||||
recordId: calendarEvent.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
|
||||
field: fieldMetadataItem,
|
||||
objectMetadataItem,
|
||||
showLabel: true,
|
||||
labelWidth: 72,
|
||||
}),
|
||||
useUpdateRecord: useUpdateOneCalendarEventRecordMutation,
|
||||
maxWidth: 300,
|
||||
isRecordFieldReadOnly: isReadOnly,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StyledPropertyBox>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: calendarEvent.id,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</PropertyBox>
|
||||
</StyledPropertyBoxContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+16
-9
@@ -5,15 +5,18 @@ import { IconLock } from 'twenty-ui/display';
|
||||
import { Card, CardContent } from 'twenty-ui/layout';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledVisibilityCard = styled(Card)`
|
||||
const StyledVisibilityCardContainer = styled.div`
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
flex: 1;
|
||||
border-color: ${themeCssVariables.border.color.light};
|
||||
transition: color ${themeCssVariables.animation.duration.normal} ease;
|
||||
width: 100%;
|
||||
|
||||
> * {
|
||||
border-color: ${themeCssVariables.border.color.light};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledVisibilityCardContent = styled(CardContent)`
|
||||
const StyledVisibilityCardContentContainer = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
box-sizing: border-box;
|
||||
@@ -29,11 +32,15 @@ export const CalendarEventNotSharedContent = () => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<StyledVisibilityCard>
|
||||
<StyledVisibilityCardContent>
|
||||
<IconLock size={theme.icon.size.sm} />
|
||||
<Trans>Not shared</Trans>
|
||||
</StyledVisibilityCardContent>
|
||||
</StyledVisibilityCard>
|
||||
<StyledVisibilityCardContainer>
|
||||
<Card>
|
||||
<StyledVisibilityCardContentContainer>
|
||||
<CardContent>
|
||||
<IconLock size={theme.icon.size.sm} />
|
||||
<Trans>Not shared</Trans>
|
||||
</CardContent>
|
||||
</StyledVisibilityCardContentContainer>
|
||||
</Card>
|
||||
</StyledVisibilityCardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+16
-14
@@ -21,7 +21,7 @@ const StyledInlineCellBaseContainer = styled.div`
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const StyledPropertyBox = styled(PropertyBox)`
|
||||
const StyledPropertyBoxContainer = styled.div`
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -87,19 +87,21 @@ export const CalendarEventParticipantsResponseStatusField = ({
|
||||
));
|
||||
|
||||
return (
|
||||
<StyledPropertyBox>
|
||||
<StyledInlineCellBaseContainer>
|
||||
<StyledLabelAndIconContainer>
|
||||
<StyledIconContainer>{Icon}</StyledIconContainer>
|
||||
<StyledPropertyBoxContainer>
|
||||
<PropertyBox>
|
||||
<StyledInlineCellBaseContainer>
|
||||
<StyledLabelAndIconContainer>
|
||||
<StyledIconContainer>{Icon}</StyledIconContainer>
|
||||
|
||||
<StyledLabelContainer width={72}>
|
||||
<EllipsisDisplay>{responseStatus}</EllipsisDisplay>
|
||||
</StyledLabelContainer>
|
||||
</StyledLabelAndIconContainer>
|
||||
<StyledDiv ref={participantsContainerRef}>
|
||||
<ExpandableList isChipCountDisplayed>{styledChips}</ExpandableList>
|
||||
</StyledDiv>
|
||||
</StyledInlineCellBaseContainer>
|
||||
</StyledPropertyBox>
|
||||
<StyledLabelContainer width={72}>
|
||||
<EllipsisDisplay>{responseStatus}</EllipsisDisplay>
|
||||
</StyledLabelContainer>
|
||||
</StyledLabelAndIconContainer>
|
||||
<StyledDiv ref={participantsContainerRef}>
|
||||
<ExpandableList isChipCountDisplayed>{styledChips}</ExpandableList>
|
||||
</StyledDiv>
|
||||
</StyledInlineCellBaseContainer>
|
||||
</PropertyBox>
|
||||
</StyledPropertyBoxContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,16 +2,20 @@ import { styled } from '@linaria/react';
|
||||
import { Card } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledList = styled(Card)`
|
||||
& > :not(:last-child) {
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
const StyledListContainer = styled.div`
|
||||
> * {
|
||||
& > :not(:last-child) {
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
}
|
||||
overflow: auto;
|
||||
width: calc(100% - 2px);
|
||||
}
|
||||
|
||||
width: calc(100% - 2px);
|
||||
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const ActivityList = ({ children }: React.PropsWithChildren) => {
|
||||
return <StyledList>{children}</StyledList>;
|
||||
return (
|
||||
<StyledListContainer>
|
||||
<Card>{children}</Card>
|
||||
</StyledListContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,14 +3,14 @@ import React from 'react';
|
||||
import { CardContent } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRowContent = styled(CardContent)`
|
||||
const StyledRowContentContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
height: ${themeCssVariables.spacing[12]};
|
||||
padding: ${themeCssVariables.spacing[0]} ${themeCssVariables.spacing[4]};
|
||||
|
||||
&[data-clickable='false'] {
|
||||
> *[data-clickable='false'] {
|
||||
cursor: default;
|
||||
}
|
||||
`;
|
||||
@@ -30,8 +30,10 @@ export const ActivityRow = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledRowContent onClick={handleClick} isClickable={disabled !== true}>
|
||||
{children}
|
||||
</StyledRowContent>
|
||||
<StyledRowContentContainer>
|
||||
<CardContent onClick={handleClick} isClickable={disabled !== true}>
|
||||
{children}
|
||||
</CardContent>
|
||||
</StyledRowContentContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { RecordChip } from '@/object-record/components/RecordChip';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledAvatar = styled(Avatar)`
|
||||
const StyledAvatarContainer = styled.span`
|
||||
margin-right: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
@@ -67,12 +67,14 @@ export const ParticipantChip = ({
|
||||
/>
|
||||
) : (
|
||||
<StyledChip>
|
||||
<StyledAvatar
|
||||
avatarUrl={avatarUrl}
|
||||
type="rounded"
|
||||
placeholder={displayName}
|
||||
size="sm"
|
||||
/>
|
||||
<StyledAvatarContainer>
|
||||
<Avatar
|
||||
avatarUrl={avatarUrl}
|
||||
type="rounded"
|
||||
placeholder={displayName}
|
||||
size="sm"
|
||||
/>
|
||||
</StyledAvatarContainer>
|
||||
<StyledSenderName variant={variant}>{displayName}</StyledSenderName>
|
||||
</StyledChip>
|
||||
)}
|
||||
|
||||
@@ -38,9 +38,11 @@ const StyledContainer = styled.div`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
const StyledH1TitleWrapper = styled.div`
|
||||
> h2 {
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledEmailCount = styled.span`
|
||||
@@ -107,15 +109,17 @@ export const EmailsCard = () => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<Section>
|
||||
<StyledH1Title
|
||||
title={
|
||||
<>
|
||||
<Trans>Inbox</Trans>{' '}
|
||||
<StyledEmailCount>{totalNumberOfThreads}</StyledEmailCount>
|
||||
</>
|
||||
}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
<StyledH1TitleWrapper>
|
||||
<H1Title
|
||||
title={
|
||||
<>
|
||||
<Trans>Inbox</Trans>{' '}
|
||||
<StyledEmailCount>{totalNumberOfThreads}</StyledEmailCount>
|
||||
</>
|
||||
}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
</StyledH1TitleWrapper>
|
||||
{!firstQueryLoading && (
|
||||
<ActivityList>
|
||||
{timelineThreads?.map((thread: TimelineThread) => (
|
||||
|
||||
@@ -34,8 +34,9 @@ const StyledUploadDragSubTitle = styled.div`
|
||||
line-height: ${themeCssVariables.text.lineHeight.md};
|
||||
`;
|
||||
|
||||
const StyledUploadIcon = styled(IconUpload)`
|
||||
const StyledUploadIconContainer = styled.div`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
margin-bottom: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
@@ -83,10 +84,12 @@ export const DropZone = ({
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...getInputProps()}
|
||||
/>
|
||||
<StyledUploadIcon
|
||||
stroke={theme.icon.stroke.sm}
|
||||
size={theme.icon.size.lg}
|
||||
/>
|
||||
<StyledUploadIconContainer>
|
||||
<IconUpload
|
||||
stroke={theme.icon.stroke.sm}
|
||||
size={theme.icon.size.lg}
|
||||
/>
|
||||
</StyledUploadIconContainer>
|
||||
<StyledUploadDragTitle>{t`Upload files`}</StyledUploadDragTitle>
|
||||
<StyledUploadDragSubTitle>
|
||||
{t`Drag and Drop Here`}
|
||||
|
||||
+16
-12
@@ -38,11 +38,11 @@ const StyledMainContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSidePanelAnimatedPlaceholderEmptyContainer = styled(
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
)`
|
||||
height: auto;
|
||||
padding-top: ${themeCssVariables.spacing[8]};
|
||||
const StyledSidePanelPlaceholderWrapper = styled.div`
|
||||
> * {
|
||||
height: auto;
|
||||
padding-top: ${themeCssVariables.spacing[8]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const TimelineCard = () => {
|
||||
@@ -59,12 +59,8 @@ export const TimelineCard = () => {
|
||||
}
|
||||
|
||||
if (isTimelineActivitiesEmpty) {
|
||||
const EmptyContainer = isInSidePanel
|
||||
? StyledSidePanelAnimatedPlaceholderEmptyContainer
|
||||
: AnimatedPlaceholderEmptyContainer;
|
||||
|
||||
return (
|
||||
<EmptyContainer
|
||||
const placeholderContent = (
|
||||
<AnimatedPlaceholderEmptyContainer
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...EMPTY_PLACEHOLDER_TRANSITION_PROPS}
|
||||
>
|
||||
@@ -77,7 +73,15 @@ export const TimelineCard = () => {
|
||||
{t`There is no activity associated with this record.`}
|
||||
</AnimatedPlaceholderEmptySubTitle>
|
||||
</AnimatedPlaceholderEmptyTextContainer>
|
||||
</EmptyContainer>
|
||||
</AnimatedPlaceholderEmptyContainer>
|
||||
);
|
||||
|
||||
return isInSidePanel ? (
|
||||
<StyledSidePanelPlaceholderWrapper>
|
||||
{placeholderContent}
|
||||
</StyledSidePanelPlaceholderWrapper>
|
||||
) : (
|
||||
placeholderContent
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { Card } from 'twenty-ui/layout';
|
||||
import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type EventCardProps = {
|
||||
@@ -22,7 +21,7 @@ const StyledCardContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
const StyledCardInnerContainer = styled.div`
|
||||
background: ${themeCssVariables.background.secondary};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
@@ -40,7 +39,7 @@ export const EventCard = ({ children, isOpen }: EventCardProps) => {
|
||||
return (
|
||||
isOpen && (
|
||||
<StyledCardContainer>
|
||||
<StyledCard fullWidth>{children}</StyledCard>
|
||||
<StyledCardInnerContainer>{children}</StyledCardInnerContainer>
|
||||
</StyledCardContainer>
|
||||
)
|
||||
);
|
||||
|
||||
+5
-1
@@ -26,8 +26,12 @@ export const StyledEventRowItemColumn = styled.div`
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
export const StyledEventRowItemAction = styled(StyledEventRowItemColumn)`
|
||||
export const StyledEventRowItemAction = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
export const EventRowDynamicComponent = ({
|
||||
|
||||
+14
-13
@@ -13,7 +13,7 @@ type BubbleMenuIconButtonProps = {
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
const StyledBubbleMenuIconButton = styled(FloatingIconButton)`
|
||||
const StyledBubbleMenuIconButtonContainer = styled.div`
|
||||
border: none;
|
||||
border-radius: ${themeCssVariables.spacing[1.5]};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
@@ -29,17 +29,18 @@ export const BubbleMenuIconButton = ({
|
||||
isActive,
|
||||
}: BubbleMenuIconButtonProps) => {
|
||||
return (
|
||||
<StyledBubbleMenuIconButton
|
||||
className={className}
|
||||
Icon={Icon}
|
||||
disabled={disabled}
|
||||
focus={focus}
|
||||
onClick={onClick}
|
||||
isActive={isActive}
|
||||
applyShadow={false}
|
||||
applyBlur={false}
|
||||
size="medium"
|
||||
position="standalone"
|
||||
/>
|
||||
<StyledBubbleMenuIconButtonContainer className={className}>
|
||||
<FloatingIconButton
|
||||
Icon={Icon}
|
||||
disabled={disabled}
|
||||
focus={focus}
|
||||
onClick={onClick}
|
||||
isActive={isActive}
|
||||
applyShadow={false}
|
||||
applyBlur={false}
|
||||
size="medium"
|
||||
position="standalone"
|
||||
/>
|
||||
</StyledBubbleMenuIconButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,8 +27,9 @@ const StyledLoadingIconContainer = styled.div`
|
||||
padding-inline: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledLoadingIcon = styled(IconDotsVertical)`
|
||||
const StyledLoadingIconWrapper = styled.span`
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: flex;
|
||||
transform: rotate(90deg);
|
||||
`;
|
||||
|
||||
@@ -37,7 +38,9 @@ const InitialLoadingIndicator = () => {
|
||||
|
||||
return (
|
||||
<StyledLoadingIconContainer>
|
||||
<StyledLoadingIcon size={theme.icon.size.xl} />
|
||||
<StyledLoadingIconWrapper>
|
||||
<IconDotsVertical size={theme.icon.size.xl} />
|
||||
</StyledLoadingIconWrapper>
|
||||
</StyledLoadingIconContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -115,12 +115,14 @@ const StyledRightButtonsContainer = styled.div`
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledReadOnlyModelButton = styled(LightButton)`
|
||||
cursor: default;
|
||||
const StyledReadOnlyModelButtonContainer = styled.div`
|
||||
> * {
|
||||
cursor: default;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: transparent;
|
||||
&:hover,
|
||||
&:active {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -163,10 +165,9 @@ export const AIChatTab = () => {
|
||||
<AIChatContextUsageButton />
|
||||
</StyledLeftButtonsContainer>
|
||||
<StyledRightButtonsContainer>
|
||||
<StyledReadOnlyModelButton
|
||||
accent="tertiary"
|
||||
title={smartModelLabel}
|
||||
/>
|
||||
<StyledReadOnlyModelButtonContainer>
|
||||
<LightButton accent="tertiary" title={smartModelLabel} />
|
||||
</StyledReadOnlyModelButtonContainer>
|
||||
<SendMessageButton onSend={handleSendAndClear} />
|
||||
</StyledRightButtonsContainer>
|
||||
</StyledButtonsContainer>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { styled } from '@linaria/react';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledScrollWrapper = styled(ScrollWrapper)`
|
||||
const StyledScrollWrapperContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
@@ -30,11 +30,13 @@ export const AIChatTabMessageList = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledScrollWrapper componentInstanceId={AI_CHAT_SCROLL_WRAPPER_ID}>
|
||||
{agentChatMessageIdsComponent.map((messageId) => {
|
||||
return <AIChatMessage messageId={messageId} key={messageId} />;
|
||||
})}
|
||||
<AIChatErrorUnderMessageList />
|
||||
</StyledScrollWrapper>
|
||||
<StyledScrollWrapperContainer>
|
||||
<ScrollWrapper componentInstanceId={AI_CHAT_SCROLL_WRAPPER_ID}>
|
||||
{agentChatMessageIdsComponent.map((messageId) => {
|
||||
return <AIChatMessage messageId={messageId} key={messageId} />;
|
||||
})}
|
||||
<AIChatErrorUnderMessageList />
|
||||
</ScrollWrapper>
|
||||
</StyledScrollWrapperContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -140,8 +140,9 @@ const StyledReasoningText = styled.p`
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
|
||||
const StyledOrbitLoaderIcon = styled(ThinkingOrbitLoaderIcon)`
|
||||
const StyledOrbitLoaderIconContainer = styled.span`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
@@ -214,7 +215,7 @@ const StyledToolDetailsContainer = styled.div`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledToolTabList = styled(TabList)`
|
||||
const StyledToolTabListContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
@@ -334,11 +335,13 @@ const ThinkingToolStepRow = ({
|
||||
<StyledToolErrorText>{part.errorText}</StyledToolErrorText>
|
||||
) : (
|
||||
<StyledToolDetailsContent>
|
||||
<StyledToolTabList
|
||||
tabs={toolTabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={toolTabListComponentInstanceId}
|
||||
/>
|
||||
<StyledToolTabListContainer>
|
||||
<TabList
|
||||
tabs={toolTabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={toolTabListComponentInstanceId}
|
||||
/>
|
||||
</StyledToolTabListContainer>
|
||||
<StyledToolJsonContent>
|
||||
<StyledJsonTreeContainer>
|
||||
<JsonTree
|
||||
@@ -388,7 +391,13 @@ const ThinkingStepRow = ({
|
||||
return (
|
||||
<StyledRow>
|
||||
<StyledIconContainer>
|
||||
{isActive ? <StyledOrbitLoaderIcon /> : <IconCpu size={14} />}
|
||||
{isActive ? (
|
||||
<StyledOrbitLoaderIconContainer>
|
||||
<ThinkingOrbitLoaderIcon />
|
||||
</StyledOrbitLoaderIconContainer>
|
||||
) : (
|
||||
<IconCpu size={14} />
|
||||
)}
|
||||
</StyledIconContainer>
|
||||
<StyledRowLabelContainer>
|
||||
<StyledRowLabel>{isActive ? t`Thinking` : t`Thought`}</StyledRowLabel>
|
||||
|
||||
+9
-8
@@ -29,7 +29,7 @@ const StyledTitle = styled.div`
|
||||
padding: 0 ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledSuggestedPromptButton = styled(LightButton)`
|
||||
const StyledSuggestedPromptButtonContainer = styled.div`
|
||||
align-self: flex-start;
|
||||
`;
|
||||
|
||||
@@ -62,13 +62,14 @@ export const AIChatSuggestedPrompts = ({
|
||||
<StyledContainer>
|
||||
<StyledTitle>{t`What can I help you with?`}</StyledTitle>
|
||||
{DEFAULT_SUGGESTED_PROMPTS.map((prompt) => (
|
||||
<StyledSuggestedPromptButton
|
||||
key={prompt.id}
|
||||
Icon={prompt.Icon}
|
||||
title={resolveMessage(prompt.label)}
|
||||
accent="secondary"
|
||||
onClick={() => handleClick(prompt)}
|
||||
/>
|
||||
<StyledSuggestedPromptButtonContainer key={prompt.id}>
|
||||
<LightButton
|
||||
Icon={prompt.Icon}
|
||||
title={resolveMessage(prompt.label)}
|
||||
accent="secondary"
|
||||
onClick={() => handleClick(prompt)}
|
||||
/>
|
||||
</StyledSuggestedPromptButtonContainer>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
+25
-21
@@ -31,11 +31,11 @@ const StyledRow = styled.div`
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledSelect = styled(Select<string>)`
|
||||
const StyledSelectContainer = styled.div`
|
||||
flex: 1 1;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
const StyledButtonContainer = styled.div`
|
||||
flex: 0 0 auto;
|
||||
`;
|
||||
|
||||
@@ -160,26 +160,30 @@ export const MeteredPriceSelector = ({
|
||||
description={t`Number of new credits allocated every ${recurringInterval}`}
|
||||
/>
|
||||
<StyledRow>
|
||||
<StyledSelect
|
||||
dropdownId="settings_billing-metered-price"
|
||||
options={options}
|
||||
value={selectedPriceId ?? currentMeteredPrice.stripePriceId}
|
||||
onChange={handleChange}
|
||||
disabled={isUpdating || isTrialing}
|
||||
description={
|
||||
isTrialing ? t`Please start your subscription first` : undefined
|
||||
}
|
||||
fullWidth
|
||||
/>
|
||||
{isChanged && (
|
||||
<StyledButton
|
||||
title={isUpgrade() ? t`Upgrade` : t`Downgrade`}
|
||||
onClick={handleOpenConfirm}
|
||||
variant="primary"
|
||||
isLoading={isUpdating}
|
||||
disabled={!isChanged}
|
||||
accent={isUpgrade() ? 'blue' : 'danger'}
|
||||
<StyledSelectContainer>
|
||||
<Select
|
||||
dropdownId="settings_billing-metered-price"
|
||||
options={options}
|
||||
value={selectedPriceId ?? currentMeteredPrice.stripePriceId}
|
||||
onChange={handleChange}
|
||||
disabled={isUpdating || isTrialing}
|
||||
description={
|
||||
isTrialing ? t`Please start your subscription first` : undefined
|
||||
}
|
||||
fullWidth
|
||||
/>
|
||||
</StyledSelectContainer>
|
||||
{isChanged && (
|
||||
<StyledButtonContainer>
|
||||
<Button
|
||||
title={isUpgrade() ? t`Upgrade` : t`Downgrade`}
|
||||
onClick={handleOpenConfirm}
|
||||
variant="primary"
|
||||
isLoading={isUpdating}
|
||||
disabled={!isChanged}
|
||||
accent={isUpgrade() ? 'blue' : 'danger'}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
)}
|
||||
</StyledRow>
|
||||
<ConfirmationModal
|
||||
|
||||
+19
-13
@@ -11,13 +11,15 @@ import { Chip, ChipVariant } from 'twenty-ui/components';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
const StyledRecordChip = styled(RecordChip)`
|
||||
const StyledRecordChipContainer = styled.div`
|
||||
display: inline;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0 ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledInlineMentionRecordChip = styled(MentionRecordChip)`
|
||||
const StyledInlineMentionRecordChipContainer = styled.div`
|
||||
display: inline;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0 ${themeCssVariables.spacing[1]};
|
||||
@@ -69,11 +71,13 @@ const LegacyMentionRenderer = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRecordChip
|
||||
objectNameSingular={objectMetadataItem.nameSingular}
|
||||
record={record}
|
||||
forceDisableClick={false}
|
||||
/>
|
||||
<StyledRecordChipContainer>
|
||||
<RecordChip
|
||||
objectNameSingular={objectMetadataItem.nameSingular}
|
||||
record={record}
|
||||
forceDisableClick={false}
|
||||
/>
|
||||
</StyledRecordChipContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -112,12 +116,14 @@ export const MentionInlineContent = createReactInlineContentSpec(
|
||||
// New notes store objectNameSingular + label + imageUrl directly
|
||||
if (isNonEmptyString(objectNameSingular) && isNonEmptyString(label)) {
|
||||
return (
|
||||
<StyledInlineMentionRecordChip
|
||||
recordId={recordId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
label={label}
|
||||
imageUrl={imageUrl}
|
||||
/>
|
||||
<StyledInlineMentionRecordChipContainer>
|
||||
<MentionRecordChip
|
||||
recordId={recordId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
label={label}
|
||||
imageUrl={imageUrl}
|
||||
/>
|
||||
</StyledInlineMentionRecordChipContainer>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+16
-14
@@ -25,8 +25,8 @@ type CommandMenuItemNumberInputProps = {
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
const StyledRightAlignedTextInput = styled(TextInput)`
|
||||
input {
|
||||
const StyledRightAlignedTextInputContainer = styled.div`
|
||||
& input {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
@@ -132,18 +132,20 @@ export const CommandMenuItemNumberInput = ({
|
||||
Icon={Icon}
|
||||
onClick={focusInput}
|
||||
RightComponent={
|
||||
<StyledRightAlignedTextInput
|
||||
ref={inputRef}
|
||||
value={draftValue}
|
||||
sizeVariant="sm"
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
error={hasError ? ' ' : undefined}
|
||||
noErrorHelper
|
||||
textClickOutsideId={focusId}
|
||||
/>
|
||||
<StyledRightAlignedTextInputContainer>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={draftValue}
|
||||
sizeVariant="sm"
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
error={hasError ? ' ' : undefined}
|
||||
noErrorHelper
|
||||
textClickOutsideId={focusId}
|
||||
/>
|
||||
</StyledRightAlignedTextInputContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+14
-12
@@ -19,8 +19,8 @@ type CommandMenuItemTextInputProps = {
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
const StyledRightAlignedTextInput = styled(TextInput)`
|
||||
input {
|
||||
const StyledRightAlignedTextInputContainer = styled.div`
|
||||
& input {
|
||||
text-align: right;
|
||||
}
|
||||
`;
|
||||
@@ -96,16 +96,18 @@ export const CommandMenuItemTextInput = ({
|
||||
Icon={Icon}
|
||||
onClick={focusInput}
|
||||
RightComponent={
|
||||
<StyledRightAlignedTextInput
|
||||
ref={inputRef}
|
||||
value={draftValue}
|
||||
sizeVariant="sm"
|
||||
onChange={setDraftValue}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
textClickOutsideId={focusId}
|
||||
/>
|
||||
<StyledRightAlignedTextInputContainer>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={draftValue}
|
||||
sizeVariant="sm"
|
||||
onChange={setDraftValue}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder={placeholder}
|
||||
textClickOutsideId={focusId}
|
||||
/>
|
||||
</StyledRightAlignedTextInputContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
+10
-8
@@ -18,7 +18,7 @@ const StyledText = styled.div`
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledCloseButton = styled(IconButton)`
|
||||
const StyledCloseButtonContainer = styled.div`
|
||||
color: ${themeCssVariables.grayScale.gray1};
|
||||
display: flex;
|
||||
`;
|
||||
@@ -79,13 +79,15 @@ export const InformationBanner = ({
|
||||
)}
|
||||
</StyledContent>
|
||||
{onClose && (
|
||||
<StyledCloseButton
|
||||
Icon={IconX}
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
onClick={onClose}
|
||||
ariaLabel={t`Close banner`}
|
||||
/>
|
||||
<StyledCloseButtonContainer>
|
||||
<IconButton
|
||||
Icon={IconX}
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
onClick={onClose}
|
||||
ariaLabel={t`Close banner`}
|
||||
/>
|
||||
</StyledCloseButtonContainer>
|
||||
)}
|
||||
</Banner>
|
||||
)}
|
||||
|
||||
+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>
|
||||
);
|
||||
};
|
||||
|
||||
+8
-6
@@ -10,7 +10,7 @@ type OnboardingSyncEmailsSettingsCardProps = {
|
||||
value?: MessageChannelVisibility;
|
||||
};
|
||||
|
||||
const StyledCardMedia = styled(SettingsAccountsVisibilityIcon)`
|
||||
const StyledCardMediaContainer = styled.div`
|
||||
width: ${themeCssVariables.spacing[10]};
|
||||
`;
|
||||
|
||||
@@ -21,11 +21,13 @@ export const OnboardingSyncEmailsSettingsCard = ({
|
||||
const optionsWithCardMedia = ONBOARDING_SYNC_EMAILS_OPTIONS.map((option) => ({
|
||||
...option,
|
||||
cardMedia: (
|
||||
<StyledCardMedia
|
||||
metadata={option.cardMediaProps.metadata}
|
||||
subject={option.cardMediaProps.subject}
|
||||
body={option.cardMediaProps.body}
|
||||
/>
|
||||
<StyledCardMediaContainer>
|
||||
<SettingsAccountsVisibilityIcon
|
||||
metadata={option.cardMediaProps.metadata}
|
||||
subject={option.cardMediaProps.subject}
|
||||
body={option.cardMediaProps.body}
|
||||
/>
|
||||
</StyledCardMediaContainer>
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
+25
-21
@@ -42,11 +42,11 @@ const StyledTabsAndDashboardContainer = styled.div`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledPageLayoutTabList = styled(PageLayoutTabList)`
|
||||
const StyledPageLayoutTabListContainer = styled.div`
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledScrollWrapper = styled(ScrollWrapper)`
|
||||
const StyledScrollWrapperContainer = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
@@ -130,27 +130,31 @@ export const PageLayoutRendererContent = () => {
|
||||
}
|
||||
/>
|
||||
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
|
||||
<StyledPageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={!isInSidePanel && !isPageLayoutInEditMode}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={canEnableTabEditing}
|
||||
onReorder={canEnableTabEditing ? reorderTabs : undefined}
|
||||
pageLayoutType={currentPageLayout.type}
|
||||
/>
|
||||
<StyledPageLayoutTabListContainer>
|
||||
<PageLayoutTabList
|
||||
tabs={sortedTabs}
|
||||
behaveAsLinks={!isInSidePanel && !isPageLayoutInEditMode}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={canEnableTabEditing}
|
||||
onReorder={canEnableTabEditing ? reorderTabs : undefined}
|
||||
pageLayoutType={currentPageLayout.type}
|
||||
/>
|
||||
</StyledPageLayoutTabListContainer>
|
||||
)}
|
||||
|
||||
<StyledScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</StyledScrollWrapper>
|
||||
<StyledScrollWrapperContainer>
|
||||
<ScrollWrapper
|
||||
componentInstanceId={getScrollWrapperInstanceIdFromPageLayoutId(
|
||||
currentPageLayout.id,
|
||||
)}
|
||||
defaultEnableXScroll={false}
|
||||
>
|
||||
{isDefined(activeTabId) && (
|
||||
<PageLayoutMainContent tabId={activeTabId} />
|
||||
)}
|
||||
</ScrollWrapper>
|
||||
</StyledScrollWrapperContainer>
|
||||
</StyledTabsAndDashboardContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
+12
-7
@@ -26,9 +26,12 @@ import { LightIconButton } from 'twenty-ui/input';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
const StyledLinkContainer = styled.div`
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
|
||||
> * {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSeeAllButtonWrapper = styled.div<{ isMobile: boolean }>`
|
||||
@@ -131,11 +134,13 @@ export const WidgetActionFieldSeeAll = () => {
|
||||
return (
|
||||
<>
|
||||
<div id={tooltipId}>
|
||||
<StyledLink to={filterLinkHref} data-testid="widget-see-all-link">
|
||||
<StyledSeeAllButtonWrapper isMobile={isMobile}>
|
||||
<LightIconButton Icon={IconArrowUpRight} accent="secondary" />
|
||||
</StyledSeeAllButtonWrapper>
|
||||
</StyledLink>
|
||||
<StyledLinkContainer>
|
||||
<Link to={filterLinkHref} data-testid="widget-see-all-link">
|
||||
<StyledSeeAllButtonWrapper isMobile={isMobile}>
|
||||
<LightIconButton Icon={IconArrowUpRight} accent="secondary" />
|
||||
</StyledSeeAllButtonWrapper>
|
||||
</Link>
|
||||
</StyledLinkContainer>
|
||||
</div>
|
||||
<AppTooltip
|
||||
anchorSelect={`#${tooltipId}`}
|
||||
|
||||
+5
-2
@@ -30,7 +30,8 @@ const StyledButton = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(IconChevronDown)`
|
||||
const StyledIconContainer = styled.span`
|
||||
display: flex;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
`;
|
||||
@@ -41,7 +42,9 @@ export const FieldWidgetShowMoreButton = ({
|
||||
}: FieldWidgetShowMoreButtonProps) => {
|
||||
return (
|
||||
<StyledButton data-testid="field-widget-show-more-button" onClick={onClick}>
|
||||
<StyledIcon />
|
||||
<StyledIconContainer>
|
||||
<IconChevronDown />
|
||||
</StyledIconContainer>
|
||||
{t`More (${remainingCount})`}
|
||||
</StyledButton>
|
||||
);
|
||||
|
||||
+7
-2
@@ -40,9 +40,14 @@ const StyledPropertyBox = styled.div`
|
||||
padding-bottom: ${themeCssVariables.spacing[3]};
|
||||
`;
|
||||
|
||||
const StyledInlineFieldsPropertyBox = styled(StyledPropertyBox)`
|
||||
padding-bottom: 0;
|
||||
const StyledInlineFieldsPropertyBox = styled.div`
|
||||
align-self: stretch;
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
`;
|
||||
|
||||
type FieldsWidgetProps = {
|
||||
|
||||
+8
-7
@@ -37,9 +37,11 @@ const StyledTrendIconContainer = styled.div`
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
font-size: ${themeCssVariables.font.size.xxl};
|
||||
margin: 0;
|
||||
const StyledH1TitleWrapper = styled.div`
|
||||
> h2 {
|
||||
font-size: ${themeCssVariables.font.size.xxl};
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const GraphWidgetAggregateChart = ({
|
||||
@@ -58,10 +60,9 @@ export const GraphWidgetAggregateChart = ({
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledH1Title
|
||||
title={displayValue}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
<StyledH1TitleWrapper>
|
||||
<H1Title title={displayValue} fontColor={H1TitleFontColor.Primary} />
|
||||
</StyledH1TitleWrapper>
|
||||
{isDefined(trendPercentage) && (
|
||||
<StyledTrendIconContainer>
|
||||
<StyledTrendPercentageValue>
|
||||
|
||||
+7
-5
@@ -51,7 +51,7 @@ const StyledChartContainer = styled.div<{ $isClickable?: boolean }>`
|
||||
: ''}
|
||||
`;
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
const StyledH1TitleWrapper = styled.div`
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -135,10 +135,12 @@ export const GraphWidgetGaugeChart = ({
|
||||
layers={['bars', renderValueEndLine]}
|
||||
/>
|
||||
{showValue && (
|
||||
<StyledH1Title
|
||||
title={formattedValue}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
<StyledH1TitleWrapper>
|
||||
<H1Title
|
||||
title={formattedValue}
|
||||
fontColor={H1TitleFontColor.Primary}
|
||||
/>
|
||||
</StyledH1TitleWrapper>
|
||||
)}
|
||||
</StyledChartContainer>
|
||||
<GraphWidgetLegend
|
||||
|
||||
+25
-21
@@ -13,11 +13,11 @@ type SettingsAccountsBlocklistTableProps = {
|
||||
handleBlockedEmailRemove: (id: string) => void;
|
||||
};
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
const StyledTableContainer = styled.div`
|
||||
margin-top: ${themeCssVariables.spacing[4]};
|
||||
`;
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
const StyledTableBodyContainer = styled.div`
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
`;
|
||||
|
||||
@@ -28,25 +28,29 @@ export const SettingsAccountsBlocklistTable = ({
|
||||
return (
|
||||
<>
|
||||
{blocklist.length > 0 && (
|
||||
<StyledTable>
|
||||
<TableRow
|
||||
gridAutoColumns="200px 1fr 20px"
|
||||
mobileGridAutoColumns="120px 1fr 20px"
|
||||
>
|
||||
<TableHeader>{t`Email/Domain`}</TableHeader>
|
||||
<TableHeader>{t`Added to blocklist`}</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</TableRow>
|
||||
<StyledTableBody>
|
||||
{blocklist.map((blocklistItem) => (
|
||||
<SettingsAccountsBlocklistTableRow
|
||||
key={blocklistItem.id}
|
||||
blocklistItem={blocklistItem}
|
||||
onRemove={handleBlockedEmailRemove}
|
||||
/>
|
||||
))}
|
||||
</StyledTableBody>
|
||||
</StyledTable>
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<TableRow
|
||||
gridAutoColumns="200px 1fr 20px"
|
||||
mobileGridAutoColumns="120px 1fr 20px"
|
||||
>
|
||||
<TableHeader>{t`Email/Domain`}</TableHeader>
|
||||
<TableHeader>{t`Added to blocklist`}</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</TableRow>
|
||||
<StyledTableBodyContainer>
|
||||
<TableBody>
|
||||
{blocklist.map((blocklistItem) => (
|
||||
<SettingsAccountsBlocklistTableRow
|
||||
key={blocklistItem.id}
|
||||
blocklistItem={blocklistItem}
|
||||
onRemove={handleBlockedEmailRemove}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</StyledTableBodyContainer>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
+14
-4
@@ -11,8 +11,10 @@ type SettingsAccountsEventVisibilitySettingsCardProps = {
|
||||
value?: CalendarChannelVisibility;
|
||||
};
|
||||
|
||||
const StyledCardMedia = styled(SettingsAccountsVisibilityIcon)`
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
const StyledCardMediaContainer = styled.div`
|
||||
> * {
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
}
|
||||
`;
|
||||
|
||||
const eventSettingsVisibilityOptions = [
|
||||
@@ -20,13 +22,21 @@ const eventSettingsVisibilityOptions = [
|
||||
title: msg`Everything`,
|
||||
description: msg`The whole event details will be shared with your team.`,
|
||||
value: CalendarChannelVisibility.SHARE_EVERYTHING,
|
||||
cardMedia: <StyledCardMedia subject="active" body="active" />,
|
||||
cardMedia: (
|
||||
<StyledCardMediaContainer>
|
||||
<SettingsAccountsVisibilityIcon subject="active" body="active" />
|
||||
</StyledCardMediaContainer>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: msg`Metadata`,
|
||||
description: msg`Only date & participants will be shared with your team.`,
|
||||
value: CalendarChannelVisibility.METADATA,
|
||||
cardMedia: <StyledCardMedia subject="active" body="inactive" />,
|
||||
cardMedia: (
|
||||
<StyledCardMediaContainer>
|
||||
<SettingsAccountsVisibilityIcon subject="active" body="inactive" />
|
||||
</StyledCardMediaContainer>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
+18
-14
@@ -19,11 +19,13 @@ const StyledTableRows = styled.div`
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledAddAccountSection = styled(Section)`
|
||||
border-top: 1px solid ${themeCssVariables.border.color.light};
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
const StyledAddAccountSectionContainer = styled.div`
|
||||
> * {
|
||||
border-top: 1px solid ${themeCssVariables.border.color.light};
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsAccountsConnectedAccountsListCard = ({
|
||||
@@ -51,15 +53,17 @@ export const SettingsAccountsConnectedAccountsListCard = ({
|
||||
))}
|
||||
</StyledTableRows>
|
||||
</Table>
|
||||
<StyledAddAccountSection>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Add account`}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
onClick={() => navigateSettings(SettingsPath.NewAccount)}
|
||||
/>
|
||||
</StyledAddAccountSection>
|
||||
<StyledAddAccountSectionContainer>
|
||||
<Section>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Add account`}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
onClick={() => navigateSettings(SettingsPath.NewAccount)}
|
||||
/>
|
||||
</Section>
|
||||
</StyledAddAccountSectionContainer>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
+11
-2
@@ -1,6 +1,5 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type SettingsAccountsMessageAutoCreationIconProps = {
|
||||
@@ -9,8 +8,18 @@ type SettingsAccountsMessageAutoCreationIconProps = {
|
||||
isReceivedActive?: boolean;
|
||||
};
|
||||
|
||||
const StyledIconContainer = styled(SettingsAccountsCardMedia)`
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: stretch;
|
||||
border: 2px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing['0.5']};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: center;
|
||||
padding: ${themeCssVariables.spacing['0.5']};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
const StyledDirectionSkeleton = styled.div<{ isActive?: boolean }>`
|
||||
|
||||
+10
-2
@@ -1,7 +1,6 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { MessageFolderImportPolicy } from '@/accounts/types/MessageChannel';
|
||||
import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type SettingsAccountsMessageFolderIconProps = {
|
||||
@@ -9,9 +8,18 @@ type SettingsAccountsMessageFolderIconProps = {
|
||||
value?: MessageFolderImportPolicy;
|
||||
};
|
||||
|
||||
const StyledCardMedia = styled(SettingsAccountsCardMedia)`
|
||||
const StyledCardMedia = styled.div`
|
||||
align-items: stretch;
|
||||
border: 2px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing['0.5']};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: center;
|
||||
padding: ${themeCssVariables.spacing['0.5']};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
const StyledFolderRow = styled.div`
|
||||
|
||||
+40
-33
@@ -14,11 +14,13 @@ type SettingsAccountsRadioSettingsCardProps<Option extends { value: string }> =
|
||||
name: string;
|
||||
};
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
cursor: pointer;
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -39,8 +41,10 @@ const StyledDescription = styled.div`
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
const StyledRadio = styled(Radio)`
|
||||
const StyledRadioContainer = styled.span`
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledExpandedContent = styled.div`
|
||||
@@ -63,34 +67,37 @@ export const SettingsAccountsRadioSettingsCard = <
|
||||
}: SettingsAccountsRadioSettingsCardProps<Option>) => (
|
||||
<Card rounded>
|
||||
{options.map((option, index) => (
|
||||
<StyledCardContent
|
||||
key={option.value}
|
||||
divider={index < options.length - 1}
|
||||
onClick={() => onChange(option.value)}
|
||||
>
|
||||
<StyledOptionHeader>
|
||||
{option.cardMedia}
|
||||
<div>
|
||||
<StyledTitle>
|
||||
<Trans id={option.title.id} />
|
||||
</StyledTitle>
|
||||
<StyledDescription>
|
||||
<Trans id={option.description.id} />
|
||||
</StyledDescription>
|
||||
</div>
|
||||
<StyledRadio
|
||||
name={name}
|
||||
value={option.value}
|
||||
onCheckedChange={() => onChange(option.value)}
|
||||
checked={value === option.value}
|
||||
/>
|
||||
</StyledOptionHeader>
|
||||
{option.cardContentExpanded && value === option.value && (
|
||||
<StyledExpandedContent>
|
||||
{option.cardContentExpanded}
|
||||
</StyledExpandedContent>
|
||||
)}
|
||||
</StyledCardContent>
|
||||
<StyledCardContentContainer key={option.value}>
|
||||
<CardContent
|
||||
divider={index < options.length - 1}
|
||||
onClick={() => onChange(option.value)}
|
||||
>
|
||||
<StyledOptionHeader>
|
||||
{option.cardMedia}
|
||||
<div>
|
||||
<StyledTitle>
|
||||
<Trans id={option.title.id} />
|
||||
</StyledTitle>
|
||||
<StyledDescription>
|
||||
<Trans id={option.description.id} />
|
||||
</StyledDescription>
|
||||
</div>
|
||||
<StyledRadioContainer>
|
||||
<Radio
|
||||
name={name}
|
||||
value={option.value}
|
||||
onCheckedChange={() => onChange(option.value)}
|
||||
checked={value === option.value}
|
||||
/>
|
||||
</StyledRadioContainer>
|
||||
</StyledOptionHeader>
|
||||
{option.cardContentExpanded && value === option.value && (
|
||||
<StyledExpandedContent>
|
||||
{option.cardContentExpanded}
|
||||
</StyledExpandedContent>
|
||||
)}
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
|
||||
+26
-19
@@ -14,14 +14,16 @@ type SettingsAccountsToggleSettingCardProps = {
|
||||
parameters: Parameter[];
|
||||
};
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[4]};
|
||||
cursor: pointer;
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[4]};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -36,8 +38,10 @@ const StyledDescription = styled.div`
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
const StyledToggle = styled(Toggle)`
|
||||
const StyledToggleContainer = styled.span`
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const SettingsAccountsToggleSettingCard = ({
|
||||
@@ -45,17 +49,20 @@ export const SettingsAccountsToggleSettingCard = ({
|
||||
}: SettingsAccountsToggleSettingCardProps) => (
|
||||
<Card rounded>
|
||||
{parameters.map((parameter, index) => (
|
||||
<StyledCardContent
|
||||
key={index}
|
||||
divider={index < parameters.length - 1}
|
||||
onClick={() => parameter.onToggle(!parameter.value)}
|
||||
>
|
||||
<div>
|
||||
<StyledTitle>{parameter.title}</StyledTitle>
|
||||
<StyledDescription>{parameter.description}</StyledDescription>
|
||||
</div>
|
||||
<StyledToggle value={parameter.value} onChange={parameter.onToggle} />
|
||||
</StyledCardContent>
|
||||
<StyledCardContentContainer key={index}>
|
||||
<CardContent
|
||||
divider={index < parameters.length - 1}
|
||||
onClick={() => parameter.onToggle(!parameter.value)}
|
||||
>
|
||||
<div>
|
||||
<StyledTitle>{parameter.title}</StyledTitle>
|
||||
<StyledDescription>{parameter.description}</StyledDescription>
|
||||
</div>
|
||||
<StyledToggleContainer>
|
||||
<Toggle value={parameter.value} onChange={parameter.onToggle} />
|
||||
</StyledToggleContainer>
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
|
||||
+24
-4
@@ -1,6 +1,5 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { SettingsAccountsCardMedia } from '@/settings/accounts/components/SettingsAccountsCardMedia';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type VisibilityElementState = 'active' | 'inactive';
|
||||
@@ -12,8 +11,18 @@ type SettingsAccountsVisibilityIconProps = {
|
||||
body?: VisibilityElementState;
|
||||
};
|
||||
|
||||
const StyledCardMedia = styled(SettingsAccountsCardMedia)`
|
||||
const StyledCardMedia = styled.div`
|
||||
align-items: stretch;
|
||||
border: 2px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing['0.5']};
|
||||
height: ${themeCssVariables.spacing[8]};
|
||||
justify-content: center;
|
||||
padding: ${themeCssVariables.spacing['0.5']};
|
||||
width: ${themeCssVariables.spacing[6]};
|
||||
`;
|
||||
|
||||
const StyledSubjectSkeleton = styled.div<{ isActive?: boolean }>`
|
||||
@@ -25,13 +34,24 @@ const StyledSubjectSkeleton = styled.div<{ isActive?: boolean }>`
|
||||
height: 3px;
|
||||
`;
|
||||
|
||||
const StyledMetadataSkeleton = styled(StyledSubjectSkeleton)`
|
||||
const StyledMetadataSkeleton = styled.div<{ isActive?: boolean }>`
|
||||
background-color: ${({ isActive }) =>
|
||||
isActive
|
||||
? themeCssVariables.accent.accent4060
|
||||
: themeCssVariables.background.quaternary};
|
||||
border-radius: 1px;
|
||||
height: 3px;
|
||||
margin-right: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledBodySkeleton = styled(StyledSubjectSkeleton)`
|
||||
const StyledBodySkeleton = styled.div<{ isActive?: boolean }>`
|
||||
background-color: ${({ isActive }) =>
|
||||
isActive
|
||||
? themeCssVariables.accent.accent4060
|
||||
: themeCssVariables.background.quaternary};
|
||||
border-radius: ${themeCssVariables.border.radius.xs};
|
||||
flex: 1 0 auto;
|
||||
height: 3px;
|
||||
`;
|
||||
|
||||
export const SettingsAccountsVisibilityIcon = ({
|
||||
|
||||
+9
-9
@@ -1,24 +1,24 @@
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledTableHeader = styled(TableHeader)`
|
||||
padding-right: ${themeCssVariables.spacing[14]};
|
||||
`;
|
||||
|
||||
export const SettingsConnectedAccountsTableHeader = () => {
|
||||
return (
|
||||
<Table>
|
||||
<TableRow gridAutoColumns="332px 1fr">
|
||||
<StyledTableHeader>
|
||||
<TableHeader
|
||||
padding={`0 ${themeCssVariables.spacing[14]} 0 ${themeCssVariables.spacing[2]}`}
|
||||
>
|
||||
<Trans>Account</Trans>
|
||||
</StyledTableHeader>
|
||||
<StyledTableHeader align="right">
|
||||
</TableHeader>
|
||||
<TableHeader
|
||||
align="right"
|
||||
padding={`0 ${themeCssVariables.spacing[14]} 0 ${themeCssVariables.spacing[2]}`}
|
||||
>
|
||||
<Trans>Status</Trans>
|
||||
</StyledTableHeader>
|
||||
</TableHeader>
|
||||
</TableRow>
|
||||
</Table>
|
||||
);
|
||||
|
||||
+20
-18
@@ -37,18 +37,11 @@ const StyledFoldersContainer = styled.div`
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledSearchInput = styled(SettingsTextInput)`
|
||||
const StyledSearchInputContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[2]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledCheckboxCell = styled(TableCell)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const StyledSectionHeader = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
@@ -61,8 +54,10 @@ const StyledSectionHeader = styled.div`
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled(Label)`
|
||||
const StyledLabelContainer = styled.span`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
margin-bottom: ${themeCssVariables.spacing[2]};
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -165,23 +160,30 @@ export const SettingsAccountsMessageFoldersCard = () => {
|
||||
return (
|
||||
<Section>
|
||||
<Table>
|
||||
<StyledSearchInput
|
||||
placeholder={t`Search folders...`}
|
||||
value={search}
|
||||
onChange={setSearch}
|
||||
instanceId={'message-folders-search'}
|
||||
/>
|
||||
<StyledLabel>{t`Folders`}</StyledLabel>
|
||||
<StyledSearchInputContainer>
|
||||
<SettingsTextInput
|
||||
placeholder={t`Search folders...`}
|
||||
value={search}
|
||||
onChange={setSearch}
|
||||
instanceId={'message-folders-search'}
|
||||
/>
|
||||
</StyledSearchInputContainer>
|
||||
<StyledLabelContainer>
|
||||
<Label>{t`Folders`}</Label>
|
||||
</StyledLabelContainer>
|
||||
|
||||
<StyledSectionHeader>
|
||||
<Label>{t`Toggle all folders`}</Label>
|
||||
<StyledCheckboxCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
padding={`0 ${themeCssVariables.spacing[1]} 0 ${themeCssVariables.spacing[2]}`}
|
||||
>
|
||||
<Checkbox
|
||||
checked={allFoldersToggled}
|
||||
onChange={() => handleToggleAllFolders(messageFolders)}
|
||||
size={CheckboxSize.Small}
|
||||
/>
|
||||
</StyledCheckboxCell>
|
||||
</TableCell>
|
||||
</StyledSectionHeader>
|
||||
|
||||
<StyledFoldersContainer>
|
||||
|
||||
+10
-8
@@ -37,7 +37,7 @@ const StyledSearchAndFilterContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledSearchInput = styled(SettingsTextInput)`
|
||||
const StyledSearchInputContainer = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
@@ -163,13 +163,15 @@ export const SettingsAdminAI = () => {
|
||||
/>
|
||||
|
||||
<StyledSearchAndFilterContainer>
|
||||
<StyledSearchInput
|
||||
instanceId="admin-model-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a model...`}
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
<StyledSearchInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId="admin-model-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a model...`}
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
</StyledSearchInputContainer>
|
||||
<Dropdown
|
||||
dropdownId="admin-ai-models-filter-dropdown"
|
||||
dropdownPlacement="bottom-end"
|
||||
|
||||
+11
-9
@@ -3,7 +3,7 @@ import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconSearch } from 'twenty-ui/display';
|
||||
|
||||
const StyledSearchInput = styled(SettingsTextInput)`
|
||||
const StyledSearchInputContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -17,13 +17,15 @@ export const ConfigVariableSearchInput = ({
|
||||
onChange,
|
||||
}: ConfigVariableSearchInputProps) => {
|
||||
return (
|
||||
<StyledSearchInput
|
||||
instanceId="config-variable-search"
|
||||
placeholder={t`Search config variables`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
autoFocus={false}
|
||||
LeftIcon={IconSearch}
|
||||
/>
|
||||
<StyledSearchInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId="config-variable-search"
|
||||
placeholder={t`Search config variables`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
autoFocus={false}
|
||||
LeftIcon={IconSearch}
|
||||
/>
|
||||
</StyledSearchInputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+38
-29
@@ -12,16 +12,11 @@ type SettingsAdminConfigVariablesRowProps = {
|
||||
variable: ConfigVariable;
|
||||
};
|
||||
|
||||
const StyledTruncatedCell = styled(TableCell)`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
const StyledTableRowContainer = styled.div`
|
||||
> * {
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -48,24 +43,38 @@ export const SettingsAdminConfigVariablesRow = ({
|
||||
: variable.value;
|
||||
|
||||
return (
|
||||
<StyledTableRow
|
||||
gridAutoColumns="5fr 3fr 1fr"
|
||||
to={getSettingsPath(SettingsPath.AdminPanelConfigVariableDetails, {
|
||||
variableName: variable.name,
|
||||
})}
|
||||
>
|
||||
<StyledTruncatedCell color={theme.font.color.primary}>
|
||||
<StyledEllipsisLabel>{variable.name}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
<StyledTruncatedCell align="right">
|
||||
<StyledEllipsisLabel>{displayValue}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
<TableCell align="right">
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
<StyledTableRowContainer>
|
||||
<TableRow
|
||||
gridAutoColumns="5fr 3fr 1fr"
|
||||
to={getSettingsPath(SettingsPath.AdminPanelConfigVariableDetails, {
|
||||
variableName: variable.name,
|
||||
})}
|
||||
>
|
||||
<TableCell
|
||||
color={theme.font.color.primary}
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{variable.name}</StyledEllipsisLabel>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="right"
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{displayValue}</StyledEllipsisLabel>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</StyledTableRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+11
-9
@@ -8,7 +8,7 @@ import { styled } from '@linaria/react';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { type ConfigVariable } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
const StyledTableBodyContainer = styled.div`
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
`;
|
||||
|
||||
@@ -26,14 +26,16 @@ export const SettingsAdminConfigVariablesTable = ({
|
||||
<TableHeader align="right">{t`Value`}</TableHeader>
|
||||
<TableHeader align="right"></TableHeader>
|
||||
</TableRow>
|
||||
<StyledTableBody>
|
||||
{variables.map((variable) => (
|
||||
<SettingsAdminConfigVariablesRow
|
||||
key={variable.name}
|
||||
variable={variable}
|
||||
/>
|
||||
))}
|
||||
</StyledTableBody>
|
||||
<StyledTableBodyContainer>
|
||||
<TableBody>
|
||||
{variables.map((variable) => (
|
||||
<SettingsAdminConfigVariablesRow
|
||||
key={variable.name}
|
||||
variable={variable}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</StyledTableBodyContainer>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
+14
-10
@@ -5,9 +5,11 @@ import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSettingsAdminTableCard = styled(SettingsAdminTableCard)`
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
const StyledSettingsAdminTableCardContainer = styled.div`
|
||||
> * {
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsAdminHealthAccountSyncCountersTable = ({
|
||||
@@ -47,13 +49,15 @@ export const SettingsAdminHealthAccountSyncCountersTable = ({
|
||||
return (
|
||||
<Section>
|
||||
<H2Title title={title} description={description} />
|
||||
<StyledSettingsAdminTableCard
|
||||
items={items}
|
||||
rounded
|
||||
gridAutoColumns="1fr 1fr"
|
||||
labelAlign="left"
|
||||
valueAlign="right"
|
||||
/>
|
||||
<StyledSettingsAdminTableCardContainer>
|
||||
<SettingsAdminTableCard
|
||||
items={items}
|
||||
rounded
|
||||
gridAutoColumns="1fr 1fr"
|
||||
labelAlign="left"
|
||||
valueAlign="right"
|
||||
/>
|
||||
</StyledSettingsAdminTableCardContainer>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
+24
-20
@@ -29,9 +29,11 @@ const StyledNoDataMessage = styled.div`
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const StyledSettingsAdminTableCard = styled(SettingsAdminTableCard)`
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
const StyledSettingsAdminTableCardContainer = styled.div`
|
||||
> * {
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
padding-right: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
type SettingsAdminWorkerMetricsGraphProps = {
|
||||
@@ -200,23 +202,25 @@ export const SettingsAdminWorkerMetricsGraph = ({
|
||||
)}
|
||||
</StyledGraphContainer>
|
||||
{metricsDetails && (
|
||||
<StyledSettingsAdminTableCard
|
||||
rounded
|
||||
items={Object.entries(metricsDetails)
|
||||
.filter(([key]) => key !== '__typename')
|
||||
.map(([key, value]) => ({
|
||||
label: key.charAt(0).toUpperCase() + key.slice(1),
|
||||
value:
|
||||
typeof value === 'number'
|
||||
? value
|
||||
: Array.isArray(value)
|
||||
? value.length
|
||||
: String(value),
|
||||
}))}
|
||||
gridAutoColumns="1fr 1fr"
|
||||
labelAlign="left"
|
||||
valueAlign="right"
|
||||
/>
|
||||
<StyledSettingsAdminTableCardContainer>
|
||||
<SettingsAdminTableCard
|
||||
rounded
|
||||
items={Object.entries(metricsDetails)
|
||||
.filter(([key]) => key !== '__typename')
|
||||
.map(([key, value]) => ({
|
||||
label: key.charAt(0).toUpperCase() + key.slice(1),
|
||||
value:
|
||||
typeof value === 'number'
|
||||
? value
|
||||
: Array.isArray(value)
|
||||
? value.length
|
||||
: String(value),
|
||||
}))}
|
||||
gridAutoColumns="1fr 1fr"
|
||||
labelAlign="left"
|
||||
valueAlign="right"
|
||||
/>
|
||||
</StyledSettingsAdminTableCardContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
+10
-6
@@ -26,8 +26,10 @@ const StyledDotContainer = styled.div<{ dotPosition: DotPosition }>`
|
||||
dotPosition === 'top' ? 'stretch' : 'center'};
|
||||
`;
|
||||
|
||||
const StyledIconPoint = styled(IconPoint)`
|
||||
const StyledIconPointContainer = styled.span`
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const AdvancedSettingsContentWrapperWithDot = ({
|
||||
@@ -41,11 +43,13 @@ export const AdvancedSettingsContentWrapperWithDot = ({
|
||||
<StyledWrapper>
|
||||
{!hideDot && (
|
||||
<StyledDotContainer dotPosition={dotPosition}>
|
||||
<StyledIconPoint
|
||||
size={12}
|
||||
color={theme.color.yellow}
|
||||
fill={theme.color.yellow}
|
||||
/>
|
||||
<StyledIconPointContainer>
|
||||
<IconPoint
|
||||
size={12}
|
||||
color={theme.color.yellow}
|
||||
fill={theme.color.yellow}
|
||||
/>
|
||||
</StyledIconPointContainer>
|
||||
</StyledDotContainer>
|
||||
)}
|
||||
{children}
|
||||
|
||||
@@ -18,28 +18,34 @@ type SettingsCardProps = {
|
||||
Status?: ReactNode;
|
||||
};
|
||||
|
||||
const StyledCard = styled(Card)<{
|
||||
const StyledCardWrapper = styled.div<{
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
clickable?: boolean;
|
||||
}>`
|
||||
color: ${({ disabled }) =>
|
||||
disabled
|
||||
? themeCssVariables.font.color.extraLight
|
||||
: themeCssVariables.font.color.tertiary};
|
||||
cursor: ${({ disabled, onClick }) =>
|
||||
disabled ? 'not-allowed' : onClick ? 'pointer' : 'default'};
|
||||
cursor: ${({ disabled, clickable }) =>
|
||||
disabled ? 'not-allowed' : clickable ? 'pointer' : 'default'};
|
||||
width: 100%;
|
||||
|
||||
> * {
|
||||
color: inherit;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]};
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[2]};
|
||||
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.background.quaternary};
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.background.quaternary};
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -61,8 +67,10 @@ const StyledTitle = styled.div<{ disabled?: boolean }>`
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
const StyledIconChevronRightContainer = styled.span`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
@@ -91,24 +99,31 @@ export const SettingsCard = ({
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<StyledCard
|
||||
<StyledCardWrapper
|
||||
disabled={disabled}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
clickable={!!onClick}
|
||||
className={className}
|
||||
rounded={true}
|
||||
>
|
||||
<StyledCardContent>
|
||||
<StyledHeader>
|
||||
<StyledIconContainer>{Icon}</StyledIconContainer>
|
||||
<StyledTitle disabled={disabled}>
|
||||
{title}
|
||||
{soon && <Pill label={t`Soon`} />}
|
||||
</StyledTitle>
|
||||
{Status && Status}
|
||||
<StyledIconChevronRight size={theme.icon.size.sm} />
|
||||
</StyledHeader>
|
||||
{description && <StyledDescription>{description}</StyledDescription>}
|
||||
</StyledCardContent>
|
||||
</StyledCard>
|
||||
<Card onClick={disabled ? undefined : onClick} rounded={true} fullWidth>
|
||||
<StyledCardContentContainer>
|
||||
<CardContent>
|
||||
<StyledHeader>
|
||||
<StyledIconContainer>{Icon}</StyledIconContainer>
|
||||
<StyledTitle disabled={disabled}>
|
||||
{title}
|
||||
{soon && <Pill label={t`Soon`} />}
|
||||
</StyledTitle>
|
||||
{Status && Status}
|
||||
<StyledIconChevronRightContainer>
|
||||
<IconChevronRight size={theme.icon.size.sm} />
|
||||
</StyledIconChevronRightContainer>
|
||||
</StyledHeader>
|
||||
{description && (
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
)}
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
</Card>
|
||||
</StyledCardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
+23
-17
@@ -14,10 +14,12 @@ const StyledNameCell = styled.div`
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.light};
|
||||
cursor: pointer;
|
||||
const StyledTableRowContainer = styled.div`
|
||||
> * {
|
||||
&:hover {
|
||||
background-color: ${themeCssVariables.background.transparent.light};
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -32,19 +34,23 @@ export const SettingsConnectedAccountsTableRow = ({
|
||||
const IconComponent = SettingsConnectedAccountIcon({ account });
|
||||
|
||||
return (
|
||||
<StyledTableRow key={account.id} gridAutoColumns="332px 1fr">
|
||||
<TableCell>
|
||||
<StyledNameCell>
|
||||
<IconComponent
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
<StyledTableRowContainer>
|
||||
<TableRow key={account.id} gridAutoColumns="332px 1fr">
|
||||
<TableCell>
|
||||
<StyledNameCell>
|
||||
<IconComponent
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
{account.handle}
|
||||
</StyledNameCell>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<SettingsAccountsConnectedAccountsRowRightContainer
|
||||
account={account}
|
||||
/>
|
||||
{account.handle}
|
||||
</StyledNameCell>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<SettingsAccountsConnectedAccountsRowRightContainer account={account} />
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</StyledTableRowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,9 +25,10 @@ const StyledCounterContainer = styled.div<{ showButtons: boolean }>`
|
||||
: themeCssVariables.spacing[16]};
|
||||
`;
|
||||
|
||||
const StyledTextInput = styled(SettingsTextInput)`
|
||||
const StyledTextInputContainer = styled.div`
|
||||
width: ${themeCssVariables.spacing[16]};
|
||||
input {
|
||||
|
||||
> * input {
|
||||
width: ${themeCssVariables.spacing[16]};
|
||||
height: ${themeCssVariables.spacing[6]};
|
||||
text-align: center;
|
||||
@@ -84,14 +85,16 @@ export const SettingsCounter = ({
|
||||
disabled={disabled}
|
||||
/>
|
||||
)}
|
||||
<StyledTextInput
|
||||
instanceId="settings-counter-input"
|
||||
name="counter"
|
||||
fullWidth
|
||||
value={value.toString()}
|
||||
onChange={handleTextInputChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<StyledTextInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId="settings-counter-input"
|
||||
name="counter"
|
||||
fullWidth
|
||||
value={value.toString()}
|
||||
onChange={handleTextInputChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</StyledTextInputContainer>
|
||||
{showButtons && (
|
||||
<IconButton
|
||||
size="small"
|
||||
|
||||
@@ -28,15 +28,16 @@ type SettingsDnsRecordsTableProps = {
|
||||
records: DnsRecord[];
|
||||
};
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
& > * {
|
||||
const StyledTableRowContainer = styled.div`
|
||||
> * > * {
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableCell = styled(TableCell)`
|
||||
const StyledTableCellFontWrapper = styled.div`
|
||||
display: contents;
|
||||
font-family: monospace;
|
||||
`;
|
||||
|
||||
@@ -69,53 +70,69 @@ export const SettingsDnsRecordsTable = ({
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<StyledTableRow gridAutoColumns={gridAutoColumns}>
|
||||
<TableHeader align="center">{t`Type`}</TableHeader>
|
||||
<TableHeader align="center">{t`Key`}</TableHeader>
|
||||
<TableHeader align="center">{t`Value`}</TableHeader>
|
||||
{hasPriorityRecords && (
|
||||
<TableHeader align="center">{t`Priority`}</TableHeader>
|
||||
)}
|
||||
{hasTtlRecords && <TableHeader align="center">{t`TTL`}</TableHeader>}
|
||||
{hasStatusRecords && (
|
||||
<TableHeader align="center">{t`Status`}</TableHeader>
|
||||
)}
|
||||
</StyledTableRow>
|
||||
<StyledTableRowContainer>
|
||||
<TableRow gridAutoColumns={gridAutoColumns}>
|
||||
<TableHeader align="center">{t`Type`}</TableHeader>
|
||||
<TableHeader align="center">{t`Key`}</TableHeader>
|
||||
<TableHeader align="center">{t`Value`}</TableHeader>
|
||||
{hasPriorityRecords && (
|
||||
<TableHeader align="center">{t`Priority`}</TableHeader>
|
||||
)}
|
||||
{hasTtlRecords && <TableHeader align="center">{t`TTL`}</TableHeader>}
|
||||
{hasStatusRecords && (
|
||||
<TableHeader align="center">{t`Status`}</TableHeader>
|
||||
)}
|
||||
</TableRow>
|
||||
</StyledTableRowContainer>
|
||||
|
||||
{records.map((record) => (
|
||||
<StyledTableRow key={record.value} gridAutoColumns={gridAutoColumns}>
|
||||
<TableCell>{record.type}</TableCell>
|
||||
<StyledTableCell
|
||||
onClick={() => {
|
||||
copyToClipboard(record.key || '');
|
||||
}}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={record.key} />
|
||||
</StyledTableCell>
|
||||
<StyledTableRowContainer key={record.value}>
|
||||
<TableRow gridAutoColumns={gridAutoColumns}>
|
||||
<TableCell>{record.type}</TableCell>
|
||||
<StyledTableCellFontWrapper>
|
||||
<TableCell
|
||||
onClick={() => {
|
||||
copyToClipboard(record.key || '');
|
||||
}}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={record.key} />
|
||||
</TableCell>
|
||||
</StyledTableCellFontWrapper>
|
||||
|
||||
<StyledTableCell
|
||||
onClick={() => {
|
||||
copyToClipboard(record.value);
|
||||
}}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={record.value} />
|
||||
</StyledTableCell>
|
||||
<StyledTableCellFontWrapper>
|
||||
<TableCell
|
||||
onClick={() => {
|
||||
copyToClipboard(record.value);
|
||||
}}
|
||||
>
|
||||
<OverflowingTextWithTooltip text={record.value} />
|
||||
</TableCell>
|
||||
</StyledTableCellFontWrapper>
|
||||
|
||||
{hasPriorityRecords && (
|
||||
<StyledTableCell>{record.priority}</StyledTableCell>
|
||||
)}
|
||||
{hasTtlRecords && <StyledTableCell>{record.ttl}</StyledTableCell>}
|
||||
{hasStatusRecords && (
|
||||
<StyledTableCell>
|
||||
{'status' in record ? (
|
||||
<Status
|
||||
color={record.statusColor}
|
||||
text={capitalize(record.status)}
|
||||
/>
|
||||
) : null}
|
||||
</StyledTableCell>
|
||||
)}
|
||||
</StyledTableRow>
|
||||
{hasPriorityRecords && (
|
||||
<StyledTableCellFontWrapper>
|
||||
<TableCell>{record.priority}</TableCell>
|
||||
</StyledTableCellFontWrapper>
|
||||
)}
|
||||
{hasTtlRecords && (
|
||||
<StyledTableCellFontWrapper>
|
||||
<TableCell>{record.ttl}</TableCell>
|
||||
</StyledTableCellFontWrapper>
|
||||
)}
|
||||
{hasStatusRecords && (
|
||||
<StyledTableCellFontWrapper>
|
||||
<TableCell>
|
||||
{'status' in record ? (
|
||||
<Status
|
||||
color={record.statusColor}
|
||||
text={capitalize(record.status)}
|
||||
/>
|
||||
) : null}
|
||||
</TableCell>
|
||||
</StyledTableCellFontWrapper>
|
||||
)}
|
||||
</TableRow>
|
||||
</StyledTableRowContainer>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
|
||||
@@ -8,10 +8,12 @@ import { Card, CardFooter } from 'twenty-ui/layout';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { SettingsListItemCardContent } from './SettingsListItemCardContent';
|
||||
|
||||
const StyledFooter = styled(CardFooter)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
const StyledFooterContainer = styled.div`
|
||||
> * {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled.button`
|
||||
@@ -91,12 +93,14 @@ export const SettingsListCard = <
|
||||
/>
|
||||
))}
|
||||
{hasFooter && (
|
||||
<StyledFooter divider={!!items.length}>
|
||||
<StyledButton onClick={onFooterButtonClick}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
{footerButtonLabel}
|
||||
</StyledButton>
|
||||
</StyledFooter>
|
||||
<StyledFooterContainer>
|
||||
<CardFooter divider={!!items.length}>
|
||||
<StyledButton onClick={onFooterButtonClick}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
{footerButtonLabel}
|
||||
</StyledButton>
|
||||
</CardFooter>
|
||||
</StyledFooterContainer>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
|
||||
+48
-36
@@ -6,15 +6,17 @@ import { IconChevronRight, type IconComponent } from 'twenty-ui/display';
|
||||
import { CardContent } from 'twenty-ui/layout';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRow = styled(CardContent)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
const StyledRowContainer = styled.div`
|
||||
> * {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.medium};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
padding-left: ${themeCssVariables.spacing[3]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRightContainer = styled.div`
|
||||
@@ -43,9 +45,11 @@ const StyledDescription = styled.span`
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
text-decoration: none;
|
||||
const StyledLinkContainer = styled.div`
|
||||
> a {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
|
||||
type SettingsListItemCardContentProps = {
|
||||
@@ -72,36 +76,44 @@ export const SettingsListItemCardContent = ({
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const content = (
|
||||
<StyledRow
|
||||
onClick={onClick}
|
||||
divider={divider}
|
||||
isClickable={!!onClick || !!to}
|
||||
hasHoverHighlight={!!to}
|
||||
>
|
||||
{!!LeftIcon && (
|
||||
<LeftIcon
|
||||
size={theme.icon.size.md}
|
||||
color={LeftIconColor ?? 'currentColor'}
|
||||
/>
|
||||
)}
|
||||
<StyledContent>
|
||||
<StyledLabel>{label}</StyledLabel>
|
||||
{!!description && <StyledDescription>{description}</StyledDescription>}
|
||||
</StyledContent>
|
||||
<StyledRightContainer>
|
||||
{rightComponent}
|
||||
{!!to && (
|
||||
<IconChevronRight
|
||||
<StyledRowContainer>
|
||||
<CardContent
|
||||
onClick={onClick}
|
||||
divider={divider}
|
||||
isClickable={!!onClick || !!to}
|
||||
hasHoverHighlight={!!to}
|
||||
>
|
||||
{!!LeftIcon && (
|
||||
<LeftIcon
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
color={LeftIconColor ?? 'currentColor'}
|
||||
/>
|
||||
)}
|
||||
</StyledRightContainer>
|
||||
</StyledRow>
|
||||
<StyledContent>
|
||||
<StyledLabel>{label}</StyledLabel>
|
||||
{!!description && (
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
)}
|
||||
</StyledContent>
|
||||
<StyledRightContainer>
|
||||
{rightComponent}
|
||||
{!!to && (
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
)}
|
||||
</StyledRightContainer>
|
||||
</CardContent>
|
||||
</StyledRowContainer>
|
||||
);
|
||||
|
||||
if (isDefined(to)) {
|
||||
return <StyledLink to={to}>{content}</StyledLink>;
|
||||
return (
|
||||
<StyledLinkContainer>
|
||||
<Link to={to}>{content}</Link>
|
||||
</StyledLinkContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return content;
|
||||
|
||||
@@ -2,9 +2,17 @@ import { styled } from '@linaria/react';
|
||||
import { Card } from 'twenty-ui/layout';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
const StyledCardContainer = styled.div`
|
||||
height: 40px;
|
||||
|
||||
> * {
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledCard as SettingsListSkeletonCard };
|
||||
export const SettingsListSkeletonCard = () => (
|
||||
<StyledCardContainer>
|
||||
<Card />
|
||||
</StyledCardContainer>
|
||||
);
|
||||
|
||||
+20
-12
@@ -1,6 +1,5 @@
|
||||
import { Separator } from '@/settings/components/Separator';
|
||||
import {
|
||||
StyledSettingsCardContent,
|
||||
StyledSettingsCardDescription,
|
||||
StyledSettingsCardIcon,
|
||||
StyledSettingsCardTextContainer,
|
||||
@@ -16,7 +15,12 @@ import {
|
||||
import { Toggle } from 'twenty-ui/input';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledSettingsCardToggleContent = styled(StyledSettingsCardContent)`
|
||||
const StyledSettingsCardToggleContent = styled.div<{ disabled?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[3]};
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding: ${themeCssVariables.spacing[4]};
|
||||
cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')};
|
||||
position: relative;
|
||||
pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};
|
||||
@@ -26,7 +30,9 @@ const StyledSettingsCardToggleContent = styled(StyledSettingsCardContent)`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledSettingsCardToggleButton = styled(Toggle)`
|
||||
const StyledSettingsCardToggleButtonContainer = styled.span`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
`;
|
||||
@@ -84,15 +90,17 @@ export const SettingsOptionCardContentToggle = ({
|
||||
</StyledSettingsCardDescription>
|
||||
)}
|
||||
</StyledSettingsCardTextContainer>
|
||||
<StyledSettingsCardToggleButton
|
||||
id={toggleId}
|
||||
value={checked}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
toggleSize="small"
|
||||
color={advancedMode ? theme.color.yellow : theme.color.blue}
|
||||
centered={toggleCentered}
|
||||
/>
|
||||
<StyledSettingsCardToggleButtonContainer>
|
||||
<Toggle
|
||||
id={toggleId}
|
||||
value={checked}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
toggleSize="small"
|
||||
color={advancedMode ? theme.color.yellow : theme.color.blue}
|
||||
centered={toggleCentered}
|
||||
/>
|
||||
</StyledSettingsCardToggleButtonContainer>
|
||||
</StyledSettingsCardToggleContent>
|
||||
{divider && <Separator />}
|
||||
</>
|
||||
|
||||
@@ -5,22 +5,26 @@ import { type IconComponent } from 'twenty-ui/display';
|
||||
import { Radio } from 'twenty-ui/input';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledRadioCardContent = styled(CardContent)`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
flex-grow: 1;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
cursor: pointer;
|
||||
const StyledRadioCardContentContainer = styled.div`
|
||||
> * {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.sm};
|
||||
flex-grow: 1;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
&:hover {
|
||||
background: ${themeCssVariables.background.transparent.lighter};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRadio = styled(Radio)`
|
||||
const StyledRadioContainer = styled.span`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
@@ -58,13 +62,17 @@ export const SettingsRadioCard = ({
|
||||
const onClick = () => handleSelect(value);
|
||||
|
||||
return (
|
||||
<StyledRadioCardContent tabIndex={0} onClick={onClick}>
|
||||
{Icon && <Icon size={theme.icon.size.xl} color={theme.color.gray10} />}
|
||||
<span>
|
||||
{title && <StyledTitle>{title}</StyledTitle>}
|
||||
{description && <StyledDescription>{description}</StyledDescription>}
|
||||
</span>
|
||||
<StyledRadio value={value} checked={isSelected} />
|
||||
</StyledRadioCardContent>
|
||||
<StyledRadioCardContentContainer>
|
||||
<CardContent tabIndex={0} onClick={onClick}>
|
||||
{Icon && <Icon size={theme.icon.size.xl} color={theme.color.gray10} />}
|
||||
<span>
|
||||
{title && <StyledTitle>{title}</StyledTitle>}
|
||||
{description && <StyledDescription>{description}</StyledDescription>}
|
||||
</span>
|
||||
<StyledRadioContainer>
|
||||
<Radio value={value} checked={isSelected} />
|
||||
</StyledRadioContainer>
|
||||
</CardContent>
|
||||
</StyledRadioCardContentContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,12 +8,14 @@ type SettingsSummaryCardProps = {
|
||||
rightComponent: ReactNode;
|
||||
};
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
min-height: ${themeCssVariables.spacing[6]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
@@ -29,9 +31,11 @@ export const SettingsSummaryCard = ({
|
||||
rightComponent,
|
||||
}: SettingsSummaryCardProps) => (
|
||||
<Card>
|
||||
<StyledCardContent>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
{rightComponent}
|
||||
</StyledCardContent>
|
||||
<StyledCardContentContainer>
|
||||
<CardContent>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
{rightComponent}
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
</Card>
|
||||
);
|
||||
|
||||
+17
-7
@@ -28,8 +28,10 @@ const StyledButtonContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDownChevron = styled(IconChevronDown)`
|
||||
const StyledDownChevronContainer = styled.span`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: ${themeCssVariables.spacing['1.5']};
|
||||
top: 50%;
|
||||
@@ -45,9 +47,11 @@ const StyledSpan = styled.span`
|
||||
margin-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
padding-right: ${themeCssVariables.spacing[6]};
|
||||
const StyledButtonWrapper = styled.div`
|
||||
> button {
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
padding-right: ${themeCssVariables.spacing[6]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
|
||||
@@ -87,11 +91,17 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<StyledButtonContainer>
|
||||
<StyledDownChevron size={theme.icon.size.md} />
|
||||
<StyledDownChevronContainer>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledDownChevronContainer>
|
||||
{isConfigureStep ? (
|
||||
<StyledButton variant="tertiary" title={t`2. Configure`} />
|
||||
<StyledButtonWrapper>
|
||||
<Button variant="tertiary" title={t`2. Configure`} />
|
||||
</StyledButtonWrapper>
|
||||
) : (
|
||||
<StyledButton variant="tertiary" title={t`1. Type`} />
|
||||
<StyledButtonWrapper>
|
||||
<Button variant="tertiary" title={t`1. Type`} />
|
||||
</StyledButtonWrapper>
|
||||
)}
|
||||
</StyledButtonContainer>
|
||||
}
|
||||
|
||||
+21
-11
@@ -13,12 +13,16 @@ type SettingsDataModelPreviewFormCardProps = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const StyledPreviewContainer = styled(CardContent)`
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
const StyledPreviewContainerWrapper = styled.div`
|
||||
> * {
|
||||
background-color: ${themeCssVariables.background.transparent.lighter};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledFormContainer = styled(CardContent)`
|
||||
padding: 0;
|
||||
const StyledFormContainerWrapper = styled.div`
|
||||
> * {
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsDataModelPreviewFormCard = ({
|
||||
@@ -27,12 +31,18 @@ export const SettingsDataModelPreviewFormCard = ({
|
||||
form,
|
||||
}: SettingsDataModelPreviewFormCardProps) => (
|
||||
<Card className={className} fullWidth rounded>
|
||||
<StyledPreviewContainer divider={!!form}>
|
||||
<StyledFormCardTitle>
|
||||
<Trans>Preview</Trans>
|
||||
</StyledFormCardTitle>
|
||||
{preview}
|
||||
</StyledPreviewContainer>
|
||||
{!!form && <StyledFormContainer>{form}</StyledFormContainer>}
|
||||
<StyledPreviewContainerWrapper>
|
||||
<CardContent divider={!!form}>
|
||||
<StyledFormCardTitle>
|
||||
<Trans>Preview</Trans>
|
||||
</StyledFormCardTitle>
|
||||
{preview}
|
||||
</CardContent>
|
||||
</StyledPreviewContainerWrapper>
|
||||
{!!form && (
|
||||
<StyledFormContainerWrapper>
|
||||
<CardContent>{form}</CardContent>
|
||||
</StyledFormContainerWrapper>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
|
||||
+10
-8
@@ -56,7 +56,7 @@ const StyledCardContainer = styled.div`
|
||||
width: calc(50% - ${themeCssVariables.spacing[1]});
|
||||
`;
|
||||
|
||||
const StyledSearchInput = styled(SettingsTextInput)`
|
||||
const StyledSearchInputContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
@@ -108,13 +108,15 @@ export const SettingsObjectNewFieldSelector = ({
|
||||
<>
|
||||
{' '}
|
||||
<Section>
|
||||
<StyledSearchInput
|
||||
instanceId="new-field-type-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a type`}
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
<StyledSearchInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId="new-field-type-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a type`}
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
</StyledSearchInputContainer>
|
||||
</Section>
|
||||
<Controller
|
||||
name="type"
|
||||
|
||||
+11
-9
@@ -33,7 +33,7 @@ export const settingsDataModelFieldDateFormSchema = z.object({
|
||||
settings: fieldDateSettings.optional(),
|
||||
});
|
||||
|
||||
const StyledTextInput = styled(SettingsTextInput)`
|
||||
const StyledTextInputContainer = styled.div`
|
||||
padding: ${themeCssVariables.spacing[4]};
|
||||
padding-top: 0;
|
||||
`;
|
||||
@@ -117,14 +117,16 @@ export const SettingsDataModelFieldDateForm = ({
|
||||
control={control}
|
||||
defaultValue={initialCustomUnicodeDateFormat}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<StyledTextInput
|
||||
instanceId="custom-date-format-input"
|
||||
placeholder={t`Format e.g. d-MMM-y (qqq''yy)`}
|
||||
value={value}
|
||||
onChange={(value) => onChange(value)}
|
||||
disabled={false}
|
||||
fullWidth
|
||||
/>
|
||||
<StyledTextInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId="custom-date-format-input"
|
||||
placeholder={t`Format e.g. d-MMM-y (qqq''yy)`}
|
||||
value={value}
|
||||
onChange={(value) => onChange(value)}
|
||||
disabled={false}
|
||||
fullWidth
|
||||
/>
|
||||
</StyledTextInputContainer>
|
||||
)}
|
||||
/>
|
||||
</AnimatedExpandableContainer>
|
||||
|
||||
+220
-199
@@ -67,8 +67,10 @@ type SettingsDataModelFieldSelectFormProps = {
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const StyledContainer = styled(CardContent)`
|
||||
padding-bottom: 14px;
|
||||
const StyledContainerWrapper = styled.div`
|
||||
> * {
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledOptionsLabel = styled.div<{
|
||||
@@ -119,18 +121,24 @@ const StyledIconContainer = styled.div`
|
||||
margin-top: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledIconPoint = styled(IconPoint)`
|
||||
const StyledIconPointContainer = styled.span`
|
||||
margin-right: ${themeCssVariables.spacing['0.5']};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledFooter = styled(CardFooter)`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
const StyledFooterContainer = styled.div`
|
||||
> * {
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding: ${themeCssVariables.spacing[1]};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled(LightButton)`
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
const StyledButtonContainer = styled.div`
|
||||
> button {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledOptionsHeaderContainer = styled.div`
|
||||
@@ -316,199 +324,212 @@ export const SettingsDataModelFieldSelectForm = ({
|
||||
defaultValue={initialOptions}
|
||||
render={({ field: { onChange, value: options } }) => (
|
||||
<>
|
||||
<StyledContainer>
|
||||
<StyledOptionsHeaderContainer>
|
||||
<StyledLabelContainer>
|
||||
{!isBulkInputMode && (
|
||||
<AdvancedSettingsWrapper animationDimension="width" hideDot>
|
||||
<StyledApiKeyContainer>
|
||||
<StyledIconContainer>
|
||||
<StyledIconPoint
|
||||
size={12}
|
||||
color={theme.color.yellow}
|
||||
fill={theme.color.yellow}
|
||||
/>
|
||||
</StyledIconContainer>
|
||||
<StyledApiKey>{t`API values`}</StyledApiKey>
|
||||
</StyledApiKeyContainer>
|
||||
</AdvancedSettingsWrapper>
|
||||
)}
|
||||
<StyledOptionsLabel
|
||||
isAdvancedModeEnabled={isAdvancedModeEnabled}
|
||||
isBulkInputMode={isBulkInputMode}
|
||||
>
|
||||
{t`Options`}
|
||||
</StyledOptionsLabel>
|
||||
</StyledLabelContainer>
|
||||
{!disabled && (
|
||||
<Dropdown
|
||||
dropdownId={OPTIONS_DROPDOWN_ID}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent
|
||||
widthInPixels={GenericDropdownContentWidth.Narrow}
|
||||
<StyledContainerWrapper>
|
||||
<CardContent>
|
||||
<StyledOptionsHeaderContainer>
|
||||
<StyledLabelContainer>
|
||||
{!isBulkInputMode && (
|
||||
<AdvancedSettingsWrapper
|
||||
animationDimension="width"
|
||||
hideDot
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text={
|
||||
isBulkInputMode ? t`Single edit` : t`Bulk edit`
|
||||
}
|
||||
LeftIcon={IconPencil}
|
||||
onClick={() => {
|
||||
if (!isBulkInputMode) {
|
||||
setBulkInputText(
|
||||
convertOptionsToBulkText(options),
|
||||
);
|
||||
}
|
||||
setIsBulkInputMode(
|
||||
(currentInputMode) => !currentInputMode,
|
||||
);
|
||||
closeOptionsDropdown(OPTIONS_DROPDOWN_ID);
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
text={t`Remove all`}
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
onClick={() => {
|
||||
onChange([]);
|
||||
closeOptionsDropdown(OPTIONS_DROPDOWN_ID);
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledOptionsHeaderContainer>
|
||||
|
||||
{isBulkInputMode ? (
|
||||
<StyledTextAreaContainer>
|
||||
<TextArea
|
||||
textAreaId="bulk-options-input"
|
||||
placeholder={t`Enter one option per line`}
|
||||
value={bulkInputText}
|
||||
onChange={(nextOptionAsText) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextOptions = convertBulkTextToOptions(
|
||||
nextOptionAsText,
|
||||
options,
|
||||
);
|
||||
|
||||
onChange(nextOptions);
|
||||
setBulkInputText(nextOptionAsText);
|
||||
}}
|
||||
minRows={5}
|
||||
maxRows={15}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<StyledHelpText>
|
||||
{t`Enter one option per line. Each line will become a new option.`}
|
||||
</StyledHelpText>
|
||||
</StyledTextAreaContainer>
|
||||
) : (
|
||||
<>
|
||||
<DraggableList
|
||||
onDragEnd={(result) =>
|
||||
!disabled
|
||||
? handleDragEnd(options, result, onChange)
|
||||
: undefined
|
||||
}
|
||||
draggableItems={
|
||||
<>
|
||||
{options.map((option, index) => (
|
||||
<DraggableItem
|
||||
isInsideScrollableContainer
|
||||
key={option.id}
|
||||
draggableId={option.id}
|
||||
index={index}
|
||||
isDragDisabled={options.length === 1}
|
||||
itemComponent={
|
||||
<SettingsDataModelFieldSelectFormOptionRow
|
||||
key={option.id}
|
||||
option={option}
|
||||
isNewRow={index === options.length - 1}
|
||||
onChange={(nextOption) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
const nextOptions = toSpliced(
|
||||
options,
|
||||
index,
|
||||
1,
|
||||
nextOption,
|
||||
);
|
||||
onChange(nextOptions);
|
||||
|
||||
// Update option value in defaultValue if value has changed
|
||||
if (
|
||||
nextOption.value !== option.value &&
|
||||
isOptionDefaultValue(option.value)
|
||||
) {
|
||||
handleRemoveOptionAsDefault(option.value);
|
||||
handleSetOptionAsDefault(nextOption.value);
|
||||
}
|
||||
}}
|
||||
onRemove={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
const nextOptions = toSpliced(
|
||||
options,
|
||||
index,
|
||||
1,
|
||||
).map((option, nextOptionIndex) => ({
|
||||
...option,
|
||||
position: nextOptionIndex,
|
||||
}));
|
||||
onChange(nextOptions);
|
||||
}}
|
||||
isDefault={isOptionDefaultValue(option.value)}
|
||||
fieldIsNullable={!!isNullable}
|
||||
onSetAsDefault={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleSetOptionAsDefault(option.value);
|
||||
}}
|
||||
onRemoveAsDefault={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleRemoveOptionAsDefault(option.value);
|
||||
}}
|
||||
onInputEnter={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleInputEnter();
|
||||
}}
|
||||
<StyledApiKeyContainer>
|
||||
<StyledIconContainer>
|
||||
<StyledIconPointContainer>
|
||||
<IconPoint
|
||||
size={12}
|
||||
color={theme.color.yellow}
|
||||
fill={theme.color.yellow}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</StyledContainer>
|
||||
</StyledIconPointContainer>
|
||||
</StyledIconContainer>
|
||||
<StyledApiKey>{t`API values`}</StyledApiKey>
|
||||
</StyledApiKeyContainer>
|
||||
</AdvancedSettingsWrapper>
|
||||
)}
|
||||
<StyledOptionsLabel
|
||||
isAdvancedModeEnabled={isAdvancedModeEnabled}
|
||||
isBulkInputMode={isBulkInputMode}
|
||||
>
|
||||
{t`Options`}
|
||||
</StyledOptionsLabel>
|
||||
</StyledLabelContainer>
|
||||
{!disabled && (
|
||||
<Dropdown
|
||||
dropdownId={OPTIONS_DROPDOWN_ID}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent
|
||||
widthInPixels={GenericDropdownContentWidth.Narrow}
|
||||
>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text={
|
||||
isBulkInputMode ? t`Single edit` : t`Bulk edit`
|
||||
}
|
||||
LeftIcon={IconPencil}
|
||||
onClick={() => {
|
||||
if (!isBulkInputMode) {
|
||||
setBulkInputText(
|
||||
convertOptionsToBulkText(options),
|
||||
);
|
||||
}
|
||||
setIsBulkInputMode(
|
||||
(currentInputMode) => !currentInputMode,
|
||||
);
|
||||
closeOptionsDropdown(OPTIONS_DROPDOWN_ID);
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
text={t`Remove all`}
|
||||
accent="danger"
|
||||
LeftIcon={IconTrash}
|
||||
onClick={() => {
|
||||
onChange([]);
|
||||
closeOptionsDropdown(OPTIONS_DROPDOWN_ID);
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</DropdownContent>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</StyledOptionsHeaderContainer>
|
||||
|
||||
{isBulkInputMode ? (
|
||||
<StyledTextAreaContainer>
|
||||
<TextArea
|
||||
textAreaId="bulk-options-input"
|
||||
placeholder={t`Enter one option per line`}
|
||||
value={bulkInputText}
|
||||
onChange={(nextOptionAsText) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextOptions = convertBulkTextToOptions(
|
||||
nextOptionAsText,
|
||||
options,
|
||||
);
|
||||
|
||||
onChange(nextOptions);
|
||||
setBulkInputText(nextOptionAsText);
|
||||
}}
|
||||
minRows={5}
|
||||
maxRows={15}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<StyledHelpText>
|
||||
{t`Enter one option per line. Each line will become a new option.`}
|
||||
</StyledHelpText>
|
||||
</StyledTextAreaContainer>
|
||||
) : (
|
||||
<>
|
||||
<DraggableList
|
||||
onDragEnd={(result) =>
|
||||
!disabled
|
||||
? handleDragEnd(options, result, onChange)
|
||||
: undefined
|
||||
}
|
||||
draggableItems={
|
||||
<>
|
||||
{options.map((option, index) => (
|
||||
<DraggableItem
|
||||
isInsideScrollableContainer
|
||||
key={option.id}
|
||||
draggableId={option.id}
|
||||
index={index}
|
||||
isDragDisabled={options.length === 1}
|
||||
itemComponent={
|
||||
<SettingsDataModelFieldSelectFormOptionRow
|
||||
key={option.id}
|
||||
option={option}
|
||||
isNewRow={index === options.length - 1}
|
||||
onChange={(nextOption) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
const nextOptions = toSpliced(
|
||||
options,
|
||||
index,
|
||||
1,
|
||||
nextOption,
|
||||
);
|
||||
onChange(nextOptions);
|
||||
|
||||
// Update option value in defaultValue if value has changed
|
||||
if (
|
||||
nextOption.value !== option.value &&
|
||||
isOptionDefaultValue(option.value)
|
||||
) {
|
||||
handleRemoveOptionAsDefault(option.value);
|
||||
handleSetOptionAsDefault(
|
||||
nextOption.value,
|
||||
);
|
||||
}
|
||||
}}
|
||||
onRemove={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
const nextOptions = toSpliced(
|
||||
options,
|
||||
index,
|
||||
1,
|
||||
).map((option, nextOptionIndex) => ({
|
||||
...option,
|
||||
position: nextOptionIndex,
|
||||
}));
|
||||
onChange(nextOptions);
|
||||
}}
|
||||
isDefault={isOptionDefaultValue(option.value)}
|
||||
fieldIsNullable={!!isNullable}
|
||||
onSetAsDefault={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleSetOptionAsDefault(option.value);
|
||||
}}
|
||||
onRemoveAsDefault={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleRemoveOptionAsDefault(option.value);
|
||||
}}
|
||||
onInputEnter={() => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
handleInputEnter();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</StyledContainerWrapper>
|
||||
{!disabled && !isBulkInputMode && (
|
||||
<StyledFooter>
|
||||
<StyledButton
|
||||
title={t`Add option`}
|
||||
Icon={IconPlus}
|
||||
onClick={handleAddOption}
|
||||
/>
|
||||
</StyledFooter>
|
||||
<StyledFooterContainer>
|
||||
<CardFooter>
|
||||
<StyledButtonContainer>
|
||||
<LightButton
|
||||
title={t`Add option`}
|
||||
Icon={IconPlus}
|
||||
onClick={handleAddOption}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
</CardFooter>
|
||||
</StyledFooterContainer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
+31
-18
@@ -75,13 +75,14 @@ const StyledRow = styled.div`
|
||||
padding: ${themeCssVariables.spacing['1.5']} 0;
|
||||
`;
|
||||
|
||||
const StyledColorSample = styled(ColorSample)`
|
||||
const StyledColorSampleContainer = styled.span`
|
||||
cursor: pointer;
|
||||
margin-top: ${themeCssVariables.spacing[1]};
|
||||
margin-bottom: ${themeCssVariables.spacing[1]};
|
||||
|
||||
margin-right: 14px;
|
||||
margin-left: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledOptionInputContainer = styled.div`
|
||||
@@ -93,12 +94,16 @@ const StyledOptionInputContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIconGripVertical = styled(IconGripVertical)`
|
||||
const StyledIconGripVerticalContainer = styled.span`
|
||||
margin-right: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledLightIconButton = styled(LightIconButton)`
|
||||
const StyledLightIconButtonContainer = styled.span`
|
||||
margin-left: ${themeCssVariables.spacing[2]};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
@@ -129,14 +134,16 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
|
||||
return (
|
||||
<StyledRow className={className}>
|
||||
<StyledIconGripVertical
|
||||
style={{
|
||||
minWidth: theme.icon.size.md,
|
||||
}}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
color={theme.font.color.extraLight}
|
||||
/>
|
||||
<StyledIconGripVerticalContainer>
|
||||
<IconGripVertical
|
||||
style={{
|
||||
minWidth: theme.icon.size.md,
|
||||
}}
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
color={theme.font.color.extraLight}
|
||||
/>
|
||||
</StyledIconGripVerticalContainer>
|
||||
<AdvancedSettingsWrapper animationDimension="width" hideDot>
|
||||
<StyledOptionInputContainer>
|
||||
<SettingsTextInput
|
||||
@@ -156,7 +163,11 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
<Dropdown
|
||||
dropdownId={SELECT_COLOR_DROPDOWN_ID}
|
||||
dropdownPlacement="bottom-start"
|
||||
clickableComponent={<StyledColorSample colorName={option.color} />}
|
||||
clickableComponent={
|
||||
<StyledColorSampleContainer>
|
||||
<ColorSample colorName={option.color} />
|
||||
</StyledColorSampleContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent>
|
||||
<DropdownMenuItemsContainer>
|
||||
@@ -203,11 +214,13 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
dropdownId={SELECT_ACTIONS_DROPDOWN_ID}
|
||||
dropdownPlacement="right-start"
|
||||
clickableComponent={
|
||||
<StyledLightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
disabled={shouldForbidRemoveAsDefault}
|
||||
/>
|
||||
<StyledLightIconButtonContainer>
|
||||
<LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
disabled={shouldForbidRemoveAsDefault}
|
||||
/>
|
||||
</StyledLightIconButtonContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
shouldForbidRemoveAsDefault ? null : (
|
||||
|
||||
+35
-27
@@ -21,13 +21,17 @@ type SettingsDataModelFieldPreviewWidgetProps = {
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
const StyledCardContainer = styled.div`
|
||||
> * {
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsDataModelFieldPreviewWidget = ({
|
||||
@@ -43,27 +47,31 @@ export const SettingsDataModelFieldPreviewWidget = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledCard className={className} fullWidth>
|
||||
<StyledCardContent>
|
||||
<SettingsDataModelObjectPreview
|
||||
objectMetadataItems={[objectMetadataItem]}
|
||||
pluralizeLabel={pluralizeLabel}
|
||||
/>
|
||||
<SettingsDataModelFieldPreview
|
||||
objectNameSingular={objectNameSingular}
|
||||
fieldMetadataItem={{
|
||||
label: fieldMetadataItem.label,
|
||||
icon: fieldMetadataItem.icon,
|
||||
defaultValue: fieldMetadataItem.defaultValue,
|
||||
options: fieldMetadataItem.options,
|
||||
settings: fieldMetadataItem.settings,
|
||||
type: fieldMetadataItem.type,
|
||||
name: computeMetadataNameFromLabel(fieldMetadataItem.label),
|
||||
}}
|
||||
shrink={shrink}
|
||||
withFieldLabel={withFieldLabel}
|
||||
/>
|
||||
</StyledCardContent>
|
||||
</StyledCard>
|
||||
<StyledCardContainer className={className}>
|
||||
<Card fullWidth>
|
||||
<StyledCardContentContainer>
|
||||
<CardContent>
|
||||
<SettingsDataModelObjectPreview
|
||||
objectMetadataItems={[objectMetadataItem]}
|
||||
pluralizeLabel={pluralizeLabel}
|
||||
/>
|
||||
<SettingsDataModelFieldPreview
|
||||
objectNameSingular={objectNameSingular}
|
||||
fieldMetadataItem={{
|
||||
label: fieldMetadataItem.label,
|
||||
icon: fieldMetadataItem.icon,
|
||||
defaultValue: fieldMetadataItem.defaultValue,
|
||||
options: fieldMetadataItem.options,
|
||||
settings: fieldMetadataItem.settings,
|
||||
type: fieldMetadataItem.type,
|
||||
name: computeMetadataNameFromLabel(fieldMetadataItem.label),
|
||||
}}
|
||||
shrink={shrink}
|
||||
withFieldLabel={withFieldLabel}
|
||||
/>
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
</Card>
|
||||
</StyledCardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+30
-21
@@ -21,14 +21,19 @@ export type SettingsDataModelRelationFieldPreviewSubWidgetProps = {
|
||||
pluralizeLabel?: boolean;
|
||||
};
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
const StyledCardContainer = styled.div`
|
||||
margin: auto;
|
||||
|
||||
> * {
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
const StyledCardContentContainer = styled.div`
|
||||
> * {
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsDataModelRelationFieldPreviewSubWidget = ({
|
||||
@@ -49,21 +54,25 @@ export const SettingsDataModelRelationFieldPreviewSubWidget = ({
|
||||
.filter(isDefined);
|
||||
|
||||
return (
|
||||
<StyledCard className={className} fullWidth>
|
||||
<StyledCardContent>
|
||||
<SettingsDataModelObjectPreview
|
||||
objectMetadataItems={targetObjectMetadataItems}
|
||||
pluralizeLabel={pluralizeLabel}
|
||||
/>
|
||||
<SettingsDataModelRelationFieldPreview
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
relationTargetObjectNameSingular={
|
||||
fieldPreviewTargetObjectNameSingular
|
||||
}
|
||||
shrink={shrink}
|
||||
withFieldLabel={withFieldLabel}
|
||||
/>
|
||||
</StyledCardContent>
|
||||
</StyledCard>
|
||||
<StyledCardContainer className={className}>
|
||||
<Card fullWidth>
|
||||
<StyledCardContentContainer>
|
||||
<CardContent>
|
||||
<SettingsDataModelObjectPreview
|
||||
objectMetadataItems={targetObjectMetadataItems}
|
||||
pluralizeLabel={pluralizeLabel}
|
||||
/>
|
||||
<SettingsDataModelRelationFieldPreview
|
||||
fieldMetadataItem={fieldMetadataItem}
|
||||
relationTargetObjectNameSingular={
|
||||
fieldPreviewTargetObjectNameSingular
|
||||
}
|
||||
shrink={shrink}
|
||||
withFieldLabel={withFieldLabel}
|
||||
/>
|
||||
</CardContent>
|
||||
</StyledCardContentContainer>
|
||||
</Card>
|
||||
</StyledCardContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+20
-16
@@ -86,15 +86,17 @@ const StyledObjectInstanceCount = styled.div`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledObjectLink = styled(Link)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
text-decoration: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
const StyledObjectLinkContainer = styled.div`
|
||||
> a {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${themeCssVariables.spacing[1]};
|
||||
text-decoration: none;
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
|
||||
&:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
&:hover {
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -123,14 +125,16 @@ export const SettingsDataModelOverviewObject = ({
|
||||
<StyledNode>
|
||||
<StyledHeader>
|
||||
<StyledObjectName onMouseEnter={() => {}} onMouseLeave={() => {}}>
|
||||
<StyledObjectLink
|
||||
to={getSettingsPath(SettingsPath.Objects, {
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
})}
|
||||
>
|
||||
{Icon && <Icon size={theme.icon.size.md} />}
|
||||
{objectMetadataItem.labelPlural}
|
||||
</StyledObjectLink>
|
||||
<StyledObjectLinkContainer>
|
||||
<Link
|
||||
to={getSettingsPath(SettingsPath.Objects, {
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
})}
|
||||
>
|
||||
{Icon && <Icon size={theme.icon.size.md} />}
|
||||
{objectMetadataItem.labelPlural}
|
||||
</Link>
|
||||
</StyledObjectLinkContainer>
|
||||
<StyledObjectInstanceCount> · {totalCount}</StyledObjectInstanceCount>
|
||||
</StyledObjectName>
|
||||
<SettingsItemTypeTag item={objectMetadataItem} />
|
||||
|
||||
+17
-23
@@ -1,34 +1,21 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { Checkbox } from 'twenty-ui/input';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
export const AVAILABLE_STANDARD_OBJECTS_GRID_TEMPLATE_COLUMNS =
|
||||
'28px 148px 256px 80px';
|
||||
|
||||
type SettingsAvailableStandardObjectItemTableRowProps = {
|
||||
isSelected?: boolean;
|
||||
objectItem: ObjectMetadataItem;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const StyledAvailableStandardObjectTableRow = styled(TableRow)`
|
||||
grid-template-columns: 28px 148px 256px 80px;
|
||||
`;
|
||||
|
||||
const StyledCheckboxTableCell = styled(TableCell)`
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
padding-left: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledNameTableCell = styled(TableCell)`
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -45,22 +32,29 @@ export const SettingsAvailableStandardObjectItemTableRow = ({
|
||||
const Icon = getIcon(objectItem.icon);
|
||||
|
||||
return (
|
||||
<StyledAvailableStandardObjectTableRow
|
||||
<TableRow
|
||||
gridTemplateColumns={AVAILABLE_STANDARD_OBJECTS_GRID_TEMPLATE_COLUMNS}
|
||||
key={objectItem.namePlural}
|
||||
isSelected={isSelected}
|
||||
onClick={onClick}
|
||||
>
|
||||
<StyledCheckboxTableCell>
|
||||
<TableCell
|
||||
align="center"
|
||||
padding={`0 0 0 ${themeCssVariables.spacing[1]}`}
|
||||
>
|
||||
<Checkbox checked={!!isSelected} />
|
||||
</StyledCheckboxTableCell>
|
||||
<StyledNameTableCell>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
color={themeCssVariables.font.color.primary}
|
||||
gap={themeCssVariables.spacing[2]}
|
||||
>
|
||||
{!!Icon && <Icon size={theme.icon.size.md} />}
|
||||
{objectItem.labelPlural}
|
||||
</StyledNameTableCell>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StyledDescription>{objectItem.description}</StyledDescription>
|
||||
</TableCell>
|
||||
<TableCell align="right">{objectItem.fields.length}</TableCell>
|
||||
</StyledAvailableStandardObjectTableRow>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-3
@@ -6,9 +6,10 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import {
|
||||
AVAILABLE_STANDARD_OBJECTS_GRID_TEMPLATE_COLUMNS,
|
||||
SettingsAvailableStandardObjectItemTableRow,
|
||||
StyledAvailableStandardObjectTableRow,
|
||||
} from './SettingsAvailableStandardObjectItemTableRow';
|
||||
|
||||
type SettingsAvailableStandardObjectsSectionProps = {
|
||||
@@ -28,12 +29,14 @@ export const SettingsAvailableStandardObjectsSection = ({
|
||||
description={t`Select one or several standard objects to activate below`}
|
||||
/>
|
||||
<Table>
|
||||
<StyledAvailableStandardObjectTableRow>
|
||||
<TableRow
|
||||
gridTemplateColumns={AVAILABLE_STANDARD_OBJECTS_GRID_TEMPLATE_COLUMNS}
|
||||
>
|
||||
<TableHeader></TableHeader>
|
||||
<TableHeader>{t`Name`}</TableHeader>
|
||||
<TableHeader>{t`Description`}</TableHeader>
|
||||
<TableHeader align="right">{t`Fields`}</TableHeader>
|
||||
</StyledAvailableStandardObjectTableRow>
|
||||
</TableRow>
|
||||
<TableBody>
|
||||
{objectItems.map((objectItem) => (
|
||||
<SettingsAvailableStandardObjectItemTableRow
|
||||
|
||||
+24
-24
@@ -36,14 +36,8 @@ type SettingsObjectFieldItemTableRowProps = {
|
||||
mode: 'view' | 'new-field';
|
||||
};
|
||||
|
||||
export const StyledObjectFieldTableRow = styled(TableRow)`
|
||||
grid-template-columns: minmax(0, 1fr) 148px 148px 36px;
|
||||
`;
|
||||
|
||||
const StyledNameTableCell = styled(TableCell)`
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
export const OBJECT_FIELD_TABLE_ROW_GRID_TEMPLATE_COLUMNS =
|
||||
'minmax(0, 1fr) 148px 148px 36px';
|
||||
|
||||
const StyledNameContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -74,13 +68,10 @@ const StyledInactiveLabel = styled.span`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIconTableCell = styled(TableCell)`
|
||||
justify-content: center;
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
`;
|
||||
|
||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
const StyledIconChevronRightContainer = styled.span`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SettingsObjectFieldItemTableRow = ({
|
||||
@@ -177,11 +168,15 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
: relationObjectMetadataItem?.labelPlural;
|
||||
|
||||
return (
|
||||
<StyledObjectFieldTableRow
|
||||
<TableRow
|
||||
gridTemplateColumns={OBJECT_FIELD_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
|
||||
onClick={mode === 'view' ? navigateToFieldEdit : undefined}
|
||||
>
|
||||
<UndecoratedLink to={linkToNavigate}>
|
||||
<StyledNameTableCell>
|
||||
<TableCell
|
||||
color={themeCssVariables.font.color.primary}
|
||||
gap={themeCssVariables.spacing[2]}
|
||||
>
|
||||
{!!Icon && (
|
||||
<Icon
|
||||
style={{
|
||||
@@ -199,7 +194,7 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
<StyledInactiveLabel>{t`Deactivated`}</StyledInactiveLabel>
|
||||
)}
|
||||
</StyledNameContainer>
|
||||
</StyledNameTableCell>
|
||||
</TableCell>
|
||||
</UndecoratedLink>
|
||||
|
||||
<TableCell>
|
||||
@@ -232,14 +227,19 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<StyledIconTableCell>
|
||||
<TableCell
|
||||
align="center"
|
||||
padding={`0 ${themeCssVariables.spacing[1]} 0 ${themeCssVariables.spacing[2]}`}
|
||||
>
|
||||
{status === 'active' ? (
|
||||
mode === 'view' ? (
|
||||
<UndecoratedLink to={linkToNavigate}>
|
||||
<StyledIconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
<StyledIconChevronRightContainer>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
</StyledIconChevronRightContainer>
|
||||
</UndecoratedLink>
|
||||
) : (
|
||||
canToggleField && (
|
||||
@@ -273,7 +273,7 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
onClick={handleToggleField}
|
||||
/>
|
||||
)}
|
||||
</StyledIconTableCell>
|
||||
</StyledObjectFieldTableRow>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
+8
-3
@@ -5,10 +5,11 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField';
|
||||
import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import {
|
||||
SETTINGS_OBJECT_TABLE_ROW_GRID_TEMPLATE_COLUMNS,
|
||||
StyledActionTableCell,
|
||||
StyledNameTableCell,
|
||||
StyledObjectTableRow,
|
||||
} from '@/settings/data-model/object-details/components/SettingsObjectItemTableRowStyledComponents';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
@@ -63,7 +64,11 @@ export const SettingsObjectMetadataItemTableRow = ({
|
||||
const Icon = getIcon(objectMetadataItem.icon);
|
||||
|
||||
return (
|
||||
<StyledObjectTableRow key={objectMetadataItem.namePlural} to={link}>
|
||||
<TableRow
|
||||
gridTemplateColumns={SETTINGS_OBJECT_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
|
||||
key={objectMetadataItem.namePlural}
|
||||
to={link}
|
||||
>
|
||||
<StyledNameTableCell>
|
||||
{!!Icon && (
|
||||
<Icon
|
||||
@@ -95,6 +100,6 @@ export const SettingsObjectMetadataItemTableRow = ({
|
||||
</TableCell>
|
||||
<TableCell align="right">{totalObjectCount}</TableCell>
|
||||
<StyledActionTableCell>{action}</StyledActionTableCell>
|
||||
</StyledObjectTableRow>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user