-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow input and display of floats for Number fields #7340
Changes from 1 commit
b3d639c
725f0c6
7313a85
816278f
4acbb14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ export const CREATE_ONE_FIELD_METADATA_ITEM = gql` | |
settings | ||
defaultValue | ||
options | ||
settings | ||
} | ||
} | ||
`; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ export const FIND_MANY_OBJECT_METADATA_ITEMS = gql` | |
createdAt | ||
updatedAt | ||
defaultValue | ||
settings | ||
options | ||
settings | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. syntax: Duplicate 'settings' field. Remove one instance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same |
||
relationDefinition { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ export const queries = { | |
${baseFields} | ||
defaultValue | ||
options | ||
settings | ||
} | ||
} | ||
`, | ||
|
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(), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
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; | ||
}; | ||
|
||
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: 0px; | ||
border-radius: 4px; | ||
gitstart-twenty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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, | ||
}: 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; | ||
Comment on lines
+121
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider setting the value to an empty string or undefined instead of MIN_VALUE when input is invalid |
||
} | ||
|
||
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} /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: The info button lacks an onClick handler or tooltip to provide additional information |
||
<StyledControlButton | ||
variant="secondary" | ||
onClick={handleDecrementCounter} | ||
Icon={IconMinus} | ||
/> | ||
<StyledTextInput | ||
gitstart-twenty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
name="decimals" | ||
fullWidth | ||
value={value.toString()} | ||
onChange={(value) => handleTextInputChange(value)} | ||
/> | ||
<StyledControlButton | ||
variant="secondary" | ||
onClick={handleIncrementCounter} | ||
Icon={IconPlus} | ||
/> | ||
</StyledCounterControlsIcons> | ||
</StyledCounterInnerContainer> | ||
</StyledCounterContainer> | ||
</> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
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'; | ||
|
||
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 | ||
disabled={disabled} | ||
name="settings" | ||
defaultValue={{ decimals: fieldMetadataItem?.settings?.decimals ?? 0 }} | ||
gitstart-twenty marked this conversation as resolved.
Show resolved
Hide resolved
|
||
control={control} | ||
render={({ field: { onChange, value } }) => { | ||
const count = value?.decimals ?? 0; | ||
|
||
return ( | ||
<SettingsDataModelFieldNumberDecimalsInput | ||
value={count} | ||
onChange={(value) => onChange({ decimals: value })} | ||
></SettingsDataModelFieldNumberDecimalsInput> | ||
); | ||
}} | ||
/> | ||
</CardContent> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: Duplicate 'settings' field in the response. Remove one instance to avoid redundancy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed with greptile !