feat(SW-3659): Use new input component * Use new input component * Update error formatter * Merged master into feat/use-new-input-component * Merged master into feat/use-new-input-component * Merge branch 'master' into feat/use-new-input-component * Merged master into feat/use-new-input-component * Update Input stories * Merge branch 'feat/use-new-input-component' of bitbucket.org:scandic-swap/web into feat/use-new-input-component * Update Storybook logo * Add some new demo icon input story * Fix the clear content button position * Fix broken password input icon * Merged master into feat/use-new-input-component * Merged master into feat/use-new-input-component * Add aria-hidden to required asterisk * Merge branch 'feat/use-new-input-component' of bitbucket.org:scandic-swap/web into feat/use-new-input-component * Merge branch 'master' into feat/use-new-input-component Approved-by: Bianca Widstam Approved-by: Matilda Landström
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
import type { ChangeEvent, ChangeEventHandler, RefObject } from 'react'
|
|
import { useState, useEffect } from 'react'
|
|
|
|
interface ClearInputOptions {
|
|
inputRef: RefObject<HTMLInputElement | null>
|
|
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
value?: string | number | readonly string[]
|
|
onRightIconClick?: () => void
|
|
}
|
|
|
|
/**
|
|
* Clears an input field value, handling both controlled and uncontrolled components.
|
|
* Works with React Aria Components Input which can be used standalone or within TextField.
|
|
*/
|
|
export function clearInput({
|
|
inputRef,
|
|
onChange,
|
|
value,
|
|
}: ClearInputOptions): void {
|
|
if (!inputRef.current) {
|
|
return
|
|
}
|
|
|
|
const isControlled = value !== undefined
|
|
|
|
if (isControlled && onChange) {
|
|
// For controlled components: call onChange with a synthetic event
|
|
const syntheticEvent = {
|
|
target: { value: '' },
|
|
currentTarget: inputRef.current,
|
|
} as ChangeEvent<HTMLInputElement>
|
|
onChange(syntheticEvent)
|
|
} else {
|
|
// For uncontrolled components: use native input value setter
|
|
// This triggers React's change detection properly
|
|
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
window.HTMLInputElement.prototype,
|
|
'value'
|
|
)?.set
|
|
|
|
if (nativeInputValueSetter) {
|
|
nativeInputValueSetter.call(inputRef.current, '')
|
|
} else {
|
|
inputRef.current.value = ''
|
|
}
|
|
|
|
// Dispatch input event to trigger any listeners
|
|
const inputEvent = new Event('input', {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
})
|
|
inputRef.current.dispatchEvent(inputEvent)
|
|
|
|
// Also dispatch change event
|
|
const changeEvent = new Event('change', {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
})
|
|
inputRef.current.dispatchEvent(changeEvent)
|
|
}
|
|
|
|
// Focus the input after clearing
|
|
inputRef.current.focus()
|
|
}
|
|
|
|
/**
|
|
* Hook to track whether an input has a value.
|
|
* Works for both controlled and uncontrolled components.
|
|
*/
|
|
export function useInputHasValue(
|
|
value: string | number | readonly string[] | undefined,
|
|
inputRef: RefObject<HTMLInputElement | null>
|
|
): boolean {
|
|
const [hasValue, setHasValue] = useState(() => {
|
|
// For controlled components, check value prop
|
|
if (value !== undefined) {
|
|
return String(value ?? '').trim().length > 0
|
|
}
|
|
// For uncontrolled, check ref
|
|
return String(inputRef.current?.value ?? '').trim().length > 0
|
|
})
|
|
|
|
// Sync with controlled value changes
|
|
useEffect(() => {
|
|
if (value !== undefined) {
|
|
setHasValue(String(value ?? '').trim().length > 0)
|
|
}
|
|
}, [value])
|
|
|
|
// For uncontrolled components, sync from ref on input events
|
|
useEffect(() => {
|
|
if (value !== undefined) {
|
|
return // Controlled component, no need to listen
|
|
}
|
|
|
|
const input = inputRef.current
|
|
if (!input) return
|
|
|
|
const updateHasValue = (event?: Event) => {
|
|
// Use setTimeout to ensure we read the value after React/React Aria Components
|
|
// has fully processed the change, especially for operations like select-all + delete
|
|
setTimeout(() => {
|
|
const target = (event?.target as HTMLInputElement) || inputRef.current
|
|
if (target) {
|
|
setHasValue((target.value ?? '').trim().length > 0)
|
|
}
|
|
}, 0)
|
|
}
|
|
|
|
input.addEventListener('input', updateHasValue)
|
|
// Also listen to change event as a fallback
|
|
input.addEventListener('change', updateHasValue)
|
|
return () => {
|
|
input.removeEventListener('input', updateHasValue)
|
|
input.removeEventListener('change', updateHasValue)
|
|
}
|
|
}, [value, inputRef])
|
|
|
|
return hasValue
|
|
}
|