mirror of
https://github.com/supabase/supabase.git
synced 2026-06-20 18:26:00 +08:00
Add SQL syntax highlighting to Query Performance page (#38386)
* Add CodeBlock for SQL query display to add syntax highlight * use monaco editor for better highlighting * create `SqlMonacoBlock` component for SQL in detail panel * add mean_time to most_time_comsuming queries * make outline transparent * Add keyboard navigation for query performance grid * Stop default RDG behavior on arrow key navigation * Update column minWidth values to remove horizontal scrollbar * Load SqlMonacoBlock and Editor client-side only - following a pattern in `SQLEditor.tsx`
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { ArrowDown, ArrowUp, TextSearch, X } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import DataGrid, { Column, DataGridHandle, Row } from 'react-data-grid'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { DbQueryHook } from 'hooks/analytics/useDbQuery'
|
||||
@@ -31,6 +32,11 @@ interface QueryPerformanceGridProps {
|
||||
queryPerformanceQuery: DbQueryHook<any>
|
||||
}
|
||||
|
||||
// Load the monaco editor client-side only (does not behave well server-side)
|
||||
const Editor = dynamic(() => import('@monaco-editor/react').then(({ Editor }) => Editor), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformanceGridProps) => {
|
||||
const router = useRouter()
|
||||
const gridRef = useRef<DataGridHandle>(null)
|
||||
@@ -74,7 +80,7 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance
|
||||
const value = props.row?.[col.id]
|
||||
if (col.id === 'query') {
|
||||
return (
|
||||
<div className="w-full flex items-center gap-x-2">
|
||||
<div className="w-full flex items-center gap-x-2 pointer-events-none">
|
||||
{hasIndexRecommendations(props.row.index_advisor_result, true) && (
|
||||
<IndexSuggestionIcon
|
||||
indexAdvisorResult={props.row.index_advisor_result}
|
||||
@@ -85,7 +91,38 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="font-mono text-xs">{value}</div>
|
||||
<Editor
|
||||
height={20}
|
||||
theme="supabase"
|
||||
language="pgsql"
|
||||
value={value.replace(/\s+/g, ' ').trim()}
|
||||
wrapperProps={{
|
||||
className:
|
||||
'[&_.monaco-editor]:!bg-transparent [&_.monaco-editor-background]:!bg-transparent [&_.monaco-editor]:!outline-transparent',
|
||||
}}
|
||||
options={{
|
||||
readOnly: true,
|
||||
domReadOnly: true,
|
||||
cursorBlinking: 'solid',
|
||||
tabIndex: -1,
|
||||
fontSize: 12,
|
||||
minimap: { enabled: false },
|
||||
lineNumbers: 'off',
|
||||
renderLineHighlight: 'none',
|
||||
scrollbar: { vertical: 'hidden', horizontal: 'hidden' },
|
||||
overviewRulerLanes: 0,
|
||||
overviewRulerBorder: false,
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
wordWrap: 'off',
|
||||
scrollBeyondLastLine: false,
|
||||
contextmenu: false,
|
||||
selectionHighlight: false,
|
||||
occurrencesHighlight: 'off',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -170,6 +207,43 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance
|
||||
setSelectedRow(undefined)
|
||||
}, [preset, search, roles, urlSort, order])
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event: KeyboardEvent) => {
|
||||
if (!reportData.length || selectedRow === undefined) return
|
||||
|
||||
if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return
|
||||
|
||||
// stop default RDG behavior (which moves focus to header when selectedRow is 0)
|
||||
event.stopPropagation()
|
||||
|
||||
let nextIndex = selectedRow
|
||||
if (event.key === 'ArrowUp' && selectedRow > 0) {
|
||||
nextIndex = selectedRow - 1
|
||||
} else if (event.key === 'ArrowDown' && selectedRow < reportData.length - 1) {
|
||||
nextIndex = selectedRow + 1
|
||||
}
|
||||
|
||||
if (nextIndex !== selectedRow) {
|
||||
setSelectedRow(nextIndex)
|
||||
gridRef.current?.scrollToCell({ idx: 0, rowIdx: nextIndex })
|
||||
|
||||
const rowQuery = reportData[nextIndex]?.query ?? ''
|
||||
if (!rowQuery.trim().toLowerCase().startsWith('select')) {
|
||||
setView('details')
|
||||
}
|
||||
}
|
||||
},
|
||||
[reportData, selectedRow]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// run before RDG to prevent header focus (the third param: true)
|
||||
window.addEventListener('keydown', handleKeyDown, true)
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown, true)
|
||||
}
|
||||
}, [handleKeyDown])
|
||||
|
||||
return (
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
|
||||
Reference in New Issue
Block a user