11import { css } from '@emotion/react'
22import * as ToggleGroup from '@radix-ui/react-toggle-group'
33import { Flex , Separator , Text , VisuallyHidden } from '@radix-ui/themes'
4- import { useCallback , useMemo , useState } from 'react'
4+ import { ReactNode , useMemo , useState } from 'react'
55
6- import { useAutoScroll } from '@/hooks/useAutoScroll'
76import { LogEntry } from '@/schemas/k6'
87
98import { AutoScrollArea } from '../AutoScrollArea'
@@ -31,20 +30,6 @@ function formatTime(time: string) {
3130 } )
3231}
3332
34- function findTableElement ( element : HTMLElement ) : HTMLTableElement | null {
35- let current : HTMLElement | null = element
36-
37- while ( current !== null ) {
38- if ( current instanceof HTMLTableElement ) {
39- return current
40- }
41-
42- current = current . parentElement
43- }
44-
45- return null
46- }
47-
4833/**
4934 * LogEntry has a source property but it's not really reliable and doesn't let us
5035 * distinguish between logs from the browser module and logs from the actual browser,
@@ -74,7 +59,7 @@ function withSource(entry: LogEntry) {
7459const colors : Record < LogEntry [ 'level' ] , string > = {
7560 info : 'green' ,
7661 debug : 'blue' ,
77- warning : 'orange ' ,
62+ warning : 'yellow ' ,
7863 error : 'red' ,
7964}
8065
@@ -99,15 +84,13 @@ const toggleGroupStyles = css`
9984const toggleItemStyles = css `
10085 box-sizing : border-box;
10186 padding : var (--space-1 ) var (--space-2 );
102- font-size : 12px ;
103- font-weight : 500 ;
87+ font-size : var (--font-size-2 );
10488 border : none;
10589 border-radius : var (--radius-2 );
10690 cursor : pointer;
10791 color : var (--gray-11 );
10892 background-color : transparent;
10993
110- & : hover,
11194 & [data-state = 'on' ] {
11295 background-color : var (--gray-a4 );
11396 }
@@ -125,6 +108,125 @@ export function useConsoleFilter() {
125108 }
126109}
127110
111+ function LogMessage ( { children } : { children : ReactNode } ) {
112+ return (
113+ < Text size = "2" color = "gray" asChild >
114+ < Flex align = "center" justify = "center" height = "100%" flexGrow = "1" >
115+ { children }
116+ </ Flex >
117+ </ Text >
118+ )
119+ }
120+
121+ interface LogsContentProps {
122+ filter : ConsoleFilter
123+ logs : LogEntry [ ]
124+ }
125+
126+ function LogsContent ( { filter, logs } : LogsContentProps ) {
127+ const filteredLogs = useMemo ( ( ) => {
128+ return logs . map ( withSource ) . filter ( ( log ) => {
129+ return (
130+ filter . levels . includes ( log . entry . level ) &&
131+ filter . sources . includes ( log . source )
132+ )
133+ } )
134+ } , [ logs , filter ] )
135+
136+ if ( logs . length === 0 ) {
137+ return < LogMessage > No logs available.</ LogMessage >
138+ }
139+
140+ if ( filteredLogs . length === 0 ) {
141+ return < LogMessage > No logs match the filter.</ LogMessage >
142+ }
143+
144+ return (
145+ < table
146+ css = { css `
147+ display : grid;
148+ align-items : center;
149+ grid-template-columns : 1fr auto auto;
150+
151+ thead ,
152+ tbody ,
153+ tr {
154+ display : grid;
155+ grid-column : 1 / -1 ;
156+ grid-template-columns : subgrid;
157+ }
158+
159+ tr {
160+ padding-right : var (--space-2 );
161+ }
162+
163+ td {
164+ font-family : var (--code-font-family );
165+ font-size : 13px ;
166+ padding : var (--space-2 ) var (--space-1 );
167+ }
168+ ` }
169+ >
170+ < thead >
171+ < tr
172+ css = { css `
173+ height : 0 ;
174+ padding : 0 ;
175+ margin : 0 ;
176+ ` }
177+ >
178+ < th >
179+ < VisuallyHidden > Message</ VisuallyHidden >
180+ </ th >
181+ < th >
182+ < VisuallyHidden > Source</ VisuallyHidden >
183+ </ th >
184+ < th >
185+ < VisuallyHidden > Time</ VisuallyHidden >
186+ </ th >
187+ </ tr >
188+ </ thead >
189+ < tbody >
190+ { filteredLogs . map ( ( { source, entry } , index ) => (
191+ < tr key = { index } >
192+ < td
193+ css = { css `
194+ border-left : 4px solid var (--${ colors [ entry . level ] } -9 );
195+ & & {
196+ padding-left : var (--space-2 );
197+ }
198+ ` }
199+ >
200+ < pre
201+ css = { css `
202+ margin : 0 ;
203+ ` }
204+ >
205+ { entry . msg }
206+ </ pre >
207+ </ td >
208+ < Text
209+ asChild
210+ css = { css `
211+ color : var (--gray-a9 );
212+ ` }
213+ >
214+ < td align = "right" > [{ source } ]</ td >
215+ </ Text >
216+ < Text
217+ asChild
218+ css = { css `
219+ color : var (--gray-a9 );
220+ ` }
221+ >
222+ < td align = "right" > { formatTime ( entry . time ) } </ td >
223+ </ Text >
224+ </ tr >
225+ ) ) }
226+ </ tbody >
227+ </ table >
228+ )
229+ }
128230interface ConsoleFilter {
129231 levels : Array < LogEntry [ 'level' ] >
130232 sources : Array < ReturnType < typeof getSource > >
@@ -149,38 +251,6 @@ export function LogsSection({
149251 autoScroll,
150252 onFilterChange,
151253} : LogsSectionProps ) {
152- const ref = useAutoScroll < HTMLTableElement > ( logs , autoScroll )
153-
154- const filteredLogs = useMemo ( ( ) => {
155- return logs . map ( withSource ) . filter ( ( log ) => {
156- return (
157- filter . levels . includes ( log . entry . level ) &&
158- filter . sources . includes ( log . source )
159- )
160- } )
161- } , [ logs , filter ] )
162-
163- // Radix UI's Table component wraps the table element in a ScrollArea but in
164- // order to autoscroll we need to get a ref to the table element. Ideally Radix
165- // would provide a way to do this, but instead we have to get the ref to an
166- // inner element and find the table element from there.
167- const setTableRef = useCallback (
168- ( element : HTMLTableSectionElement | null ) => {
169- if ( element === null ) {
170- return
171- }
172-
173- const table = findTableElement ( element )
174-
175- if ( table === null ) {
176- return
177- }
178-
179- ref . current = table
180- } ,
181- [ ref ]
182- )
183-
184254 const handleLogLevelsChange = ( values : string [ ] ) => {
185255 onFilterChange ( {
186256 ...filter ,
@@ -205,10 +275,19 @@ export function LogsSection({
205275 gap = "2"
206276 minHeight = "40px"
207277 css = { css `
278+ font-size : var (--font-size-2 );
279+ line-height : 1em ;
208280 flex-shrink : 0 ;
209281 border-bottom : 1px solid var (--gray-a6 );
210282 ` }
211283 >
284+ < div
285+ css = { css `
286+ padding : var (--space-1 ) 0 ;
287+ ` }
288+ >
289+ Filters:
290+ </ div >
212291 < ToggleGroup . Root
213292 type = "multiple"
214293 value = { filter . levels }
@@ -282,95 +361,8 @@ export function LogsSection({
282361 ) }
283362 </ ToggleGroup . Root >
284363 </ Flex >
285- < AutoScrollArea tail items = { filteredLogs . length } >
286- < Flex height = "100%" >
287- < table
288- css = { css `
289- height : 100% ;
290- flex : 1 ;
291-
292- display : grid;
293- align-items : center;
294- grid-template-columns : 1fr auto auto;
295-
296- thead ,
297- tbody ,
298- tr {
299- display : grid;
300- grid-column : 1 / -1 ;
301- grid-template-columns : subgrid;
302- }
303-
304- tr {
305- padding-right : var (--space-2 );
306- }
307-
308- td {
309- font-family : var (--code-font-family );
310- font-size : 13px ;
311- padding : var (--space-2 ) var (--space-1 );
312- }
313- ` }
314- >
315- < thead ref = { setTableRef } >
316- < tr
317- css = { css `
318- height : 0 ;
319- padding : 0 ;
320- margin : 0 ;
321- ` }
322- >
323- < th >
324- < VisuallyHidden > Message</ VisuallyHidden >
325- </ th >
326- < th >
327- < VisuallyHidden > Source</ VisuallyHidden >
328- </ th >
329- < th >
330- < VisuallyHidden > Time</ VisuallyHidden >
331- </ th >
332- </ tr >
333- </ thead >
334- < tbody >
335- { filteredLogs . map ( ( { source, entry } , index ) => (
336- < tr key = { index } >
337- < td
338- css = { css `
339- border-left : 3px solid var (--${ colors [ entry . level ] } -9 );
340- & & {
341- padding-left : var (--space-2 );
342- }
343- ` }
344- >
345- < pre
346- css = { css `
347- margin : 0 ;
348- ` }
349- >
350- { entry . msg }
351- </ pre >
352- </ td >
353- < Text
354- asChild
355- css = { css `
356- color : var (--gray-a9 );
357- ` }
358- >
359- < td align = "right" > [{ source } ]</ td >
360- </ Text >
361- < Text
362- asChild
363- css = { css `
364- color : var (--gray-a9 );
365- ` }
366- >
367- < td align = "right" > { formatTime ( entry . time ) } </ td >
368- </ Text >
369- </ tr >
370- ) ) }
371- </ tbody >
372- </ table >
373- </ Flex >
364+ < AutoScrollArea tail = { autoScroll } items = { logs . length } >
365+ < LogsContent filter = { filter } logs = { logs } />
374366 </ AutoScrollArea >
375367 </ Flex >
376368 )
0 commit comments