/*
* The MIT License (MIT)
*
* Copyright (c) 2015 - present Instructure, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { SyntheticEvent } from 'react'
import type { FormMessage } from '@instructure/ui-form-field/v11_6'
import type { InteractionType } from '@instructure/ui-react-utils'
import type { Moment } from '@instructure/ui-i18n'
import type { Renderable } from '@instructure/shared-types'
type DateTimeInputProps = {
/**
* The label over the composite `DateTimeInput` component
**/
description: React.ReactNode
/**
* The label over the DateInput
**/
dateRenderLabel: Renderable
/**
* The screen reader label for the calendar navigation header's prev month
* button
*/
prevMonthLabel: string
/**
* The screen reader label for the calendar navigation header's next month
* button
*/
nextMonthLabel: string
/**
* HTML placeholder text to display when the date input has no value.
* This should be hint text, not a label replacement.
**/
datePlaceholder?: string
/**
* HTML placeholder text to display when the time input has no value.
* This should be hint text, not a label replacement.
**/
timePlaceholder?: string
/**
* The format of the date shown in the `DateInput` when a date is selected.
* Valid formats are compatible with
* [Moment formats](https://momentjs.com/docs/#/displaying/format/),
* including localized formats.
*
* If omitted, it will use 'LL' which is a localized date with full month,
* e.g. "August 6, 2014"
**/
dateFormat?: string
/**
* The label over the time input
**/
timeRenderLabel: Renderable
/**
* The number of minutes to increment by when generating the allowable time options.
*/
timeStep?: 5 | 10 | 15 | 20 | 30 | 60
/**
* The format of the time shown in the `TimeSelect` when a time is selected.
* Valid formats are compatible with
* [Moment formats](https://momentjs.com/docs/#/displaying/format/),
* including localized formats.
*
* If omitted, defers to the underlying `TimeSelect`'s default.
**/
timeFormat?: string
/**
* A standard language identifier.
*
* See [Moment.js](https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/) for
* more details.
*
* This property can also be set via a context property and if both are set
* then the component property takes precedence over the context property.
*
* The web browser's locale will be used if no value is set via a component
* property or a context property.
**/
locale?: string
/**
* A timezone identifier in the format: *Area/Location*
*
* See [List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the list
* of possible options.
*
* This property can also be set via a context property and if both are set
* then the component property takes precedence over the context property.
*
* The web browser's timezone will be used if no value is set via a component
* property or a context property.
**/
timezone?: string
/**
* The message shown to the user when the data is invalid.
* If a string, shown to the user anytime the input is invalid.
*
* If a function, receives a single parameter:
* - *rawDateValue*: the string entered as a date by the user.
*
* Currently, times must be selected from a list, it can never be incorrect,
* Though `invalidDateTimeMessage()` will be called if the user selects a time without
* setting the date.
**/
invalidDateTimeMessage: string | ((rawDateValue: string) => string)
/**
* Toggles whether to show built-in messages (the date/time, or the
* `invalidDateTimeMessage`). Even when set to `false` the component will
* show user supplied messages by the `messages` prop.
* @default true
*/
showMessages?: boolean
/**
* Extra message(s) to be displayed.
*/
messages?: FormMessage[]
/**
* This format of the composite date-time when displayed in messages.
* Valid formats are defined in the
* [Moment docs](https://momentjs.com/docs/#/displaying/format/)
**/
messageFormat?: string
/**
* The layout of this component.
* Vertically stacked, horizontally arranged in 2 columns, or inline (default).
* See [FormFieldGroup](FormFieldGroup) for details.
**/
layout?: 'stacked' | 'columns' | 'inline'
/**
* Controls the spacing between the inputs when they are in a vertical layout.
**/
rowSpacing?: 'none' | 'small' | 'medium' | 'large'
/**
* Controls the spacing between the inputs when they are in a horizontal layout.
**/
colSpacing?: 'none' | 'small' | 'medium' | 'large'
/**
* An ISO 8601 formatted date string representing the current date-time
* (must be accompanied by an onChange prop).
**/
value?: string // TODO: controllable(I18nPropTypes.iso8601, 'onChange')
/**
* An ISO 8601 formatted date string to use if `value` isn't provided.
**/
defaultValue?: string
/**
* An array of labels containing the name of each day of the week. The visible
* portion of the label should be abbreviated (no longer than three characters).
* Note that screen readers will read this content preceding each date as the
* `` is navigated. Consider using
* [AccessibleContent](AccessibleContent) with the `alt` prop containing the
* full day name for assistive technologies and the children containing the
* abbreviation. ex. `[Mon, ...]`
*
* You must render set the starting day of the week to the one specified by
* the current locale (e.g. Sunday in the US, Monday in Germany),
* dates are already displayed this way.
*
* By default it will render accessible, localized, abbreviated weekdays
* with week starts according to the current locale.
*/
renderWeekdayLabels?: (
| React.ReactNode
| ((...args: any[]) => React.ReactNode)
)[]
/**
* Specifies if the input is required (its passed down to the native DOM
* elements). If its `true` then an empty input will produce an error message
* (`invalidDateTimeMessage`)
*/
isRequired?: boolean
/**
* Specifies if interaction with the input is enabled, disabled, or readonly.
* When "disabled", the input changes visibly to indicate that it cannot
* receive user interactions. When "readonly" the input still cannot receive
* user interactions but it keeps the same styles as if it were enabled.
*/
interaction?: InteractionType
/**
* Called when the date-time value has changed.
* The passed in parameters are:
* - *event*: The triggering event (which may be from the underlying
* `DateInput` or `TimeSelect`)
* - *isoValue*: The new date value in ISO8601 format, undefined if its invalid
**/
onChange?: (event: SyntheticEvent, isoValue?: string) => void
/**
* The HTML `input` element where the date is entered.
**/
dateInputRef?: (el: HTMLInputElement | null) => void
/**
* The HTML `input` element where the time is entered.
**/
timeInputRef?: (el: HTMLInputElement | null) => void
/**
* onBlur event handler for when focus leaves DateTimeInput.
* Does not fire when focus moves between DateInput and TimeSelect within the
* component
*/
onBlur?: (e: SyntheticEvent) => void
/*
* Specify which date(s) will be shown as disabled in the calendar.
* You can either supply an array of ISO8601 timeDate strings or
* a function that will be called for each date shown in the calendar.
*/
disabledDates?: string[] | ((isoDateToCheck: string) => boolean)
/**
* Error message shown to the user if they enter a date that is disabled.
* If not specified the component will show the `invalidDateTimeMessage`.
*/
disabledDateTimeMessage?: string | ((rawDateValue: string) => string)
/**
* Whether to allow the user to enter non-step divisible values in the time
* input field. Note that even if this is set to false one can enter non-step
* divisible values programmatically. The user will need to enter the value
* exactly (except for lower/uppercase) as specified by the `timeFormat` prop
* for it to be accepted.
* Default is `undefined` which equals to `false`
*/
allowNonStepInput?: boolean
/**
* The default time to be prefilled if a day is selected. The time input has to be empty for this to be applied.
* An error is thrown if the time format is not HH:MM.
*/
initialTimeForNewDate?: string
/**
* Used for getting the internal reset function of DateTimeInput. If that
* function is called, the component will reset to its default inner state.
* The callback function will be called in componentDidMount
* NOTE: this won't call onChange, so you have to reset the value manually when calling reset
*/
reset?: (reset: () => void) => void
/**
* Screen reader label appended to the date label when the day is selected.
* Used to announce the selected state to assistive technologies.
*/
selectedLabel?: string
}
type DateTimeInputState = {
// the time and date currently selected
iso?: Moment
// set when the user types text into the DateInput
dateInputTextChanged: boolean
// The currently selected dateTime in the calendar. Will be final when
// the calendar closes.
calendarSelectedDate?: Moment
// the date rendered by the opened calendar. Not selected just determines
// which month/year to show
renderedDate: Moment
// The value currently displayed in the dateInput component.
// Just the date part is visible
dateInputText: string
// The value currently displayed in the timeSelect component as ISO datetime
timeSelectValue?: string
// The message (success/error) shown below the component
message?: FormMessage
// Whether the calendar is open
isShowingCalendar?: boolean
}
type PropKeys = keyof DateTimeInputProps
type AllowedPropKeys = Readonly>
const allowedProps: AllowedPropKeys = [
'description',
'dateRenderLabel',
'prevMonthLabel',
'nextMonthLabel',
'datePlaceholder',
'timePlaceholder',
'dateFormat',
'interaction',
'timeRenderLabel',
'timeStep',
'timeFormat',
'locale',
'timezone',
'invalidDateTimeMessage',
'showMessages',
'messages',
'messageFormat',
'layout',
'rowSpacing',
'colSpacing',
'value',
'defaultValue',
'renderWeekdayLabels',
'isRequired',
'onChange',
'dateInputRef',
'timeInputRef',
'onBlur',
'disabledDates',
'disabledDateTimeMessage',
'allowNonStepInput',
'reset',
'selectedLabel'
]
export type { DateTimeInputProps, DateTimeInputState }
export { allowedProps }