Files
web/packages/design-system/lib/components/Input/utils.ts
Rasmus Langvad b9a62b5280 Merged in feat/use-new-input-component (pull request #3324)
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
2025-12-18 15:42:09 +00:00

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
}