@strategicos/ui 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/dist/components/date-input.d.ts +46 -0
- package/dist/components/date-input.d.ts.map +1 -0
- package/dist/components/date-input.js +202 -0
- package/dist/components/date-input.js.map +1 -0
- package/dist/components/date-picker-input.d.ts +49 -0
- package/dist/components/date-picker-input.d.ts.map +1 -0
- package/dist/components/date-picker-input.js +126 -0
- package/dist/components/date-picker-input.js.map +1 -0
- package/dist/components/date-time-picker.d.ts +27 -0
- package/dist/components/date-time-picker.d.ts.map +1 -0
- package/dist/components/date-time-picker.js +77 -0
- package/dist/components/date-time-picker.js.map +1 -0
- package/dist/components/dates.d.ts +94 -0
- package/dist/components/dates.d.ts.map +1 -0
- package/dist/components/dates.js +94 -0
- package/dist/components/dates.js.map +1 -0
- package/dist/components/time-input.d.ts +16 -0
- package/dist/components/time-input.d.ts.map +1 -0
- package/dist/components/time-input.js +18 -0
- package/dist/components/time-input.js.map +1 -0
- package/package.json +1 -1
- package/src/components/date-input.tsx +287 -0
- package/src/components/date-picker-input.tsx +287 -0
- package/src/components/date-time-picker.tsx +152 -0
- package/src/components/dates.tsx +274 -0
- package/src/components/time-input.tsx +34 -0
- package/dist/components/date-picker.d.ts +0 -78
- package/dist/components/date-picker.d.ts.map +0 -1
- package/dist/components/date-picker.js +0 -142
- package/dist/components/date-picker.js.map +0 -1
- package/src/components/date-picker.tsx +0 -414
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
// Derivado do DatePicker do Tremor (Apache-2.0) e modificado pela Strategicos.
|
|
4
|
+
// Ver THIRD_PARTY_NOTICES.md.
|
|
5
|
+
|
|
6
|
+
import * as React from "react"
|
|
7
|
+
|
|
8
|
+
import { useIsMobile } from "../hooks/use-mobile.js"
|
|
9
|
+
import { Calendar } from "./calendar.js"
|
|
10
|
+
import {
|
|
11
|
+
clampDate, formatDate, inRange, outOfRange, parseDateValue, toDateValue,
|
|
12
|
+
PickerFooter, PickerHint, PickerTrigger, PresetList, type PickerBaseProps,
|
|
13
|
+
} from "./dates.js"
|
|
14
|
+
import { useFieldControl } from "./field.js"
|
|
15
|
+
import { Popover, PopoverContent } from "./popover.js"
|
|
16
|
+
|
|
17
|
+
export type DatePreset = { label: string; value: string }
|
|
18
|
+
export type DateRangeValue = [string | null, string | null]
|
|
19
|
+
export type DateRangePreset = { label: string; value: [string, string] }
|
|
20
|
+
|
|
21
|
+
type SingleProps = PickerBaseProps & {
|
|
22
|
+
type?: "default"
|
|
23
|
+
value: string | null
|
|
24
|
+
onValueChange: (value: string | null) => void
|
|
25
|
+
presets?: DatePreset[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type RangeProps = PickerBaseProps & {
|
|
29
|
+
type: "range"
|
|
30
|
+
value: DateRangeValue
|
|
31
|
+
onValueChange: (value: DateRangeValue) => void
|
|
32
|
+
presets?: DateRangePreset[]
|
|
33
|
+
/** Meses lado a lado (no celular é sempre um). Padrão: 2. */
|
|
34
|
+
numberOfColumns?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const contentCls = "flex w-auto max-w-[calc(100vw-2rem)] flex-col p-0 sm:flex-row"
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Campo que abre um calendário para escolher uma data ou um período, como o
|
|
41
|
+
* DatePickerInput do Mantine. Não se digita nele — para digitar, `DateInput`.
|
|
42
|
+
*
|
|
43
|
+
* <Field label="Prazo">
|
|
44
|
+
* <DatePickerInput value={prazo} onValueChange={setPrazo} minDate={inicio} clearable />
|
|
45
|
+
* </Field>
|
|
46
|
+
*
|
|
47
|
+
* <DatePickerInput type="range" value={[inicio, fim]} onValueChange={([i, f]) => …} withApplyButton />
|
|
48
|
+
*
|
|
49
|
+
* - Valor: "AAAA-MM-DD" (ou `[início, fim]` no `type="range"`), `null` quando vazio.
|
|
50
|
+
* - `withApplyButton`: a escolha só vale no Aplicar; fechar de outro jeito
|
|
51
|
+
* descarta. Sem ele, escolher o dia (ou o fim do período) já grava e fecha.
|
|
52
|
+
* - `presets`: atalhos ao lado ("Hoje", "Fim do mês"…); os que caem fora de
|
|
53
|
+
* `minDate`/`maxDate` ficam desabilitados.
|
|
54
|
+
* - Período: o primeiro clique marca o início e o segundo, o fim; se o segundo
|
|
55
|
+
* vier antes, os dois trocam.
|
|
56
|
+
*
|
|
57
|
+
* Teclado: o calendário abre com o foco no dia escolhido (ou hoje); setas
|
|
58
|
+
* andam pelos dias, PageUp/PageDown pelos meses, Enter escolhe, Esc fecha.
|
|
59
|
+
*/
|
|
60
|
+
function DatePickerInput(props: SingleProps | RangeProps) {
|
|
61
|
+
return props.type === "range" ? <RangePicker {...props} /> : <SinglePicker {...props} />
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function SinglePicker({
|
|
65
|
+
type: _type,
|
|
66
|
+
value,
|
|
67
|
+
onValueChange,
|
|
68
|
+
placeholder = "Selecione a data",
|
|
69
|
+
minDate,
|
|
70
|
+
maxDate,
|
|
71
|
+
clearable = false,
|
|
72
|
+
withApplyButton = false,
|
|
73
|
+
presets,
|
|
74
|
+
valueFormatter = formatDate,
|
|
75
|
+
captionLayout,
|
|
76
|
+
size,
|
|
77
|
+
disabled,
|
|
78
|
+
className,
|
|
79
|
+
wrapperClassName,
|
|
80
|
+
align = "start",
|
|
81
|
+
onOpenChange,
|
|
82
|
+
...props
|
|
83
|
+
}: SingleProps) {
|
|
84
|
+
const controlProps = useFieldControl(props, { withRequired: false })
|
|
85
|
+
const [open, setOpen] = React.useState(false)
|
|
86
|
+
// Com o Aplicar, a escolha fica aqui até confirmar.
|
|
87
|
+
const [draft, setDraft] = React.useState<string | null>(value)
|
|
88
|
+
const shown = withApplyButton ? draft : value
|
|
89
|
+
|
|
90
|
+
const selected = parseDateValue(shown)
|
|
91
|
+
const committed = parseDateValue(value)
|
|
92
|
+
const min = parseDateValue(minDate)
|
|
93
|
+
const max = parseDateValue(maxDate)
|
|
94
|
+
|
|
95
|
+
function handleOpenChange(o: boolean) {
|
|
96
|
+
setOpen(o)
|
|
97
|
+
if (o) setDraft(value)
|
|
98
|
+
onOpenChange?.(o)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function pick(v: string) {
|
|
102
|
+
if (withApplyButton) { setDraft(v); return }
|
|
103
|
+
onValueChange(v)
|
|
104
|
+
handleOpenChange(false)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function apply() {
|
|
108
|
+
if (draft !== value) onValueChange(draft)
|
|
109
|
+
handleOpenChange(false)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
114
|
+
<PickerTrigger
|
|
115
|
+
slot="date-picker-input"
|
|
116
|
+
text={committed ? valueFormatter(committed) : null}
|
|
117
|
+
placeholder={placeholder}
|
|
118
|
+
open={open}
|
|
119
|
+
size={size}
|
|
120
|
+
disabled={disabled}
|
|
121
|
+
clearable={clearable}
|
|
122
|
+
onClear={() => onValueChange(null)}
|
|
123
|
+
className={className}
|
|
124
|
+
wrapperClassName={wrapperClassName}
|
|
125
|
+
controlProps={controlProps}
|
|
126
|
+
/>
|
|
127
|
+
<PopoverContent
|
|
128
|
+
align={align}
|
|
129
|
+
className={contentCls}
|
|
130
|
+
// O foco vai para o dia (autoFocus do calendário), não para o 1º botão.
|
|
131
|
+
onOpenAutoFocus={e => e.preventDefault()}
|
|
132
|
+
>
|
|
133
|
+
{presets && presets.length > 0 && (
|
|
134
|
+
<PresetList
|
|
135
|
+
presets={presets}
|
|
136
|
+
isActive={v => v === shown}
|
|
137
|
+
isDisabled={v => !inRange(v, minDate, maxDate)}
|
|
138
|
+
onSelect={pick}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
<div className="flex flex-col">
|
|
142
|
+
<Calendar
|
|
143
|
+
mode="single"
|
|
144
|
+
required
|
|
145
|
+
autoFocus
|
|
146
|
+
selected={selected}
|
|
147
|
+
onSelect={d => pick(toDateValue(d))}
|
|
148
|
+
defaultMonth={selected ?? clampDate(new Date(), min, max)}
|
|
149
|
+
startMonth={min}
|
|
150
|
+
endMonth={max}
|
|
151
|
+
disabled={outOfRange(min, max)}
|
|
152
|
+
captionLayout={captionLayout}
|
|
153
|
+
/>
|
|
154
|
+
{withApplyButton && (
|
|
155
|
+
<PickerFooter onCancel={() => handleOpenChange(false)} onApply={apply}>
|
|
156
|
+
<PickerHint>{selected ? valueFormatter(selected) : "Nenhuma data"}</PickerHint>
|
|
157
|
+
</PickerFooter>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
</PopoverContent>
|
|
161
|
+
</Popover>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function RangePicker({
|
|
166
|
+
type: _type,
|
|
167
|
+
value,
|
|
168
|
+
onValueChange,
|
|
169
|
+
placeholder = "Selecione o período",
|
|
170
|
+
minDate,
|
|
171
|
+
maxDate,
|
|
172
|
+
clearable = false,
|
|
173
|
+
withApplyButton = false,
|
|
174
|
+
presets,
|
|
175
|
+
numberOfColumns = 2,
|
|
176
|
+
valueFormatter = formatDate,
|
|
177
|
+
captionLayout,
|
|
178
|
+
size,
|
|
179
|
+
disabled,
|
|
180
|
+
className,
|
|
181
|
+
wrapperClassName,
|
|
182
|
+
align = "start",
|
|
183
|
+
onOpenChange,
|
|
184
|
+
...props
|
|
185
|
+
}: RangeProps) {
|
|
186
|
+
const controlProps = useFieldControl(props, { withRequired: false })
|
|
187
|
+
const isMobile = useIsMobile()
|
|
188
|
+
const [open, setOpen] = React.useState(false)
|
|
189
|
+
const [draft, setDraft] = React.useState<DateRangeValue>(value)
|
|
190
|
+
// Início escolhido nesta abertura, à espera do fim.
|
|
191
|
+
const [pendingFrom, setPendingFrom] = React.useState<Date | null>(null)
|
|
192
|
+
const shown = withApplyButton ? draft : value
|
|
193
|
+
|
|
194
|
+
const from = parseDateValue(shown[0])
|
|
195
|
+
const to = parseDateValue(shown[1])
|
|
196
|
+
const min = parseDateValue(minDate)
|
|
197
|
+
const max = parseDateValue(maxDate)
|
|
198
|
+
|
|
199
|
+
const format = (range: DateRangeValue) => {
|
|
200
|
+
const f = parseDateValue(range[0])
|
|
201
|
+
const t = parseDateValue(range[1])
|
|
202
|
+
return f ? `${valueFormatter(f)} – ${t ? valueFormatter(t) : "…"}` : null
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function handleOpenChange(o: boolean) {
|
|
206
|
+
setOpen(o)
|
|
207
|
+
setPendingFrom(null)
|
|
208
|
+
if (o) setDraft(value)
|
|
209
|
+
onOpenChange?.(o)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function commit(range: [string, string]) {
|
|
213
|
+
if (withApplyButton) { setDraft(range); setPendingFrom(null); return }
|
|
214
|
+
onValueChange(range)
|
|
215
|
+
handleOpenChange(false)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function pickDay(day: Date) {
|
|
219
|
+
if (!pendingFrom) { setPendingFrom(day); return }
|
|
220
|
+
const [s, e] = day < pendingFrom ? [day, pendingFrom] : [pendingFrom, day]
|
|
221
|
+
commit([toDateValue(s), toDateValue(e)])
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function apply() {
|
|
225
|
+
if (draft[0] !== value[0] || draft[1] !== value[1]) onValueChange(draft)
|
|
226
|
+
handleOpenChange(false)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const hint = pendingFrom
|
|
230
|
+
? "Agora escolha o último dia."
|
|
231
|
+
: withApplyButton && from
|
|
232
|
+
? format(shown)
|
|
233
|
+
: "Escolha o primeiro dia."
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
237
|
+
<PickerTrigger
|
|
238
|
+
slot="date-picker-input"
|
|
239
|
+
text={format(value)}
|
|
240
|
+
placeholder={placeholder}
|
|
241
|
+
open={open}
|
|
242
|
+
size={size}
|
|
243
|
+
disabled={disabled}
|
|
244
|
+
clearable={clearable}
|
|
245
|
+
onClear={() => onValueChange([null, null])}
|
|
246
|
+
className={className}
|
|
247
|
+
wrapperClassName={wrapperClassName}
|
|
248
|
+
controlProps={controlProps}
|
|
249
|
+
/>
|
|
250
|
+
<PopoverContent align={align} className={contentCls} onOpenAutoFocus={e => e.preventDefault()}>
|
|
251
|
+
{presets && presets.length > 0 && (
|
|
252
|
+
<PresetList
|
|
253
|
+
presets={presets}
|
|
254
|
+
isActive={v => v[0] === shown[0] && v[1] === shown[1]}
|
|
255
|
+
isDisabled={v => !inRange(v[0], minDate, maxDate) || !inRange(v[1], minDate, maxDate)}
|
|
256
|
+
onSelect={commit}
|
|
257
|
+
/>
|
|
258
|
+
)}
|
|
259
|
+
<div className="flex flex-col">
|
|
260
|
+
<Calendar
|
|
261
|
+
mode="range"
|
|
262
|
+
autoFocus
|
|
263
|
+
numberOfMonths={isMobile ? 1 : numberOfColumns}
|
|
264
|
+
// O cálculo do período é nosso (pickDay): o do DayPicker já fecha
|
|
265
|
+
// um período de um dia no primeiro clique.
|
|
266
|
+
selected={pendingFrom ? { from: pendingFrom, to: undefined } : { from, to }}
|
|
267
|
+
onSelect={(_, day) => pickDay(day)}
|
|
268
|
+
defaultMonth={from ?? clampDate(new Date(), min, max)}
|
|
269
|
+
startMonth={min}
|
|
270
|
+
endMonth={max}
|
|
271
|
+
disabled={outOfRange(min, max)}
|
|
272
|
+
captionLayout={captionLayout}
|
|
273
|
+
/>
|
|
274
|
+
<PickerFooter
|
|
275
|
+
onCancel={withApplyButton ? () => handleOpenChange(false) : undefined}
|
|
276
|
+
onApply={withApplyButton ? apply : undefined}
|
|
277
|
+
applyDisabled={!!pendingFrom}
|
|
278
|
+
>
|
|
279
|
+
<PickerHint>{hint}</PickerHint>
|
|
280
|
+
</PickerFooter>
|
|
281
|
+
</div>
|
|
282
|
+
</PopoverContent>
|
|
283
|
+
</Popover>
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export { DatePickerInput }
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
// Derivado do DatePicker do Tremor (Apache-2.0) e modificado pela Strategicos.
|
|
4
|
+
// Ver THIRD_PARTY_NOTICES.md.
|
|
5
|
+
|
|
6
|
+
import * as React from "react"
|
|
7
|
+
|
|
8
|
+
import { Calendar } from "./calendar.js"
|
|
9
|
+
import {
|
|
10
|
+
clampDate, formatDateTime, outOfRange, parseDateValue, toDateValue,
|
|
11
|
+
PickerFooter, PickerTrigger, type PickerBaseProps,
|
|
12
|
+
} from "./dates.js"
|
|
13
|
+
import { useFieldControl } from "./field.js"
|
|
14
|
+
import { Popover, PopoverContent } from "./popover.js"
|
|
15
|
+
import { TimeInput } from "./time-input.js"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Data e hora num calendário com campo de hora, como o DateTimePicker do
|
|
19
|
+
* Mantine. Para digitar a data e a hora direto, `DateInput withTime`.
|
|
20
|
+
*
|
|
21
|
+
* <Field label="Reunião">
|
|
22
|
+
* <DateTimePicker value={inicio} onValueChange={setInicio} />
|
|
23
|
+
* </Field>
|
|
24
|
+
*
|
|
25
|
+
* - Valor: "AAAA-MM-DD HH:mm:ss" ou `null`.
|
|
26
|
+
* - `withApplyButton` vem LIGADO (o botão de confirmar do Mantine): escolhe-se
|
|
27
|
+
* o dia e a hora e grava-se no Aplicar. Desligado, cada mudança já grava e o
|
|
28
|
+
* calendário fica aberto até sair dele.
|
|
29
|
+
* - `defaultTime`: hora usada quando ainda não há valor. Padrão: 00:00.
|
|
30
|
+
* - `minDate`/`maxDate` limitam o DIA ("AAAA-MM-DD").
|
|
31
|
+
*/
|
|
32
|
+
function DateTimePicker({
|
|
33
|
+
value,
|
|
34
|
+
onValueChange,
|
|
35
|
+
placeholder = "Selecione data e hora",
|
|
36
|
+
minDate,
|
|
37
|
+
maxDate,
|
|
38
|
+
clearable = false,
|
|
39
|
+
withApplyButton = true,
|
|
40
|
+
withSeconds = false,
|
|
41
|
+
defaultTime = "00:00",
|
|
42
|
+
valueFormatter = formatDateTime,
|
|
43
|
+
captionLayout,
|
|
44
|
+
size,
|
|
45
|
+
disabled,
|
|
46
|
+
className,
|
|
47
|
+
wrapperClassName,
|
|
48
|
+
align = "start",
|
|
49
|
+
onOpenChange,
|
|
50
|
+
...props
|
|
51
|
+
}: PickerBaseProps & {
|
|
52
|
+
value: string | null
|
|
53
|
+
onValueChange: (value: string | null) => void
|
|
54
|
+
/** Campo de hora com segundos. */
|
|
55
|
+
withSeconds?: boolean
|
|
56
|
+
/** "HH:mm" usada quando ainda não há valor. */
|
|
57
|
+
defaultTime?: string
|
|
58
|
+
}) {
|
|
59
|
+
const controlProps = useFieldControl(props, { withRequired: false })
|
|
60
|
+
const [open, setOpen] = React.useState(false)
|
|
61
|
+
const [draft, setDraft] = React.useState<string | null>(value)
|
|
62
|
+
// A hora existe antes do dia: dá para acertá-la e escolher o dia depois.
|
|
63
|
+
const [time, setTime] = React.useState(() => timeOf(value, defaultTime))
|
|
64
|
+
const shown = withApplyButton ? draft : value
|
|
65
|
+
|
|
66
|
+
const selected = parseDateValue(shown)
|
|
67
|
+
const committed = parseDateValue(value)
|
|
68
|
+
const min = parseDateValue(minDate)
|
|
69
|
+
const max = parseDateValue(maxDate)
|
|
70
|
+
|
|
71
|
+
function handleOpenChange(o: boolean) {
|
|
72
|
+
setOpen(o)
|
|
73
|
+
if (o) { setDraft(value); setTime(timeOf(value, defaultTime)) }
|
|
74
|
+
onOpenChange?.(o)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function update(v: string) {
|
|
78
|
+
if (withApplyButton) setDraft(v)
|
|
79
|
+
else onValueChange(v)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// `time` é sempre "HH:mm:ss" (timeOf e changeTime completam).
|
|
83
|
+
function pickDay(day: Date) {
|
|
84
|
+
update(`${toDateValue(day)} ${time}`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function changeTime(t: string) {
|
|
88
|
+
// O campo nativo devolve "" enquanto a hora está pela metade.
|
|
89
|
+
if (!/^\d{2}:\d{2}(:\d{2})?$/.test(t)) return
|
|
90
|
+
const full = t.length === 5 ? `${t}:00` : t
|
|
91
|
+
setTime(full)
|
|
92
|
+
if (shown) update(`${shown.slice(0, 10)} ${full}`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function apply() {
|
|
96
|
+
if (draft !== value) onValueChange(draft)
|
|
97
|
+
handleOpenChange(false)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Popover open={open} onOpenChange={handleOpenChange}>
|
|
102
|
+
<PickerTrigger
|
|
103
|
+
slot="date-time-picker"
|
|
104
|
+
text={committed ? valueFormatter(committed) : null}
|
|
105
|
+
placeholder={placeholder}
|
|
106
|
+
open={open}
|
|
107
|
+
size={size}
|
|
108
|
+
disabled={disabled}
|
|
109
|
+
clearable={clearable}
|
|
110
|
+
onClear={() => onValueChange(null)}
|
|
111
|
+
className={className}
|
|
112
|
+
wrapperClassName={wrapperClassName}
|
|
113
|
+
controlProps={controlProps}
|
|
114
|
+
/>
|
|
115
|
+
<PopoverContent align={align} className="w-auto max-w-[calc(100vw-2rem)] p-0" onOpenAutoFocus={e => e.preventDefault()}>
|
|
116
|
+
<Calendar
|
|
117
|
+
mode="single"
|
|
118
|
+
required
|
|
119
|
+
autoFocus
|
|
120
|
+
selected={selected}
|
|
121
|
+
onSelect={pickDay}
|
|
122
|
+
defaultMonth={selected ?? clampDate(new Date(), min, max)}
|
|
123
|
+
startMonth={min}
|
|
124
|
+
endMonth={max}
|
|
125
|
+
disabled={outOfRange(min, max)}
|
|
126
|
+
captionLayout={captionLayout}
|
|
127
|
+
/>
|
|
128
|
+
<PickerFooter
|
|
129
|
+
onCancel={withApplyButton ? () => handleOpenChange(false) : undefined}
|
|
130
|
+
onApply={withApplyButton ? apply : undefined}
|
|
131
|
+
applyDisabled={!draft}
|
|
132
|
+
>
|
|
133
|
+
<TimeInput
|
|
134
|
+
aria-label="Hora"
|
|
135
|
+
withSeconds={withSeconds}
|
|
136
|
+
value={withSeconds ? time : time.slice(0, 5)}
|
|
137
|
+
onChange={e => changeTime(e.target.value)}
|
|
138
|
+
className="w-32"
|
|
139
|
+
/>
|
|
140
|
+
</PickerFooter>
|
|
141
|
+
</PopoverContent>
|
|
142
|
+
</Popover>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** "HH:mm:ss" do valor, ou a hora padrão. */
|
|
147
|
+
function timeOf(value: string | null, defaultTime: string) {
|
|
148
|
+
const t = value && value.length >= 16 ? value.slice(11, 19) : defaultTime
|
|
149
|
+
return t.length === 5 ? `${t}:00` : t
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { DateTimePicker }
|