Improve NameInput
This commit is contained in:
parent
e13b55dadb
commit
c72cd6a86e
2 changed files with 63 additions and 22 deletions
|
|
@ -1,13 +1,13 @@
|
|||
'use client'
|
||||
|
||||
import { ChangeEventHandler, type ComponentProps } from 'react'
|
||||
import type {
|
||||
CompositionEventHandler,
|
||||
FocusEventHandler,
|
||||
InputEventHandler,
|
||||
ComponentProps,
|
||||
} from 'react'
|
||||
import { Input } from './ui/input'
|
||||
import {
|
||||
type FieldPath,
|
||||
type FieldValues,
|
||||
type RegisterOptions,
|
||||
useFormContext,
|
||||
} from 'react-hook-form'
|
||||
import { type FieldPath, type FieldValues, useFormContext } from 'react-hook-form'
|
||||
|
||||
interface FormInpuProps<
|
||||
TFieldValues extends FieldValues,
|
||||
|
|
@ -25,22 +25,50 @@ export const FormInput = <
|
|||
name,
|
||||
format,
|
||||
sanitize,
|
||||
onChange,
|
||||
...props
|
||||
}: FormInpuProps<TFieldValues, TFieldName>) => {
|
||||
const { onChange: onValueChange, ...registerResult } = useFormContext<TFieldValues>().register(
|
||||
const { onChange, onBlur, ...registerResult } = useFormContext<TFieldValues>().register(
|
||||
name,
|
||||
sanitize ? { setValueAs: sanitize } : undefined,
|
||||
)
|
||||
|
||||
const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
if (format) {
|
||||
e.target.value = format(e.target.value)
|
||||
}
|
||||
|
||||
onValueChange(e)
|
||||
onChange?.(e)
|
||||
const handleUpdate = (e: EventTarget & HTMLInputElement, value: string) => {
|
||||
const selectionStart = e.selectionStart
|
||||
e.value = value
|
||||
e.setSelectionRange(selectionStart, selectionStart)
|
||||
}
|
||||
|
||||
return <Input {...props} {...registerResult} onChange={handleChange} />
|
||||
const handleInput: InputEventHandler<HTMLInputElement> = (e) => {
|
||||
if (e.nativeEvent.isComposing) {
|
||||
return
|
||||
}
|
||||
|
||||
if (format) {
|
||||
handleUpdate(e.currentTarget, format(e.currentTarget.value))
|
||||
}
|
||||
|
||||
onChange(e)
|
||||
}
|
||||
|
||||
const handleCompositionEnd: CompositionEventHandler<HTMLInputElement> | undefined = format
|
||||
? (e) => handleUpdate(e.currentTarget, format(e.currentTarget.value))
|
||||
: undefined
|
||||
|
||||
const handleBlur: FocusEventHandler<HTMLInputElement> = (e) => {
|
||||
handleUpdate(
|
||||
e.currentTarget,
|
||||
format?.(sanitize?.(e.currentTarget.value) ?? e.currentTarget.value) ?? e.currentTarget.value,
|
||||
)
|
||||
onBlur(e)
|
||||
}
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
{...registerResult}
|
||||
onInput={handleInput}
|
||||
onCompositionEnd={handleCompositionEnd}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,18 +10,31 @@ export const NameInput = (props: ComponentProps<'input'>) => (
|
|||
autoComplete="name"
|
||||
maxLength={120}
|
||||
minLength={2}
|
||||
format={sanitize}
|
||||
format={format}
|
||||
sanitize={sanitize}
|
||||
required
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
const sanitize = (value: string) =>
|
||||
const format = (value: string) =>
|
||||
value
|
||||
.normalize('NFC')
|
||||
.replace(/[^\p{L}\p{M}\s-]+/gu, '')
|
||||
.replace(/-+/g, '-')
|
||||
.trimStart()
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/\s*-\s*/g, '-')
|
||||
.trim()
|
||||
.replace(/-{2,}/g, '-')
|
||||
.replace(/(^|\s)-+/gu, '$1')
|
||||
.toLowerCase()
|
||||
.replace(/\b\p{L}+\b/gu, (word) =>
|
||||
connectors.has(word) ? word : word[0].toUpperCase() + word.slice(1),
|
||||
)
|
||||
.slice(0, 120)
|
||||
|
||||
const sanitize = (value: string) =>
|
||||
format(value)
|
||||
.trim()
|
||||
.replace(/(^|\s)-+/gu, '$1')
|
||||
.replace(/-(?!\p{L})/gu, '')
|
||||
|
||||
const connectors = new Set(['da', 'das', 'de', 'di', 'do', 'dos', 'du', 'e'])
|
||||
|
|
|
|||
Loading…
Reference in a new issue