-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow input and display of floats for Number fields (#7340)
### Description - We added a decimal field for a Number Field type in the settings - We updated the Number Field type create a form with decimals input - We are not implementing the dropdown present on the Figma because it seems not related ### Demo <https://www.loom.com/share/18a8d4b712a14f6d8b66806764f8467f?sid=3fc79b46-ae32-46e3-8635-d0eee02e53b2> Fixes #6987 --------- Co-authored-by: gitstart-twenty <[email protected]> Co-authored-by: Marie Stoppa <[email protected]>
- Loading branch information
1 parent
e3ed574
commit 97eff77
Showing
22 changed files
with
478 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ export const queries = { | |
${baseFields} | ||
defaultValue | ||
options | ||
settings | ||
} | ||
} | ||
`, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...rc/modules/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { z } from 'zod'; | ||
|
||
export const numberFieldDefaultValueSchema = z.object({ | ||
decimals: z.number().nullable(), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
...gs/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import styled from '@emotion/styled'; | ||
|
||
import { Button } from '@/ui/input/button/components/Button'; | ||
import { TextInput } from '@/ui/input/components/TextInput'; | ||
import { IconInfoCircle, IconMinus, IconPlus } from 'twenty-ui'; | ||
import { castAsNumberOrNull } from '~/utils/cast-as-number-or-null'; | ||
|
||
type SettingsDataModelFieldNumberDecimalsInputProps = { | ||
value: number; | ||
onChange: (value: number) => void; | ||
disabled?: boolean; | ||
}; | ||
|
||
const StyledCounterContainer = styled.div` | ||
align-items: center; | ||
background: ${({ theme }) => theme.background.noisy}; | ||
border: 1px solid ${({ theme }) => theme.border.color.medium}; | ||
border-radius: 4px; | ||
display: flex; | ||
flex-direction: column; | ||
flex: 1; | ||
gap: ${({ theme }) => theme.spacing(1)}; | ||
justify-content: center; | ||
`; | ||
|
||
const StyledExampleText = styled.div` | ||
color: ${({ theme }) => theme.font.color.primary}; | ||
width: 100%; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
font-weight: ${({ theme }) => theme.font.weight.regular}; | ||
`; | ||
|
||
const StyledCounterControlsIcons = styled.div` | ||
align-items: center; | ||
display: flex; | ||
gap: ${({ theme }) => theme.spacing(2)}; | ||
`; | ||
|
||
const StyledCounterInnerContainer = styled.div` | ||
align-items: center; | ||
align-self: stretch; | ||
display: flex; | ||
gap: ${({ theme }) => theme.spacing(1)}; | ||
padding: ${({ theme }) => theme.spacing(2)}; | ||
height: 24px; | ||
`; | ||
|
||
const StyledTextInput = styled(TextInput)` | ||
width: ${({ theme }) => theme.spacing(16)}; | ||
input { | ||
width: ${({ theme }) => theme.spacing(16)}; | ||
height: ${({ theme }) => theme.spacing(6)}; | ||
text-align: center; | ||
font-weight: ${({ theme }) => theme.font.weight.medium}; | ||
background: ${({ theme }) => theme.background.noisy}; | ||
} | ||
input ~ div { | ||
padding-right: ${({ theme }) => theme.spacing(0)}; | ||
border-radius: ${({ theme }) => theme.spacing(1)}; | ||
background: ${({ theme }) => theme.background.noisy}; | ||
} | ||
`; | ||
|
||
const StyledTitle = styled.div` | ||
color: ${({ theme }) => theme.font.color.light}; | ||
font-size: ${({ theme }) => theme.font.size.xs}; | ||
font-weight: ${({ theme }) => theme.font.weight.semiBold}; | ||
margin-bottom: ${({ theme }) => theme.spacing(1)}; | ||
`; | ||
|
||
const StyledControlButton = styled(Button)` | ||
height: ${({ theme }) => theme.spacing(6)}; | ||
width: ${({ theme }) => theme.spacing(6)}; | ||
padding: 0; | ||
justify-content: center; | ||
svg { | ||
height: ${({ theme }) => theme.spacing(4)}; | ||
width: ${({ theme }) => theme.spacing(4)}; | ||
} | ||
`; | ||
|
||
const StyledInfoButton = styled(Button)` | ||
height: ${({ theme }) => theme.spacing(6)}; | ||
width: ${({ theme }) => theme.spacing(6)}; | ||
padding: 0; | ||
justify-content: center; | ||
svg { | ||
color: ${({ theme }) => theme.font.color.extraLight}; | ||
height: ${({ theme }) => theme.spacing(4)}; | ||
width: ${({ theme }) => theme.spacing(4)}; | ||
} | ||
`; | ||
|
||
const MIN_VALUE = 0; | ||
const MAX_VALUE = 100; | ||
export const SettingsDataModelFieldNumberDecimalsInput = ({ | ||
value, | ||
onChange, | ||
disabled, | ||
}: SettingsDataModelFieldNumberDecimalsInputProps) => { | ||
const exampleValue = (1000).toFixed(value); | ||
|
||
const handleIncrementCounter = () => { | ||
if (value < MAX_VALUE) { | ||
const newValue = value + 1; | ||
onChange(newValue); | ||
} | ||
}; | ||
|
||
const handleDecrementCounter = () => { | ||
if (value > MIN_VALUE) { | ||
const newValue = value - 1; | ||
onChange(newValue); | ||
} | ||
}; | ||
|
||
const handleTextInputChange = (value: string) => { | ||
const castedNumber = castAsNumberOrNull(value); | ||
if (castedNumber === null) { | ||
onChange(MIN_VALUE); | ||
return; | ||
} | ||
|
||
if (castedNumber < MIN_VALUE) { | ||
return; | ||
} | ||
|
||
if (castedNumber > MAX_VALUE) { | ||
onChange(MAX_VALUE); | ||
return; | ||
} | ||
onChange(castedNumber); | ||
}; | ||
return ( | ||
<> | ||
<StyledTitle>Number of decimals</StyledTitle> | ||
<StyledCounterContainer> | ||
<StyledCounterInnerContainer> | ||
<StyledExampleText>Example: {exampleValue}</StyledExampleText> | ||
<StyledCounterControlsIcons> | ||
<StyledInfoButton variant="tertiary" Icon={IconInfoCircle} /> | ||
<StyledControlButton | ||
variant="secondary" | ||
onClick={handleDecrementCounter} | ||
Icon={IconMinus} | ||
disabled={disabled} | ||
/> | ||
<StyledTextInput | ||
name="decimals" | ||
fullWidth | ||
value={value.toString()} | ||
onChange={(value) => handleTextInputChange(value)} | ||
disabled={disabled} | ||
/> | ||
<StyledControlButton | ||
variant="secondary" | ||
onClick={handleIncrementCounter} | ||
Icon={IconPlus} | ||
disabled={disabled} | ||
/> | ||
</StyledCounterControlsIcons> | ||
</StyledCounterInnerContainer> | ||
</StyledCounterContainer> | ||
</> | ||
); | ||
}; |
55 changes: 55 additions & 0 deletions
55
...s/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Controller, useFormContext } from 'react-hook-form'; | ||
import { z } from 'zod'; | ||
|
||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; | ||
import { numberFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema'; | ||
import { SettingsDataModelFieldNumberDecimalsInput } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput'; | ||
import { CardContent } from '@/ui/layout/card/components/CardContent'; | ||
import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number'; | ||
|
||
export const settingsDataModelFieldNumberFormSchema = z.object({ | ||
settings: numberFieldDefaultValueSchema, | ||
}); | ||
|
||
export type SettingsDataModelFieldNumberFormValues = z.infer< | ||
typeof settingsDataModelFieldNumberFormSchema | ||
>; | ||
|
||
type SettingsDataModelFieldNumberFormProps = { | ||
disabled?: boolean; | ||
fieldMetadataItem: Pick< | ||
FieldMetadataItem, | ||
'icon' | 'label' | 'type' | 'defaultValue' | 'settings' | ||
>; | ||
}; | ||
|
||
export const SettingsDataModelFieldNumberForm = ({ | ||
disabled, | ||
fieldMetadataItem, | ||
}: SettingsDataModelFieldNumberFormProps) => { | ||
const { control } = useFormContext<SettingsDataModelFieldNumberFormValues>(); | ||
|
||
return ( | ||
<CardContent> | ||
<Controller | ||
name="settings" | ||
defaultValue={{ | ||
decimals: | ||
fieldMetadataItem?.settings?.decimals ?? DEFAULT_DECIMAL_VALUE, | ||
}} | ||
control={control} | ||
render={({ field: { onChange, value } }) => { | ||
const count = value?.decimals ?? 0; | ||
|
||
return ( | ||
<SettingsDataModelFieldNumberDecimalsInput | ||
value={count} | ||
onChange={(value) => onChange({ decimals: value })} | ||
disabled={disabled} | ||
></SettingsDataModelFieldNumberDecimalsInput> | ||
); | ||
}} | ||
/> | ||
</CardContent> | ||
); | ||
}; |
Oops, something went wrong.