feat: cadastro manual de acesso remoto e ajustes de horas
This commit is contained in:
parent
8e3cbc7a9a
commit
f3a7045691
16 changed files with 1549 additions and 207 deletions
|
|
@ -1,44 +1,72 @@
|
|||
'use client'
|
||||
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { MinusIcon, PlusIcon } from 'lucide-react'
|
||||
|
||||
import { Button, Group, Input, Label, NumberField } from 'react-aria-components'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
|
||||
const clamp = (value: number, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) =>
|
||||
Math.min(Math.max(value, min), max)
|
||||
|
||||
const InputWithEndButtonsDemo = () => {
|
||||
const [value, setValue] = useState<number>(1024)
|
||||
const minValue = 0
|
||||
|
||||
const formattedValue = useMemo(() => (Number.isFinite(value) ? value.toString() : ''), [value])
|
||||
|
||||
const handleManualChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const digitsOnly = event.target.value.replace(/\D/g, '')
|
||||
if (digitsOnly.length === 0) {
|
||||
setValue(minValue)
|
||||
return
|
||||
}
|
||||
const next = Number.parseInt(digitsOnly, 10)
|
||||
setValue(clamp(next, minValue))
|
||||
},
|
||||
[minValue],
|
||||
)
|
||||
|
||||
const handleIncrement = useCallback(() => setValue((current) => clamp(current + 1, minValue)), [minValue])
|
||||
const handleDecrement = useCallback(() => setValue((current) => clamp(current - 1, minValue)), [minValue])
|
||||
|
||||
return (
|
||||
<NumberField defaultValue={1024} minValue={0} className='w-full max-w-xs space-y-2'>
|
||||
<Label className='flex items-center gap-2 text-sm leading-none font-medium select-none'>
|
||||
<div className='w-full max-w-xs space-y-2'>
|
||||
<Label className='flex items-center gap-2 text-sm font-medium leading-none text-muted-foreground'>
|
||||
Input with end buttons
|
||||
</Label>
|
||||
<Group className='dark:bg-input/30 border-input data-focus-within:border-ring data-focus-within:ring-ring/50 data-focus-within:has-aria-invalid:ring-destructive/20 dark:data-focus-within:has-aria-invalid:ring-destructive/40 data-focus-within:has-aria-invalid:border-destructive relative inline-flex h-9 w-full min-w-0 items-center overflow-hidden rounded-md border bg-transparent text-base whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50 data-focus-within:ring-[3px] md:text-sm'>
|
||||
<Input className='selection:bg-primary selection:text-primary-foreground w-full grow px-3 py-2 text-center tabular-nums outline-none' />
|
||||
<div className='dark:bg-input/30 relative inline-flex h-9 w-full min-w-0 items-center overflow-hidden rounded-md border border-input bg-transparent text-base shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/40 md:text-sm'>
|
||||
<Input
|
||||
className='selection:bg-primary selection:text-primary-foreground w-full grow px-3 py-2 text-center tabular-nums outline-none'
|
||||
inputMode='numeric'
|
||||
pattern='[0-9]*'
|
||||
value={formattedValue}
|
||||
onChange={handleManualChange}
|
||||
/>
|
||||
<Button
|
||||
slot='decrement'
|
||||
className='border-input bg-background text-muted-foreground hover:bg-accent hover:text-foreground -me-px flex aspect-square h-[inherit] items-center justify-center border text-sm transition-[color,box-shadow] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50'
|
||||
type='button'
|
||||
variant='ghost'
|
||||
className='border-input text-muted-foreground hover:bg-accent hover:text-foreground -me-px flex aspect-square h-[inherit] items-center justify-center rounded-none border-l text-sm transition-[color,box-shadow]'
|
||||
onClick={handleDecrement}
|
||||
disabled={value <= minValue}
|
||||
>
|
||||
<MinusIcon className='size-4' />
|
||||
<span className='sr-only'>Decrement</span>
|
||||
</Button>
|
||||
<Button
|
||||
slot='increment'
|
||||
className='border-input bg-background text-muted-foreground hover:bg-accent hover:text-foreground -me-px flex aspect-square h-[inherit] items-center justify-center rounded-r-md border text-sm transition-[color,box-shadow] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50'
|
||||
type='button'
|
||||
variant='ghost'
|
||||
className='border-input text-muted-foreground hover:bg-accent hover:text-foreground flex aspect-square h-[inherit] items-center justify-center rounded-none border-l text-sm transition-[color,box-shadow]'
|
||||
onClick={handleIncrement}
|
||||
>
|
||||
<PlusIcon className='size-4' />
|
||||
<span className='sr-only'>Increment</span>
|
||||
</Button>
|
||||
</Group>
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
Built with{' '}
|
||||
<a
|
||||
className='hover:text-foreground underline'
|
||||
href='https://react-spectrum.adobe.com/react-aria/NumberField.html'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
React Aria
|
||||
</a>
|
||||
</p>
|
||||
</NumberField>
|
||||
</div>
|
||||
<p className='text-xs text-muted-foreground'>Demonstração simples de input numérico com botões de incremento.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue