diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 7252995..e0cacc1 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,55 +1,61 @@ # Table of contents -* [Home](README.md) +- [Home](README.md) ## Components -* [Datagrid](components/datagrid.md) -* [Fields](components/fields/README.md) - * [BooleanField](components/fields/booleanfield.md) - * [ChipField](components/fields/chipfield.md) - * [CountryField](components/fields/countryfield.md) - * [CurrencyField](components/fields/currencyfield.md) - * [DateField](components/fields/datefield.md) - * [FileViewerField](components/fields/fileviewerfield.md) - * [MaskedNumberField](components/fields/maskednumberfield.md) - * [PhoneField](components/fields/phonefield.md) - * [ReferenceArrayField](components/fields/referencearrayfield.md) - * [ReferenceField](components/fields/referencefield.md) - * [RichTextField](components/fields/richtextfield.md) - * [SelectField](components/fields/selectfield.md) - * [TextField](components/fields/textfield.md) - * [TimeField](components/fields/timefield.md) - * [TimelineArrayField](components/fields/timelinearrayfield.md) -* [Inputs](components/inputs/README.md) - * [CountryInput](components/inputs/countryinput.md) - * [CurrencyInput](components/inputs/currencyinput.md) - * [DateInput](components/inputs/dateinput.md) - * [MapsInput](components/inputs/mapsinput.md) - * [MaskedNumberInput](components/inputs/maskednumberinput.md) - * [MaskedTextInput](components/inputs/maskedtextinput.md) - * [PhoneInput](components/inputs/phoneinput.md) - * [PlacesTimelineInput](components/inputs/placestimelineinput.md) - * [TimeInput](components/inputs/timeinput.md) -* [ResponsiveDatagrid](components/responsivedatagrid.md) +- [Datagrid](components/datagrid.md) +- [Fields](components/fields/README.md) + - [BooleanField](components/fields/booleanfield.md) + - [ChipField](components/fields/chipfield.md) + - [CountryField](components/fields/countryfield.md) + - [CurrencyField](components/fields/currencyfield.md) + - [DateField](components/fields/datefield.md) + - [FileViewerField](components/fields/fileviewerfield.md) + - [MaskedNumberField](components/fields/maskednumberfield.md) + - [PhoneField](components/fields/phonefield.md) + - [ReferenceArrayField](components/fields/referencearrayfield.md) + - [ReferenceField](components/fields/referencefield.md) + - [RichTextField](components/fields/richtextfield.md) + - [SelectField](components/fields/selectfield.md) + - [TextField](components/fields/textfield.md) + - [TimeField](components/fields/timefield.md) + - [TimelineArrayField](components/fields/timelinearrayfield.md) + - [TimezoneField](components/fields/timezonefield.md) + - [TzTimeField](components/fields/tztimefield.md) + - [TzDateField](components/fields/tzdatefield.md) +- [Inputs](components/inputs/README.md) + - [CountryInput](components/inputs/countryinput.md) + - [CurrencyInput](components/inputs/currencyinput.md) + - [DateInput](components/inputs/dateinput.md) + - [MapsInput](components/inputs/mapsinput.md) + - [MaskedNumberInput](components/inputs/maskednumberinput.md) + - [MaskedTextInput](components/inputs/maskedtextinput.md) + - [PhoneInput](components/inputs/phoneinput.md) + - [PlacesTimelineInput](components/inputs/placestimelineinput.md) + - [TimeInput](components/inputs/timeinput.md) + - [TimezoneInput](components/fields/timezoneinput.md) + - [TzTimeInput](components/fields/tztimeinput.md) + - [TzDateInput](components/fields/tzdateinput.md) +- [ResponsiveDatagrid](components/responsivedatagrid.md) ## Hooks -* [useScreenSize](hooks/usescreensize.md) -* [useCurrencies](hooks/usecurrencies.md) -* [useCountries](hooks/usecountries.md) +- [useScreenSize](hooks/usescreensize.md) +- [useCurrencies](hooks/usecurrencies.md) +- [useCountries](hooks/usecountries.md) ## Services -* [HttpRequest](services/httprequest.md) -* [LocalSession](services/localsession.md) -* [raDataRestProvider](services/radatarestprovider.md) +- [HttpRequest](services/httprequest.md) +- [LocalSession](services/localsession.md) +- [raDataRestProvider](services/radatarestprovider.md) ## Providers -* [DataProvider](providers/dataprovider.md) +- [DataProvider](providers/dataprovider.md) ## Validations -* [minYear](validations/minyear.md) -* [maxYear](validations/maxyear.md) +- [minYear](validations/minyear.md) +- [maxYear](validations/maxyear.md) diff --git a/docs/components/fields/timezonefield.md b/docs/components/fields/timezonefield.md new file mode 100644 index 0000000..cc0adea --- /dev/null +++ b/docs/components/fields/timezonefield.md @@ -0,0 +1,19 @@ +# TimezoneField + +TimezoneField component that map IANA timezone to text with Labeled component. + +### Usage + +```tsx +import { TimezoneField } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| label | false | string | | the label to display. | +| source | true | string | | the field source to retrieve its value. | diff --git a/docs/components/fields/tzdatefield.md b/docs/components/fields/tzdatefield.md new file mode 100644 index 0000000..9970249 --- /dev/null +++ b/docs/components/fields/tzdatefield.md @@ -0,0 +1,19 @@ +# TzDateField + +TzDateField component uses ra DateField with Labeled component and timezone. + +### Usage + +```tsx +import { TzDateField } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| source | true | string | | the field source to retrieve its value. | +| timezoneSource | true | string | | the timezone field source to retrieve its value. | diff --git a/docs/components/fields/tztimefield.md b/docs/components/fields/tztimefield.md new file mode 100644 index 0000000..13e3e4e --- /dev/null +++ b/docs/components/fields/tztimefield.md @@ -0,0 +1,19 @@ +# TzTimeField + +TzTimeField component uses ra DateField (time-only) with Labeled component and timezone. + +### Usage + +```tsx +import { TzTimeField } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| source | true | string | | the field source to retrieve its value. | +| timezoneSource | true | string | | the timezone field source to retrieve its value. | diff --git a/docs/components/inputs/timezoneinput.md b/docs/components/inputs/timezoneinput.md new file mode 100644 index 0000000..ba57f25 --- /dev/null +++ b/docs/components/inputs/timezoneinput.md @@ -0,0 +1,19 @@ +# TimezoneInput + +TimezoneInput component that uses mui Autocomplete with IANA list. + +### Usage + +```tsx +import { TimezoneInput } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| label | false | string | | the label to display. | +| source | true | string | | the field source to retrieve its value. | diff --git a/docs/components/inputs/tzdateinput.md b/docs/components/inputs/tzdateinput.md new file mode 100644 index 0000000..48d8661 --- /dev/null +++ b/docs/components/inputs/tzdateinput.md @@ -0,0 +1,19 @@ +# TzDateInput + +TzDateInput component uses mui Date picker with Dayjs timezone. + +### Usage + +```tsx +import { TzDateInput } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| source | true | string | | the field source to retrieve its value. | +| timezoneSource | true | string | | the timezone field source to retrieve its value. | diff --git a/docs/components/inputs/tztimeinput.md b/docs/components/inputs/tztimeinput.md new file mode 100644 index 0000000..ac89c48 --- /dev/null +++ b/docs/components/inputs/tztimeinput.md @@ -0,0 +1,19 @@ +# TzTimeInput + +TzTimeInput component uses mui Time Picker with Dayjs timezone. + +### Usage + +```tsx +import { TzTimeInput } from '@ra-libs/react'; + +; +``` + +### Props + +| Prop | Required | Type | Default | Description | +| -------------- | -------- | ------- | ------- | ---------------------------------------------------- | +| useLabel | false | boolean | false | whether to use react-admin Labeled component or not. | +| source | true | string | | the field source to retrieve its value. | +| timezoneSource | true | string | | the timezone field source to retrieve its value. | diff --git a/package-lock.json b/package-lock.json index bf79bc4..27fafcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,8 @@ "react-hook-form": "^7.45.0", "react-imask": "^7.0.1", "react-number-format": "^5.2.2", - "scheduler": "^0.23.0" + "scheduler": "^0.23.0", + "timezones.json": "^1.7.1" }, "devDependencies": { "@semantic-release/changelog": "^6.0.3", @@ -12968,6 +12969,11 @@ "xtend": "~4.0.1" } }, + "node_modules/timezones.json": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/timezones.json/-/timezones.json-1.7.1.tgz", + "integrity": "sha512-4dB58ulcrRWfiGufzlofLG45RIoalCTZiFUc7tnj0g8za0CpNTyIOVlspg1JD7OFyDeW5up3ntlkukizwB0IJA==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -22344,6 +22350,11 @@ "xtend": "~4.0.1" } }, + "timezones.json": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/timezones.json/-/timezones.json-1.7.1.tgz", + "integrity": "sha512-4dB58ulcrRWfiGufzlofLG45RIoalCTZiFUc7tnj0g8za0CpNTyIOVlspg1JD7OFyDeW5up3ntlkukizwB0IJA==" + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 7333862..336ada5 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,8 @@ "react-hook-form": "^7.45.0", "react-imask": "^7.0.1", "react-number-format": "^5.2.2", - "scheduler": "^0.23.0" + "scheduler": "^0.23.0", + "timezones.json": "^1.7.1" }, "peerDependencies": { "react": ">=16" diff --git a/src/components/fields/TimezoneField/index.tsx b/src/components/fields/TimezoneField/index.tsx new file mode 100644 index 0000000..724bf3b --- /dev/null +++ b/src/components/fields/TimezoneField/index.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { FunctionField, Labeled, TextFieldProps, useRecordContext, useResourceContext, useTranslate } from 'react-admin' +import timezones from 'timezones.json' + +import { LabeledFieldProps } from '../../../config' + +interface TimezoneFieldProps extends LabeledFieldProps { + source: string +} + +export function TimezoneField(props: TimezoneFieldProps) { + const { useLabel, source } = props + + const record = useRecordContext() + + const timezone = timezones.find((timezone) => timezone.utc[0] === record?.[source]) + + const resource = useResourceContext() + const translate = useTranslate() + + const label = props.label ? props.label : translate(`resources.${resource}.fields.${source}`) + + const field = timezone?.text} /> + return useLabel ? {field} : <>{field} +} diff --git a/src/components/fields/TzDateField/index.tsx b/src/components/fields/TzDateField/index.tsx new file mode 100644 index 0000000..80eb8d1 --- /dev/null +++ b/src/components/fields/TzDateField/index.tsx @@ -0,0 +1,39 @@ +import dayjs from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import React from 'react' +import { DateField as RaDateField, DateFieldProps, Labeled, useRecordContext } from 'react-admin' + +import { LabeledFieldProps } from '../../../config' + +dayjs.extend(utc) +dayjs.extend(timezone) + +interface TzDateFieldProps extends LabeledFieldProps { + timezoneSource: string + source: string +} + +export function TzDateField(props: TzDateFieldProps) { + const { useLabel, timezoneSource, ...rest } = props + + const record = useRecordContext() + const timezone = record?.[timezoneSource] + const date = record?.[rest.source] + const utcDate = dayjs(date) + + const field = ( + { + return utcDate.toDate() + }} + options={{ + timeZone: timezone, + }} + /> + ) + return useLabel ? {field} : <>{field} +} diff --git a/src/components/fields/TzTimeField/index.tsx b/src/components/fields/TzTimeField/index.tsx new file mode 100644 index 0000000..1550878 --- /dev/null +++ b/src/components/fields/TzTimeField/index.tsx @@ -0,0 +1,38 @@ +import dayjs from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import React from 'react' +import { DateField as RaDateField, DateFieldProps, Labeled, useRecordContext } from 'react-admin' + +import { LabeledFieldProps } from '../../../config' + +dayjs.extend(utc) +dayjs.extend(timezone) + +interface TzTimeFieldProps extends LabeledFieldProps { + timezoneSource: string + source: string +} + +export function TzTimeField(props: TzTimeFieldProps) { + const { useLabel, timezoneSource, ...rest } = props + const record = useRecordContext() + const timezone = record?.[timezoneSource] + const date = record?.[rest.source] + const utcDate = dayjs(date) + + const field = ( + utcDate.toDate()} + options={{ + hour: '2-digit', + minute: '2-digit', + timeZone: timezone, + }} + /> + ) + return useLabel ? {field} : <>{field} +} diff --git a/src/components/fields/index.ts b/src/components/fields/index.ts index efb59c3..e753287 100644 --- a/src/components/fields/index.ts +++ b/src/components/fields/index.ts @@ -13,3 +13,6 @@ export * from './CountryField' export * from './TimelineArrayField' export * from './TimeField' export * from './PhoneField' +export * from './TzDateField' +export * from './TzTimeField' +export * from './TimezoneField' diff --git a/src/components/inputs/MapsInput/index.tsx b/src/components/inputs/MapsInput/index.tsx index 85cc772..418d5b4 100644 --- a/src/components/inputs/MapsInput/index.tsx +++ b/src/components/inputs/MapsInput/index.tsx @@ -1,17 +1,17 @@ -import React, { useEffect } from 'react' -import Box from '@mui/material/Box' -import TextField from '@mui/material/TextField' -import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete' import LocationOnIcon from '@mui/icons-material/LocationOn' +import { debounce } from '@mui/material' +import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete' +import Box from '@mui/material/Box' import Grid from '@mui/material/Grid' +import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import parse from 'autosuggest-highlight/parse' -import { throttle } from 'lodash' - +import React, { useEffect } from 'react' import { TextInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' -import { HttpRequest } from '../../../services' import { useFormContext } from 'react-hook-form' +import { HttpRequest } from '../../../services' + interface MapsInputProps extends TextInputProps { source: string useMainText?: boolean @@ -73,7 +73,7 @@ export function MapsInput(props: MapsInputProps) { const fetch = React.useMemo( () => - throttle((input: string, callback: (results?: readonly PlaceType[]) => void) => { + debounce((input: string, callback: (results?: readonly PlaceType[]) => void) => { getPlacePredictions( API_URL, API_SEARCH_FIELD, @@ -81,7 +81,7 @@ export function MapsInput(props: MapsInputProps) { { search: input, mapType: props.mapType }, callback, ) - }, 200), + }, 400), [], ) diff --git a/src/components/inputs/PlacesTimelineInput/index.tsx b/src/components/inputs/PlacesTimelineInput/index.tsx index 800a9d1..b818f6a 100644 --- a/src/components/inputs/PlacesTimelineInput/index.tsx +++ b/src/components/inputs/PlacesTimelineInput/index.tsx @@ -1,15 +1,5 @@ -import React, { useEffect } from 'react' -import Box from '@mui/material/Box' -import TextField from '@mui/material/TextField' -import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete' +import DeleteIcon from '@mui/icons-material/Delete' import LocationOnIcon from '@mui/icons-material/LocationOn' -import Grid from '@mui/material/Grid' -import Typography from '@mui/material/Typography' -import parse from 'autosuggest-highlight/parse' -import { throttle } from 'lodash' -import { useFormContext } from 'react-hook-form' - -import { TextInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' import { Timeline, TimelineConnector, @@ -19,9 +9,17 @@ import { TimelineOppositeContent, TimelineSeparator, } from '@mui/lab' -import { IconButton } from '@mui/material' +import { debounce, IconButton } from '@mui/material' +import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete' +import Box from '@mui/material/Box' +import Grid from '@mui/material/Grid' +import TextField from '@mui/material/TextField' +import Typography from '@mui/material/Typography' +import parse from 'autosuggest-highlight/parse' +import React, { useEffect } from 'react' +import { TextInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' +import { useFormContext } from 'react-hook-form' -import DeleteIcon from '@mui/icons-material/Delete' import { HttpRequest } from '../../../services' interface PlacesTimelineInputProps extends TextInputProps { @@ -84,7 +82,7 @@ export function PlacesTimelineInput(props: PlacesTimelineInputProps) { const fetch = React.useMemo( () => - throttle((input: string, callback: (results?: readonly PlaceType[]) => void) => { + debounce((input: string, callback: (results?: readonly PlaceType[]) => void) => { getPlacePredictions( API_URL, API_SEARCH_FIELD, @@ -92,7 +90,7 @@ export function PlacesTimelineInput(props: PlacesTimelineInputProps) { { search: input, mapType: props.mapType }, callback, ) - }, 200), + }, 400), [], ) diff --git a/src/components/inputs/TimezoneInput/index.tsx b/src/components/inputs/TimezoneInput/index.tsx new file mode 100644 index 0000000..f89a62e --- /dev/null +++ b/src/components/inputs/TimezoneInput/index.tsx @@ -0,0 +1,82 @@ +import { Autocomplete, TextField } from '@mui/material' +import React, { useEffect, useState } from 'react' +import { DateInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' +import { useFormContext } from 'react-hook-form' +import timezones from 'timezones.json' + +interface OptionValue { + label: string + value: string +} + +export function TimezoneInput(props: DateInputProps) { + const autoCompletesTimezones: OptionValue[] = timezones.map((timezone) => { + return { label: timezone.text, value: timezone.utc[0] } + }) + + const translate = useTranslate() + const { setValue: setFormValue } = useFormContext() + + const { margin = 'dense', defaultValue = 'America/Sao_Paulo' } = props + + const { + field, + fieldState: { isTouched, invalid, error }, + formState: { isSubmitted }, + isRequired, + id, + } = useInput({ source: props.source }) + + const hasError = (isTouched || isSubmitted) && invalid + + const resource = useResourceContext() + const label = props.label ? props.label : translate(`resources.${resource}.fields.${field.name}`) + + const timezoneDefaultValue = autoCompletesTimezones.find((timezone) => timezone.value === defaultValue) + + const [optionValue, setOptionValue] = useState(timezoneDefaultValue) + + useEffect(() => { + if (!field.value) { + setFormValue(props.source, timezoneDefaultValue?.value, { + shouldDirty: true, + }) + } + }, []) + + useEffect(() => { + if (field.value) { + const timezone = autoCompletesTimezones.find((timezone) => timezone.value === field.value) + setOptionValue(timezone) + } + }, [field.value]) + + const handleValueChange = (_: React.SyntheticEvent, newOptionValue: OptionValue | null) => { + setFormValue(props.source, newOptionValue?.value, { + shouldDirty: true, + }) + } + + return ( + ( + + )} + /> + ) +} diff --git a/src/components/inputs/TzDateInput/index.tsx b/src/components/inputs/TzDateInput/index.tsx new file mode 100644 index 0000000..18e8325 --- /dev/null +++ b/src/components/inputs/TzDateInput/index.tsx @@ -0,0 +1,76 @@ +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' +import { DatePicker } from '@mui/x-date-pickers/DatePicker' +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' +import dayjs, { Dayjs } from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import React, { useEffect } from 'react' +import { DateInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' +import { useFormContext } from 'react-hook-form' + +dayjs.extend(utc) +dayjs.extend(timezone) + +interface TzDateInputProps extends DateInputProps { + timezoneSource: string + adapterLocale?: string +} + +export function TzDateInput(props: TzDateInputProps) { + const translate = useTranslate() + + const { setValue: setFormValue } = useFormContext() + + const { margin = 'dense', adapterLocale = 'pt-br' } = props + + const { + field, + fieldState: { isTouched, invalid, error }, + formState: { isSubmitted }, + isRequired, + id, + } = useInput(props) + + const hasError = (isTouched || isSubmitted) && invalid + + const resource = useResourceContext() + const label = props.label ? props.label : translate(`resources.${resource}.fields.${field.name}`) + + const timezoneSourceInput = useInput({ + source: props.timezoneSource, + }) + + const [value, setValue] = React.useState() + + const handleValueChange = (newValue: Dayjs | null) => { + setFormValue(field.name, newValue?.toISOString(), { shouldDirty: true }) + } + + useEffect(() => { + if (field.value) { + const date = dayjs.utc(field.value) + setValue(date.tz(timezoneSourceInput.field.value)) + } + }, [field.value]) + + return ( + + + + ) +} diff --git a/src/components/inputs/TzTimeInput/index.tsx b/src/components/inputs/TzTimeInput/index.tsx new file mode 100644 index 0000000..36d381c --- /dev/null +++ b/src/components/inputs/TzTimeInput/index.tsx @@ -0,0 +1,91 @@ +import 'dayjs/locale/pt-br' + +import { LocalizationProvider } from '@mui/x-date-pickers' +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' +import { TimePicker } from '@mui/x-date-pickers/TimePicker' +import dayjs, { Dayjs } from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import React, { useEffect } from 'react' +import { DateInputProps, useInput, useResourceContext, useTranslate } from 'react-admin' +import { useFormContext } from 'react-hook-form' + +interface TzTimeInputProps extends DateInputProps { + timezoneSource: string + adapterLocale?: string + dateSource?: string +} + +dayjs.extend(utc) +dayjs.extend(timezone) + +export function TzTimeInput(props: TzTimeInputProps) { + const translate = useTranslate() + const { setValue: setFormValue } = useFormContext() + + const { margin = 'dense', adapterLocale = 'pt-br', dateSource } = props + + const { + field, + fieldState: { isTouched, invalid, error }, + formState: { isSubmitted }, + isRequired, + id, + } = useInput({ source: props.source }) + + const timezoneSourceInput = useInput({ + source: props.timezoneSource, + }) + + const [value, setValue] = React.useState(null) + + const hasError = (isTouched || isSubmitted) && invalid + + const resource = useResourceContext() + + const label = props.label ? props.label : translate(`resources.${resource}.fields.${field.name}`) + + const dateSourceInput = useInput({ + source: dateSource || '', + }) + + const handleValueChange = (newValue: Dayjs | null) => { + if (dateSourceInput.id !== id) { + const dateSourceValue = dateSourceInput.field.value as Date + newValue?.set('year', dateSourceValue.getUTCFullYear()) + newValue?.set('month', dateSourceValue.getUTCMonth()) + newValue?.set('date', dateSourceValue.getUTCDate()) + } + + setFormValue(field.name, newValue?.toISOString(), { + shouldDirty: true, + }) + } + + useEffect(() => { + if (field.value) { + const date = dayjs.utc(field.value) + setValue(dayjs.tz(date, timezoneSourceInput.field.value)) + } + }, [field.value]) + + return ( + + + + ) +} diff --git a/src/components/inputs/index.ts b/src/components/inputs/index.ts index 92bfc72..121335d 100644 --- a/src/components/inputs/index.ts +++ b/src/components/inputs/index.ts @@ -7,3 +7,6 @@ export * from './PlacesTimelineInput' export * from './TimeInput' export * from './MapsInput' export * from './PhoneInput' +export * from './TzDateInput' +export * from './TzTimeInput' +export * from './TimezoneInput'