Files
supabase/studio/components/interfaces/Database/Backups/PITR/TimeInput.tsx
T
b91532f6c7 Migrate to styling tokens (#18314)
* migrate some www components to tokens

* consolidate InteractiveShimmerCard to Panel component

* update tokens in blog

* update tokens in careers page

* update tokens in customers section

* update tokens in open-source section

* update tokens in Realtime page

* update tokens in Storage and Vector

* update tokens in SplitCodeBlockCarousel

* update tokens in PGCharts

* remove unused css files

* update tokens in Card

* update tokens in Pricing page

* clean up priving page imports

* remove hardcoded theme vars

* migrate first half of defaultTheme.ts to tokens

* migrate second half of defaultTheme.ts to tokens

* improve inputs

* add foreground to text-light and text-lighter

* add foreground to text-light and text-lighter

* migrate docs components with styling tokens

* migrate docs components with styling tokens

* fix broken Repos component

* fix broken classes in blog

* update tokens on Button and other components

* update tokens on IconPanel

* update studio main layout base styling tokens

* update tokens across studio, docs and www

* update tokens across studio, docs and www

* update ui/Panel to styling tokens

* update ExampleProject and TableEditorMenu tokens

* www vector page tokens

* update studio UI tokens

* update other studio UI tokens

* update more studio UI tokens

* change tokens here, change tokens there

* finish updating colors with tokens variables

* add gui sandbox for theme experimentation

* use common package for www, docs and studio and fix Command K tokens

* provide light mode default tokens options

* fix conflict leftover

* update loading line

* fix className typo

* fix prettier

* update themeSandbox preset default values

* fix text-background0

* prettier

* update warningBanner with warning color

* switch all border-border with border-default

* improve border-secondary and foreground-muted in light mode

* force ring color on toggle

* fix button bg color and border-muted light token

* fix input bg color

* fix dark button hover

* fix homepage product card

* fix code-hike table header colors

* button dark border

* remove tabIndex leftover from homepage main ctas

---------

Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2023-11-07 03:40:53 +00:00

190 lines
6.7 KiB
TypeScript

import * as Tooltip from '@radix-ui/react-tooltip'
import dayjs from 'dayjs'
import { isNaN, noop } from 'lodash'
import { ChangeEvent, useEffect, useState } from 'react'
import { IconClock } from 'ui'
import { Time } from './PITR.types'
import { formatNumberToTwoDigits, formatTimeToTimeString } from './PITR.utils'
// [Joshen] This is trying to do the same thing as TimeSplitInput.tsx
// so can we please look to try to combine these 2 components together if possible
// The problem with TimeSplitInput is that it's tightly coupled to the date + its
// tightly coupled to the context of a range. Ideally it should be more modular
// which is what this component is trying to achieve
// [Joshen] Potential extension, give option to toggle 24 hours or AM/PM
interface TimeInputProps {
defaultTime?: Time
minimumTime?: Time
maximumTime?: Time
onChange?: (time: Time) => void
}
const TimeInput = ({ defaultTime, minimumTime, maximumTime, onChange = noop }: TimeInputProps) => {
const [isFocused, setIsFocused] = useState(false)
const [error, setError] = useState<string>()
const [time, setTime] = useState<Time>(defaultTime || { h: 0, m: 0, s: 0 })
const formattedMinimumTime = minimumTime
? dayjs(formatTimeToTimeString(minimumTime), 'HH:mm:ss', true)
: undefined
const formattedMaximumTime = maximumTime
? dayjs(formatTimeToTimeString(maximumTime), 'HH:mm:ss', true)
: undefined
useEffect(() => {
if (minimumTime || maximumTime) validate(time)
}, [JSON.stringify(minimumTime), JSON.stringify(maximumTime)])
useEffect(() => {
if (defaultTime) {
setTime(defaultTime)
validate(defaultTime)
}
}, [defaultTime])
const validate = (time: Time) => {
let error = undefined
const formattedTime = dayjs(formatTimeToTimeString(time), 'HH:mm:ss', true)
if (!formattedTime.isValid()) {
error = 'Please enter a valid time'
} else if (formattedMinimumTime && formattedTime.isBefore(formattedMinimumTime)) {
error = 'Selected time is before the minimum time allowed'
} else if (formattedMaximumTime && formattedTime.isAfter(formattedMaximumTime)) {
error = 'Selected time is after the maximum time allowed'
}
setError(error)
return error === undefined
}
const onFocus = () => setIsFocused(true)
const onInputChange = (event: ChangeEvent<HTMLInputElement>, unit: 'h' | 'm' | 's') => {
if (isNaN(Number(event.target.value))) return
setTime({ ...time, [unit]: event.target.value })
}
const onInputBlur = (event: ChangeEvent<HTMLInputElement>, unit: 'h' | 'm' | 's') => {
const formattedInput = Number(event.target.value)
const updatedTime = { ...time, [unit]: formattedInput }
setTime(updatedTime)
validate(updatedTime)
onChange(updatedTime)
setIsFocused(false)
}
return (
<>
<div
className={[
'flex items-center justify-between transition',
'rounded-md bg-background border px-3.5 py-2 w-[200px]',
`${
isFocused ? 'border-stronger' : error === undefined ? 'border-strong' : 'border-red-800'
}`,
].join(' ')}
>
<IconClock className="text-foreground-light" size={18} strokeWidth={1.5} />
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger className="w-1/4" tabIndex={-1}>
<input
type="text"
maxLength={2}
pattern="[0-9]*"
placeholder="HH"
value={formatNumberToTwoDigits(time.h)}
onFocus={onFocus}
aria-label="Hours"
onBlur={(event) => onInputBlur(event, 'h')}
onChange={(event) => onInputChange(event, 'h')}
className="w-full text-sm bg-transparent p-0 text-center outline-none border-none focus:ring-0"
/>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'bg-alternative rounded py-1 px-2 leading-none shadow',
'border-background border',
].join(' ')}
>
<span className="text-foreground text-xs">Hours (HH)</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
<span>:</span>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger className="w-1/4" tabIndex={-1}>
<input
type="text"
maxLength={2}
pattern="[0-9]*"
placeholder="MM"
value={formatNumberToTwoDigits(time.m)}
onFocus={onFocus}
aria-label="Minutes"
onBlur={(event) => onInputBlur(event, 'm')}
onChange={(event) => onInputChange(event, 'm')}
className="w-full text-sm bg-transparent p-0 text-center outline-none border-none focus:ring-0"
/>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'bg-alternative rounded py-1 px-2 leading-none shadow',
'border-background border',
].join(' ')}
>
<span className="text-foreground text-xs">Minutes (MM)</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
<span>:</span>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger className="w-1/4" tabIndex={-1}>
<input
type="text"
maxLength={2}
pattern="[0-9]*"
placeholder="SS"
value={formatNumberToTwoDigits(time.s)}
onFocus={onFocus}
aria-label="Seconds"
onBlur={(event) => onInputBlur(event, 's')}
onChange={(event) => onInputChange(event, 's')}
className="w-full text-sm bg-transparent p-0 text-center outline-none border-none focus:ring-0"
/>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'bg-alternative rounded py-1 px-2 leading-none shadow',
'border-background border',
].join(' ')}
>
<span className="text-foreground text-xs">Seconds (SS)</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</div>
{error && <p className="text-sm text-red-900">{error}</p>}
</>
)
}
export default TimeInput