Skip to content

Commit 7ceaa87

Browse files
gitstart-app[bot]gitstart-twentyijreilly
authored
Avanced Settings: Custom API names for Select & Multi-Select Keys (twentyhq#7489)
### Description - text input was changed because it renders an empty div as the right icon, but the margin and padding affect the layout - we have duplicated code on ExpandedWidthAnimationVariants.ts, because of an eslint rule that prevents more than one const definition, can we ignore the rule? - ### Demo <https://www.loom.com/share/17a37bf5549a4a23ba12343b6046ec6b?sid=4cf297f3-66db-44c9-9a9b-7bde89b90d02> ### Refs <twentyhq#6146> --------- Co-authored-by: gitstart-twenty <[email protected]> Co-authored-by: gitstart-twenty <[email protected]> Co-authored-by: Marie Stoppa <[email protected]>
1 parent 3ecf955 commit 7ceaa87

File tree

5 files changed

+164
-17
lines changed

5 files changed

+164
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const ADVANCED_SETTINGS_ANIMATION_DURATION = {
2+
opacity: 0.2,
3+
size: 0.4,
4+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations';
2+
3+
export const EXPANDED_WIDTH_ANIMATION_VARIANTS = {
4+
initial: {
5+
opacity: 0,
6+
width: 0,
7+
overflow: 'hidden',
8+
transition: {
9+
opacity: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.opacity },
10+
width: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.size },
11+
},
12+
},
13+
animate: {
14+
opacity: 1,
15+
width: '100%',
16+
overflow: 'hidden',
17+
transition: {
18+
opacity: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.opacity },
19+
width: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.size },
20+
},
21+
},
22+
exit: {
23+
opacity: 0,
24+
width: 0,
25+
overflow: 'hidden',
26+
transition: {
27+
opacity: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.opacity },
28+
width: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.size },
29+
},
30+
},
31+
};

packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx

+72-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import styled from '@emotion/styled';
22
import { DropResult } from '@hello-pangea/dnd';
33
import { Controller, useFormContext } from 'react-hook-form';
4-
import { IconPlus } from 'twenty-ui';
4+
import { IconPlus, IconTool, MAIN_COLORS } from 'twenty-ui';
55
import { z } from 'zod';
66

