11'use client' ;
2- import React from 'react' ;
3- import { useEffect , useRef } from 'react' ;
2+ import React , { useEffect , useRef } from 'react' ;
43
54import { tString , useLanguage } from '@/intl/client' ;
6- import { tcls } from '@/lib/tailwind' ;
7- import { Icon } from '@gitbook/icons' ;
8- import { Button , variantClasses } from '../primitives' ;
5+ import { Input } from '../primitives/Input' ;
96import { KeyboardShortcut } from '../primitives/KeyboardShortcut' ;
10- import { useClassnames } from '../primitives/StyleProvider' ;
117
128interface SearchInputProps {
139 onChange : ( value : string ) => void ;
@@ -20,14 +16,11 @@ interface SearchInputProps {
2016 children ?: React . ReactNode ;
2117}
2218
23- // Size classes for medium size button
24- const sizeClasses = [ 'text-sm' , 'px-3.5' , 'py-1.5' , '@2xl:circular-corners:px-4' ] ;
25-
2619/**
2720 * Input to trigger search.
2821 */
2922export const SearchInput = React . forwardRef < HTMLDivElement , SearchInputProps > (
30- function SearchInput ( props , ref ) {
23+ function SearchInput ( props , containerRef ) {
3124 const {
3225 onChange,
3326 onKeyDown,
@@ -42,7 +35,6 @@ export const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(
4235 const inputRef = useRef < HTMLInputElement > ( null ) ;
4336
4437 const language = useLanguage ( ) ;
45- const buttonStyles = useClassnames ( [ 'ButtonStyles' ] ) ;
4638
4739 useEffect ( ( ) => {
4840 if ( isOpen ) {
@@ -58,75 +50,32 @@ export const SearchInput = React.forwardRef<HTMLDivElement, SearchInputProps>(
5850 } , [ isOpen , value ] ) ;
5951
6052 return (
61- < div className = { tcls ( 'relative flex size-9 grow' , className ) } >
62- { /* biome-ignore lint/a11y/useKeyWithClickEvents: this div needs an onClick to show the input on mobile, where it's normally hidden.
63- Normally you'd also need to add a keyboard trigger to do the same without a pointer, but in this case the input already be focused on its own. */ }
64- < div
65- ref = { ref }
66- onClick = { onFocus }
67- className = { tcls (
68- // Apply button styles
69- buttonStyles ,
70- variantClasses . header ,
71- sizeClasses ,
72- // Additional custom styles
73- 'has-[input:focus]:-translate-y-px h-9 grow @2xl:cursor-text cursor-pointer px-2.5 has-[input:focus]:bg-tint-base has-[input:focus]:depth-subtle:shadow-lg has-[input:focus]:depth-subtle:shadow-primary-subtle has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-primary-hover' ,
74- 'theme-bold:border-header-link/3 has-[input:focus-visible]:theme-bold:border-header-link/5 has-[input:focus-visible]:theme-bold:bg-header-link/3 has-[input:focus-visible]:theme-bold:ring-header-link/5' ,
75- 'theme-bold:before:absolute theme-bold:before:inset-0 theme-bold:before:bg-header-background/7 theme-bold:before:backdrop-blur-xl ' , // Special overlay to make the transparent colors of theme-bold visible.
76- '@max-2xl:absolute relative @max-2xl:right-0 z-30 max-w-none shrink grow justify-start' ,
77- isOpen ? '@max-2xl:w-56' : '@max-2xl:w-[38px]'
78- ) }
79- >
80- { value && isOpen ? (
81- < Button
82- variant = "blank"
83- label = { tString ( language , 'clear' ) }
84- size = "medium"
85- iconOnly
86- icon = "circle-xmark"
87- className = "-ml-1.5 -mr-1 animate-scale-in px-1.5 theme-bold:text-header-link theme-bold:hover:bg-header-link/3"
88- onClick = { ( ) => {
89- onChange ( '' ) ;
90- inputRef . current ?. focus ( ) ;
91- } }
92- />
93- ) : (
94- < Icon
95- icon = "magnifying-glass"
96- className = "size-4 shrink-0 animate-scale-in"
97- />
98- ) }
99- { children }
100- < input
101- { ...rest }
102- type = "text"
103- onFocus = { onFocus }
104- onKeyDown = { onKeyDown }
105- onChange = { ( event ) => onChange ( event . target . value ) }
106- value = { value }
107- // We only show "search or ask" if the search input actually handles both search and ask.
108- placeholder = { `${ tString ( language , withAI ? 'search_or_ask' : 'search' ) } …` }
109- maxLength = { 512 }
110- size = { 10 }
111- data-testid = "search-input"
112- className = { tcls (
113- 'peer z-10 min-w-0 grow bg-transparent py-0.5 text-tint-strong theme-bold:text-header-link outline-hidden transition-[width] duration-300 contain-paint placeholder:text-tint theme-bold:placeholder:text-current theme-bold:placeholder:opacity-7' ,
114- isOpen ? '' : '@max-2xl:opacity-0'
115- ) }
116- role = "combobox"
117- autoComplete = "off"
118- aria-autocomplete = "list"
119- aria-haspopup = "listbox"
120- aria-expanded = { value && isOpen ? 'true' : 'false' }
121- // Forward
122- ref = { inputRef }
123- />
124- < KeyboardShortcut
125- keys = { isOpen ? [ 'esc' ] : [ 'mod' , 'k' ] }
126- className = "last:-mr-1 theme-bold:border-header-link/5 theme-bold:bg-header-background theme-bold:text-header-link"
127- />
128- </ div >
129- </ div >
53+ < Input
54+ data-testid = "search-input"
55+ name = "search-input"
56+ ref = { inputRef }
57+ containerRef = { containerRef as React . RefObject < HTMLDivElement | null > }
58+ sizing = "small"
59+ label = { tString ( language , withAI ? 'search_or_ask' : 'search' ) }
60+ className = "grow"
61+ placeholder = { `${ tString ( language , withAI ? 'search_or_ask' : 'search' ) } …` }
62+ onFocus = { onFocus }
63+ onKeyDown = { onKeyDown }
64+ leading = "magnifying-glass"
65+ onChange = { ( event ) => {
66+ onChange ( event . target . value ) ;
67+ } }
68+ value = { value }
69+ maxLength = { 512 }
70+ autoComplete = "off"
71+ aria-autocomplete = "list"
72+ aria-haspopup = "listbox"
73+ aria-expanded = { value && isOpen ? 'true' : 'false' }
74+ clearButton
75+ keyboardShortcut = { < KeyboardShortcut keys = { isOpen ? [ 'esc' ] : [ 'mod' , 'k' ] } /> }
76+ { ...rest }
77+ type = "text"
78+ />
13079 ) ;
13180 }
13281) ;
0 commit comments