1
+ import { Button , InputAdornment , TextField , Typography , Box } from '@mui/material' ;
2
+ import { Search as SearchIcon , Clear as ClearIcon , NavigateNext , NavigateBefore } from '@mui/icons-material' ;
3
+ import { useState , useCallback } from 'react' ;
4
+ import { styled } from '@mui/material/styles' ;
5
+ import { MotionBox } from '@/components/MotionComponents' ;
6
+
7
+ const SearchContainer = styled ( 'div' ) ( ( ) => ( {
8
+ position : 'sticky' ,
9
+ top : 0 ,
10
+ zIndex : 1 ,
11
+ gap : 5 ,
12
+ paddingBottom : 8 ,
13
+ alignItems : 'center' ,
14
+ backdropFilter : 'blur(10px)' ,
15
+ borderRadius : 5 ,
16
+ } ) ) ;
17
+
18
+ const SearchControls = styled ( Box ) ( ( { theme } ) => ( {
19
+ display : 'flex' ,
20
+ alignItems : 'center' ,
21
+ gap : theme . spacing ( 1 ) ,
22
+ marginTop : 8 ,
23
+ } ) ) ;
24
+
25
+ const StyledTextField = styled ( TextField ) ( ( { theme } ) => ( {
26
+ '& .MuiOutlinedInput-root' : {
27
+ backgroundColor : theme . palette . background . paper ,
28
+ transition : 'all 0.2s ease-in-out' ,
29
+ '&:hover' : {
30
+ backgroundColor : theme . palette . background . paper ,
31
+ boxShadow : `0 0 0 1px ${ theme . palette . primary . main } ` ,
32
+ } ,
33
+ '&.Mui-focused' : {
34
+ backgroundColor : theme . palette . background . paper ,
35
+ boxShadow : `0 0 0 2px ${ theme . palette . primary . main } ` ,
36
+ } ,
37
+ } ,
38
+ } ) ) ;
39
+
40
+ interface LogSearchProps {
41
+ onSearch : ( query : string ) => void ;
42
+ onNavigate : ( direction : 'prev' | 'next' ) => void ;
43
+ currentMatch : number ;
44
+ totalMatches : number ;
45
+ }
46
+
47
+ export const LogSearch = ( { onSearch, onNavigate, currentMatch, totalMatches } : LogSearchProps ) => {
48
+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
49
+
50
+ const handleSearchChange = useCallback (
51
+ ( event : React . ChangeEvent < HTMLInputElement > ) => {
52
+ const query = event . target . value ;
53
+ setSearchQuery ( query ) ;
54
+ onSearch ( query ) ;
55
+ } ,
56
+ [ onSearch ]
57
+ ) ;
58
+
59
+ const handleClear = useCallback ( ( ) => {
60
+ setSearchQuery ( '' ) ;
61
+ onSearch ( '' ) ;
62
+ } , [ onSearch ] ) ;
63
+
64
+ const handleKeyDown = useCallback (
65
+ ( event : React . KeyboardEvent ) => {
66
+ if ( event . key === 'Enter' && event . shiftKey ) {
67
+ onNavigate ( 'prev' ) ;
68
+ } else if ( event . key === 'Enter' ) {
69
+ onNavigate ( 'next' ) ;
70
+ }
71
+ } ,
72
+ [ onNavigate ]
73
+ ) ;
74
+
75
+ return (
76
+ < SearchContainer >
77
+ < StyledTextField
78
+ fullWidth
79
+ variant = "outlined"
80
+ placeholder = "Search logs..."
81
+ value = { searchQuery }
82
+ onChange = { handleSearchChange }
83
+ onKeyDown = { handleKeyDown }
84
+ InputProps = { {
85
+ startAdornment : (
86
+ < InputAdornment position = "start" >
87
+ < SearchIcon color = "action" />
88
+ </ InputAdornment >
89
+ ) ,
90
+ endAdornment : searchQuery && (
91
+ < InputAdornment position = "end" >
92
+ < ClearIcon
93
+ style = { { cursor : 'pointer' } }
94
+ onClick = { handleClear }
95
+ color = "action"
96
+ />
97
+ </ InputAdornment >
98
+ ) ,
99
+ } }
100
+ />
101
+ { searchQuery && totalMatches > 0 && (
102
+ < MotionBox
103
+ initial = { { y : 20 , opacity : 0 } }
104
+ animate = { { y : 0 , opacity : 1 } }
105
+ exit = { { y : 20 , opacity : 0 } }
106
+ transition = { {
107
+ type : 'tween' ,
108
+ ease : 'easeOut' ,
109
+ duration : 0.3 ,
110
+ } }
111
+ >
112
+ < SearchControls >
113
+ < Button
114
+ size = "small"
115
+ onClick = { ( ) => onNavigate ( 'prev' ) }
116
+ disabled = { currentMatch === 1 }
117
+ startIcon = { < NavigateBefore /> }
118
+ sx = { {
119
+ minWidth : '20px' ,
120
+ padding : '4px 8px' ,
121
+ } }
122
+ />
123
+ < Typography variant = "body2" color = "text.secondary" >
124
+ { currentMatch } of { totalMatches }
125
+ </ Typography >
126
+ < Button
127
+ size = "small"
128
+ onClick = { ( ) => onNavigate ( 'next' ) }
129
+ disabled = { currentMatch === totalMatches }
130
+ startIcon = { < NavigateNext /> }
131
+ sx = { {
132
+ minWidth : '20px' ,
133
+ padding : '4px 8px' ,
134
+ } }
135
+ />
136
+ </ SearchControls >
137
+ </ MotionBox >
138
+ ) }
139
+ </ SearchContainer >
140
+ ) ;
141
+ } ;
0 commit comments