Skip to content

Commit

Permalink
chore: added SearchInput to components package
Browse files Browse the repository at this point in the history
  • Loading branch information
UrbanWill committed Jan 3, 2025
1 parent 9f1b7e5 commit f6107f8
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/talisman-ui/src/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChevronDownIcon } from "@talismn/icons"
import { classNames } from "@talismn/util"
import { ReactNode } from "react"

import { SearchInput } from "@talisman/components/SearchInput"
import { SearchInput } from "./SearchInput"

export type DropdownOption = Record<string, unknown>

Expand Down
114 changes: 114 additions & 0 deletions packages/talisman-ui/src/components/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { SearchIcon, XIcon } from "@talismn/icons"
import { classNames } from "@talismn/util"
import {
ChangeEventHandler,
FC,
KeyboardEventHandler,
ReactNode,
useCallback,
useDeferredValue,
useEffect,
useMemo,
useRef,
useState,
} from "react"
import { FormFieldInputContainerProps, FormFieldInputText, IconButton } from "talisman-ui"

const INPUT_CONTAINER_PROPS: FormFieldInputContainerProps = {
small: true,
className: "!px-8 h-[4.6rem] my-1 !bg-black-tertiary",
}

type SearchInputProps = {
small?: boolean
className?: string
containerClassName?: string
autoFocus?: boolean
placeholder?: string
initialValue?: string
after?: ReactNode
disabled?: boolean
onChange?: (search: string) => void
onSubmit?: () => void
}

export const SearchInput: FC<SearchInputProps> = ({
className,
containerClassName,
small,
autoFocus,
placeholder,
initialValue,
after,
disabled,
onChange,
onSubmit,
}) => {
const ref = useRef<HTMLInputElement>(null)
const [syncSearch, setSearch] = useState(initialValue ?? "")
const search = useDeferredValue(syncSearch)

const handleSearchChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(e) => {
setSearch(e.target.value)
},
[setSearch],
)

const handleKeyUp: KeyboardEventHandler<HTMLInputElement> = useCallback(
(e) => {
if (e.key === "Enter") {
onSubmit?.()
}
},
[onSubmit],
)

useEffect(() => {
onChange?.(search)
}, [onChange, search])

useEffect(() => {
// set focus after render to prevent appear animations from flickering
if (autoFocus) ref.current?.focus()
}, [autoFocus])

const containerProps = useMemo(
() => ({
small: small === undefined ? INPUT_CONTAINER_PROPS.small : small,
className: classNames(INPUT_CONTAINER_PROPS.className, containerClassName),
}),
[containerClassName, small],
)

const handleClear = useCallback(() => {
if (!ref.current) return
setSearch("")
ref.current.value = ""
ref.current.blur()
}, [setSearch])

return (
<FormFieldInputText
ref={ref}
className={classNames("text-base", className)}
containerProps={containerProps}
before={<SearchIcon className="text-body-disabled shrink-0" />}
after={
after ?? (
<IconButton
onClick={handleClear}
className={classNames(search ? "visible" : "invisible")}
>
<XIcon />
</IconButton>
)
}
defaultValue={initialValue}
placeholder={placeholder}
disabled={disabled}
onChange={handleSearchChange}
onKeyUp={handleKeyUp}
/>
)
}

0 comments on commit f6107f8

Please sign in to comment.