@@ -286,26 +286,172 @@ export const ToolbarOverlay = ({ children, elements }: Props) => {
286286 </ ToolbarButton >
287287 < ToolbarButton
288288 isSmallScreen = { isSmallScreen }
289- style = { errorCount > 0 ? { color : "red" } : { } }
289+ style = { {
290+ position : "relative" ,
291+ ...( errorCount > 0 ? { color : "red" } : { } ) ,
292+ } }
290293 onClick = { ( ) => setErrorsOpen ( ! isErrorsOpen ) }
291- onMouseLeave = { ( ) => setErrorsOpen ( false ) }
292294 >
293295 < div > { errorCount } errors</ div >
294- { isErrorsOpen && (
295- < div
296- style = { { display : "grid" , gridTemplateColumns : "100px 300px" } }
297- >
298- { elements
299- ?. filter ( ( e ) : e is PcbTraceError => e . type . includes ( "error" ) )
300- . map ( ( e , i ) => (
301- < Fragment key = { i } >
302- < div > { e . error_type } </ div >
303- < div > { e . message } </ div >
304- </ Fragment >
305- ) ) }
306- </ div >
307- ) }
308296 </ ToolbarButton >
297+ { isErrorsOpen && (
298+ < div
299+ style = { {
300+ position : "absolute" ,
301+ top : "100%" ,
302+ left : 0 ,
303+ backgroundColor : "#2a2a2a" ,
304+ border : "1px solid #666" ,
305+ borderRadius : 4 ,
306+ marginTop : 4 ,
307+ zIndex : 1000 ,
308+ minWidth : isSmallScreen ? "280px" : "400px" ,
309+ maxWidth : isSmallScreen ? "90vw" : "600px" ,
310+ boxShadow : "0 4px 12px rgba(0, 0, 0, 0.3)" ,
311+ } }
312+ >
313+ { ( ( ) => {
314+ const errorElements =
315+ elements ?. filter ( ( el ) : el is PcbTraceError =>
316+ el . type . includes ( "error" ) ,
317+ ) || [ ]
318+ const errorCount = errorElements . length
319+
320+ return errorElements . map ( ( e , i ) => (
321+ < div
322+ key = { i }
323+ style = { {
324+ borderBottom :
325+ i < errorCount - 1 ? "1px solid #444" : "none" ,
326+ } }
327+ >
328+ < div
329+ style = { {
330+ display : "flex" ,
331+ alignItems : "center" ,
332+ gap : 12 ,
333+ padding : "12px 16px" ,
334+ cursor : "pointer" ,
335+ backgroundColor : "#2a2a2a" ,
336+ transition : "background-color 0.2s ease" ,
337+ touchAction : "manipulation" ,
338+ userSelect : "none" ,
339+ } }
340+ onMouseEnter = { ( e ) => {
341+ e . currentTarget . style . backgroundColor = "#333"
342+ } }
343+ onMouseLeave = { ( e ) => {
344+ e . currentTarget . style . backgroundColor = "#2a2a2a"
345+ } }
346+ onTouchStart = { ( e ) => {
347+ e . stopPropagation ( )
348+ e . currentTarget . style . backgroundColor = "#333"
349+ } }
350+ onTouchEnd = { ( e ) => {
351+ e . stopPropagation ( )
352+ e . preventDefault ( )
353+ e . currentTarget . style . backgroundColor = "#2a2a2a"
354+
355+ const errorElement = document . querySelector (
356+ `[data-error-id="${ i } "]` ,
357+ ) as HTMLElement
358+ const arrow = document . querySelector (
359+ `[data-arrow-id="${ i } "]` ,
360+ ) as HTMLElement
361+ if ( errorElement && arrow ) {
362+ const isVisible = errorElement . style . display !== "none"
363+ errorElement . style . display = isVisible
364+ ? "none"
365+ : "block"
366+ arrow . style . transform = isVisible
367+ ? "rotate(90deg)"
368+ : "rotate(0deg)"
369+ }
370+ } }
371+ onClick = { ( e ) => {
372+ e . stopPropagation ( )
373+ const errorElement = document . querySelector (
374+ `[data-error-id="${ i } "]` ,
375+ ) as HTMLElement
376+ const arrow = document . querySelector (
377+ `[data-arrow-id="${ i } "]` ,
378+ ) as HTMLElement
379+ if ( errorElement && arrow ) {
380+ const isVisible = errorElement . style . display !== "none"
381+ errorElement . style . display = isVisible
382+ ? "none"
383+ : "block"
384+ arrow . style . transform = isVisible
385+ ? "rotate(90deg)"
386+ : "rotate(0deg)"
387+ }
388+ } }
389+ >
390+ < div
391+ style = { {
392+ fontWeight : "bold" ,
393+ fontSize : isSmallScreen ? "12px" : "13px" ,
394+ whiteSpace : "nowrap" ,
395+ flexShrink : 0 ,
396+ color : "#ff6b6b" ,
397+ display : isSmallScreen ? "none" : "block" ,
398+ } }
399+ >
400+ { e . error_type }
401+ </ div >
402+ < div
403+ style = { {
404+ flex : 1 ,
405+ fontSize : isSmallScreen ? "12px" : "13px" ,
406+ color : "#ddd" ,
407+ lineHeight : 1.4 ,
408+ overflow : "hidden" ,
409+ textOverflow : "ellipsis" ,
410+ whiteSpace : "nowrap" ,
411+ } }
412+ >
413+ { e . message }
414+ </ div >
415+ < div
416+ data-arrow-id = { i }
417+ style = { {
418+ color : "#888" ,
419+ fontSize : "16px" ,
420+ transform : "rotate(90deg)" ,
421+ transition : "transform 0.2s ease" ,
422+ flexShrink : 0 ,
423+ } }
424+ >
425+ ›
426+ </ div >
427+ </ div >
428+ < div
429+ data-error-id = { i }
430+ style = { {
431+ display : "none" ,
432+ padding : "12px 16px" ,
433+ backgroundColor : "#1a1a1a" ,
434+ borderTop : "1px solid #444" ,
435+ } }
436+ >
437+ < div
438+ style = { {
439+ fontSize : isSmallScreen ? "11px" : "12px" ,
440+ color : "#ccc" ,
441+ lineHeight : 1.5 ,
442+ wordWrap : "break-word" ,
443+ overflowWrap : "break-word" ,
444+ hyphens : "auto" ,
445+ } }
446+ >
447+ { e . message }
448+ </ div >
449+ </ div >
450+ </ div >
451+ ) )
452+ } ) ( ) }
453+ </ div >
454+ ) }
309455 < ToolbarButton
310456 isSmallScreen = { isSmallScreen }
311457 style = { { } }
0 commit comments