77
import {
@@ -24,6 +24,10 @@ import { moveArrayItem } from '~/utils/array/moveArrayItem';
2424
import { toSpliced } from '~/utils/array/toSpliced';
2525
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
2626

27+
import { EXPANDED_WIDTH_ANIMATION_VARIANTS } from '@/settings/constants/ExpandedWidthAnimationVariants';
28+
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
29+
import { AnimatePresence, motion } from 'framer-motion';
30+
import { useRecoilValue } from 'recoil';
2731
import { SettingsDataModelFieldSelectFormOptionRow } from './SettingsDataModelFieldSelectFormOptionRow';
2832

2933
export const settingsDataModelFieldSelectFormSchema = z.object({
@@ -56,15 +60,51 @@ const StyledContainer = styled(CardContent)`
5660
padding-bottom: ${({ theme }) => theme.spacing(3.5)};
5761
`;
5862

59-
const StyledLabel = styled.span`
63+
const StyledOptionsLabel = styled.div<{
64+
isAdvancedModeEnabled: boolean;
65+
}>`
6066
color: ${({ theme }) => theme.font.color.light};
61-
display: block;
6267
font-size: ${({ theme }) => theme.font.size.xs};
6368
font-weight: ${({ theme }) => theme.font.weight.semiBold};
64-
margin-bottom: 6px;
69+
margin-bottom: ${({ theme }) => theme.spacing(1.5)};
70+
margin-top: ${({ theme }) => theme.spacing(1)};
71+
width: 100%;
72+
margin-left: ${({ theme, isAdvancedModeEnabled }) =>
73+
theme.spacing(isAdvancedModeEnabled ? 10 : 0)};
74+
`;
75+
76+
const StyledApiKeyContainer = styled.div`
77+
display: flex;
78+
gap: ${({ theme }) => theme.spacing(2)};
79+
width: 100%;
80+
`;
81+
82+
const StyledApiKey = styled.span`
83+
color: ${({ theme }) => theme.font.color.light};
84+
font-size: ${({ theme }) => theme.font.size.xs};
85+
font-weight: ${({ theme }) => theme.font.weight.semiBold};
86+
margin-bottom: ${({ theme }) => theme.spacing(1.5)};
87+
margin-top: ${({ theme }) => theme.spacing(1)};
88+
width: 100%;
89+
white-space: nowrap;
90+
`;
91+
92+
const StyledLabelContainer = styled.div`
93+
display: flex;
94+
`;
95+
96+
const StyledIconContainer = styled.div`
97+
border-right: 1px solid ${MAIN_COLORS.yellow};
98+
display: flex;
99+
100+
margin-bottom: ${({ theme }) => theme.spacing(1.5)};
65101
margin-top: ${({ theme }) => theme.spacing(1)};
66102
`;
67103

104+
const StyledIconTool = styled(IconTool)`
105+
margin-right: ${({ theme }) => theme.spacing(0.5)};
106+
`;
107+
68108
const StyledFooter = styled(CardFooter)`
69109
background-color: ${({ theme }) => theme.background.secondary};
70110
padding: ${({ theme }) => theme.spacing(1)};
@@ -80,6 +120,7 @@ export const SettingsDataModelFieldSelectForm = ({
80120
}: SettingsDataModelFieldSelectFormProps) => {
81121
const { initialDefaultValue, initialOptions } =
82122
useSelectSettingsFormInitialValues({ fieldMetadataItem });
123+
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
83124

84125
const {
85126
control,
@@ -205,7 +246,33 @@ export const SettingsDataModelFieldSelectForm = ({
205246
render={({ field: { onChange, value: options } }) => (
206247
<>
207248
<StyledContainer>
208-
<StyledLabel>Options</StyledLabel>
249+
<StyledLabelContainer>
250+
<AnimatePresence>
251+
{isAdvancedModeEnabled && (
252+
<motion.div
253+
initial="initial"
254+
animate="animate"
255+
exit="exit"
256+
variants={EXPANDED_WIDTH_ANIMATION_VARIANTS}
257+
>
258+
<StyledApiKeyContainer>
259+
<StyledIconContainer>
260+
<StyledIconTool
261+
size={12}
262+
color={MAIN_COLORS.yellow}
263+
/>
264+
</StyledIconContainer>
265+
<StyledApiKey>API keys</StyledApiKey>
266+
</StyledApiKeyContainer>
267+
</motion.div>
268+
)}
269+
</AnimatePresence>
270+
<StyledOptionsLabel
271+
isAdvancedModeEnabled={isAdvancedModeEnabled}
272+
>
273+
Options
274+
</StyledOptionsLabel>
275+
</StyledLabelContainer>
209276
<DraggableList
210277
onDragEnd={(result) => handleDragEnd(options, result, onChange)}
211278
draggableItems={

packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx

+56-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { v4 } from 'uuid';
1414

1515
import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem';
16+
import { EXPANDED_WIDTH_ANIMATION_VARIANTS } from '@/settings/constants/ExpandedWidthAnimationVariants';
1617
import { OPTION_VALUE_MAXIMUM_LENGTH } from '@/settings/data-model/constants/OptionValueMaximumLength';
1718
import { getOptionValueFromLabel } from '@/settings/data-model/fields/forms/select/utils/getOptionValueFromLabel';
1819
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
@@ -23,6 +24,9 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
2324
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
2425
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
2526
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
27+
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
28+
import { AnimatePresence, motion } from 'framer-motion';
29+
import { useRecoilValue } from 'recoil';
2630

2731
type SettingsDataModelFieldSelectFormOptionRowProps = {
2832
className?: string;
@@ -45,19 +49,29 @@ const StyledRow = styled.div`
4549

4650
const StyledColorSample = styled(ColorSample)`
4751
cursor: pointer;
48-
margin-left: 9px;
49-
margin-right: 14px;
52+
margin-top: ${({ theme }) => theme.spacing(1)};
53+
margin-bottom: ${({ theme }) => theme.spacing(1)};
54+
55+
margin-right: ${({ theme }) => theme.spacing(3.5)};
56+
margin-left: ${({ theme }) => theme.spacing(3.5)};
5057
`;
5158

5259
const StyledOptionInput = styled(TextInput)`
53-
flex: 1 0 auto;
54-
margin-right: ${({ theme }) => theme.spacing(2)};
55-
60+
flex-grow: 1;
61+
width: 100%;
5662
& input {
5763
height: ${({ theme }) => theme.spacing(6)};
5864
}
5965
`;
6066

67+
const StyledIconGripVertical = styled(IconGripVertical)`
68+
margin-right: ${({ theme }) => theme.spacing(0.75)};
69+
`;
70+
71+
const StyledLightIconButton = styled(LightIconButton)`
72+
margin-left: ${({ theme }) => theme.spacing(2)};
73+
`;
74+
6175
export const SettingsDataModelFieldSelectFormOptionRow = ({
6276
className,
6377
isDefault,
@@ -69,6 +83,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
6983
option,
7084
isNewRow,
7185
}: SettingsDataModelFieldSelectFormOptionRowProps) => {
86+
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
7287
const theme = useTheme();
7388

7489
const dropdownIds = useMemo(() => {
@@ -90,11 +105,34 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
90105

91106
return (
92107
<StyledRow className={className}>
93-
<IconGripVertical
108+
<StyledIconGripVertical
109+
style={{ minWidth: theme.icon.size.md }}
94110
size={theme.icon.size.md}
95111
stroke={theme.icon.stroke.sm}
96112
color={theme.font.color.extraLight}
97113
/>
114+
<AnimatePresence>
115+
{isAdvancedModeEnabled && (
116+
<motion.div
117+
initial="initial"
118+
animate="animate"
119+
exit="exit"
120+
variants={EXPANDED_WIDTH_ANIMATION_VARIANTS}
121+
>
122+
<StyledOptionInput
123+
value={option.value}
124+
onChange={(input) =>
125+
onChange({
126+
...option,
127+
value: getOptionValueFromLabel(input),
128+
})
129+
}
130+
RightIcon={isDefault ? IconCheck : undefined}
131+
maxLength={OPTION_VALUE_MAXIMUM_LENGTH}
132+
/>
133+
</motion.div>
134+
)}
135+
</AnimatePresence>
98136
<Dropdown
99137
dropdownId={dropdownIds.color}
100138
dropdownPlacement="bottom-start"
@@ -122,13 +160,18 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
122160
/>
123161
<StyledOptionInput
124162
value={option.label}
125-
onChange={(label) =>
163+
onChange={(label) => {
164+
const optionNameHasBeenEdited = !(
165+
option.value === getOptionValueFromLabel(option.label)
166+
);
126167
onChange({
127168
...option,
128169
label,
129-
value: getOptionValueFromLabel(label),
130-
})
131-
}
170+
value: optionNameHasBeenEdited
171+
? option.value
172+
: getOptionValueFromLabel(label),
173+
});
174+
}}
132175
RightIcon={isDefault ? IconCheck : undefined}
133176
maxLength={OPTION_VALUE_MAXIMUM_LENGTH}
134177
onInputEnter={handleInputEnter}
@@ -141,7 +184,9 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
141184
dropdownHotkeyScope={{
142185
scope: dropdownIds.actions,
143186
}}
144-
clickableComponent={<LightIconButton Icon={IconDotsVertical} />}
187+
clickableComponent={
188+
<StyledLightIconButton accent="tertiary" Icon={IconDotsVertical} />
189+
}
145190
dropdownComponents={
146191
<DropdownMenu>
147192
<DropdownMenuItemsContainer>

packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isDefined } from 'twenty-ui';
33

44
const transitionValues = {
55
transition: {
6-
opactity: { duration: 0.2 },
6+
opacity: { duration: 0.2 },
77
height: { duration: 0.4 },
88
},
99
};

0 commit comments

Comments
 (0)