TableDefinition.tsx123 lines · main
| 1 | import Editor from '@monaco-editor/react' |
| 2 | import { useParams } from 'common' |
| 3 | import { useTheme } from 'next-themes' |
| 4 | import Link from 'next/link' |
| 5 | import { useMemo, useRef } from 'react' |
| 6 | import { Button } from 'ui' |
| 7 | import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' |
| 8 | |
| 9 | import { Footer } from '@/components/grid/components/footer/Footer' |
| 10 | import { useTableDefinitionQuery } from '@/data/database/table-definition-query' |
| 11 | import { useViewDefinitionQuery } from '@/data/database/view-definition-query' |
| 12 | import { Entity, isTableLike, isViewLike } from '@/data/table-editor/table-editor-types' |
| 13 | import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' |
| 14 | import { formatSql } from '@/lib/formatSql' |
| 15 | import { timeout } from '@/lib/helpers' |
| 16 | |
| 17 | export interface TableDefinitionProps { |
| 18 | entity?: Entity |
| 19 | } |
| 20 | |
| 21 | export const TableDefinition = ({ entity }: TableDefinitionProps) => { |
| 22 | const { ref } = useParams() |
| 23 | const editorRef = useRef(null) |
| 24 | const monacoRef = useRef(null) |
| 25 | const { resolvedTheme } = useTheme() |
| 26 | const { data: project } = useSelectedProjectQuery() |
| 27 | |
| 28 | const viewResult = useViewDefinitionQuery( |
| 29 | { |
| 30 | id: entity?.id, |
| 31 | includeCreateStatement: true, |
| 32 | projectRef: project?.ref, |
| 33 | connectionString: project?.connectionString, |
| 34 | }, |
| 35 | { |
| 36 | enabled: isViewLike(entity), |
| 37 | } |
| 38 | ) |
| 39 | |
| 40 | const tableResult = useTableDefinitionQuery( |
| 41 | { |
| 42 | id: entity?.id, |
| 43 | projectRef: project?.ref, |
| 44 | connectionString: project?.connectionString, |
| 45 | }, |
| 46 | { |
| 47 | enabled: isTableLike(entity), |
| 48 | } |
| 49 | ) |
| 50 | |
| 51 | const { data: definition, isLoading } = isViewLike(entity) ? viewResult : tableResult |
| 52 | |
| 53 | const formattedDefinition = useMemo( |
| 54 | () => (definition ? formatSql(definition) : undefined), |
| 55 | [definition] |
| 56 | ) |
| 57 | |
| 58 | const handleEditorOnMount = async (editor: any, monaco: any) => { |
| 59 | editorRef.current = editor |
| 60 | monacoRef.current = monaco |
| 61 | |
| 62 | // add margin above first line |
| 63 | editor.changeViewZones((accessor: any) => { |
| 64 | accessor.addZone({ |
| 65 | afterLineNumber: 0, |
| 66 | heightInPx: 4, |
| 67 | domNode: document.createElement('div'), |
| 68 | }) |
| 69 | }) |
| 70 | |
| 71 | // when editor did mount, it will need a delay before focus() works properly |
| 72 | await timeout(500) |
| 73 | editor?.focus() |
| 74 | } |
| 75 | |
| 76 | if (isLoading) { |
| 77 | return ( |
| 78 | <div className="h-full grid"> |
| 79 | <div className="p-4"> |
| 80 | <GenericSkeletonLoader /> |
| 81 | </div> |
| 82 | <div className="mt-auto"> |
| 83 | <Footer /> |
| 84 | </div> |
| 85 | </div> |
| 86 | ) |
| 87 | } |
| 88 | |
| 89 | return ( |
| 90 | <> |
| 91 | <div className="grow overflow-y-auto border-t border-muted relative"> |
| 92 | <Button asChild type="default" className="absolute top-2 right-5 z-10"> |
| 93 | <Link |
| 94 | href={`/project/${ref}/sql/new?content=${encodeURIComponent( |
| 95 | formattedDefinition ?? '' |
| 96 | )}`} |
| 97 | > |
| 98 | Open in SQL Editor |
| 99 | </Link> |
| 100 | </Button> |
| 101 | <Editor |
| 102 | className="monaco-editor" |
| 103 | theme={resolvedTheme?.includes('dark') ? 'vs-dark' : 'vs'} |
| 104 | onMount={handleEditorOnMount} |
| 105 | defaultLanguage="pgsql" |
| 106 | value={formattedDefinition} |
| 107 | path={''} |
| 108 | options={{ |
| 109 | domReadOnly: true, |
| 110 | readOnly: true, |
| 111 | tabSize: 2, |
| 112 | fontSize: 13, |
| 113 | minimap: { enabled: false }, |
| 114 | wordWrap: 'on', |
| 115 | fixedOverflowWidgets: true, |
| 116 | }} |
| 117 | /> |
| 118 | </div> |
| 119 | |
| 120 | <Footer /> |
| 121 | </> |
| 122 | ) |
| 123 | } |