diff --git a/src/component/TableLayout.tsx b/src/component/TableLayout.tsx deleted file mode 100644 index 762e04c..0000000 --- a/src/component/TableLayout.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { - ReactElement, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import type { FieldPacket, RowDataPacket } from 'mysql2/promise'; -import { useParams } from 'react-router-dom'; -import { useConfiguration } from '../contexts/ConfigurationContext'; -import { useDatabaseContext } from '../contexts/DatabaseContext'; -import { useConnectionContext } from '../contexts/ConnectionContext'; -import TableGrid from './TableGrid'; -import WhereFilter from './Query/WhereFilter'; -import { useTranslation } from '../i18n'; -import invariant from 'tiny-invariant'; -import { Button, Flex } from 'antd'; - -interface TableNameProps { - tableName: string; - database: string; - primaryKeys: Array; -} - -const DEFAULT_LIMIT = 100; - -function useTableHeight(): [number, React.RefObject] { - const [yTableScroll, setYTableScroll] = useState(0); - const resizeRef = useRef(null); - - const resizeObserver = useMemo( - () => - new ResizeObserver((entries) => { - const entry = entries[0]; - const divHeight = entry.borderBoxSize[0].blockSize; - - const tableHeader = - resizeRef.current?.querySelector('.ant-table-header'); - - const tableHeaderHeight = - tableHeader?.getBoundingClientRect().height ?? 0; - - // I don't know why we need to subtract 1 from the height, but if not, the div will have a scrollbar - setYTableScroll(divHeight - tableHeaderHeight - 1); - }), - [] - ); - - useEffect(() => { - const resizeRefCurrent = resizeRef.current; - - if (resizeRefCurrent) { - resizeObserver.observe(resizeRefCurrent); - } - - return () => { - if (resizeRefCurrent) { - resizeObserver.unobserve(resizeRefCurrent); - } - }; - }, [resizeObserver]); - - return [yTableScroll, resizeRef]; -} - -function TableLayout({ - tableName, - database, - primaryKeys, -}: TableNameProps): ReactElement { - const { t } = useTranslation(); - const { executeQuery } = useDatabaseContext(); - const [result, setResult] = useState(null); - const [fields, setFields] = useState(null); - const [error, setError] = useState(null); - const [currentOffset, setCurrentOffset] = useState(0); - const [where, setWhere] = useState(''); - - const [yTableScroll, resizeRef] = useTableHeight(); - - const fetchTableData = useCallback( - (offset: number) => { - const query = `SELECT * FROM ${database}.${tableName} ${ - where ? ` WHERE ${where}` : '' - } LIMIT ${DEFAULT_LIMIT} OFFSET ${offset};`; - - executeQuery(query) - .then(([result, fields]) => { - setCurrentOffset(offset); - setFields(fields || null); - setResult((prev) => - offset > 0 && prev ? prev.concat(result) : result - ); - }) - .catch((err) => { - setError(err); - }); - }, - [database, tableName, where, executeQuery] - ); - - useEffect(() => { - fetchTableData(currentOffset); - }, [fetchTableData, currentOffset]); - - if (error) { - return
{error.message}
; - } - - return ( - -
-

{tableName}

- - { - setCurrentOffset(0); - setWhere(where); - }} - /> -
- -
- -
- - - - -
- ); -} - -type Props = { primaryKeys: Array }; - -function TableGridWithConnection({ primaryKeys }: Props) { - const { currentConnectionName } = useConnectionContext(); - const { database } = useDatabaseContext(); - const { tableName } = useParams(); - const { updateConnectionState } = useConfiguration(); - - useEffect(() => { - invariant(currentConnectionName, 'Connection name is required'); - invariant(tableName, 'Table name is required'); - - updateConnectionState(currentConnectionName, 'openedTable', tableName); - }, [currentConnectionName, tableName, updateConnectionState]); - - if (!currentConnectionName || !database || !tableName) { - return null; - } - - return ( - - ); -} - -export default TableGridWithConnection; diff --git a/src/component/TableLayout/TableLayout.tsx b/src/component/TableLayout/TableLayout.tsx new file mode 100644 index 0000000..c5c7a22 --- /dev/null +++ b/src/component/TableLayout/TableLayout.tsx @@ -0,0 +1,94 @@ +import { ReactElement, useCallback, useEffect, useState } from 'react'; +import type { FieldPacket, RowDataPacket } from 'mysql2/promise'; +import { useDatabaseContext } from '../../contexts/DatabaseContext'; +import TableGrid from '../TableGrid'; +import WhereFilter from '../Query/WhereFilter'; +import { useTranslation } from '../../i18n'; +import { Button, Flex } from 'antd'; +import { useTableHeight } from './useTableHeight'; + +interface TableNameProps { + tableName: string; + database: string; + primaryKeys: Array; +} +const DEFAULT_LIMIT = 100; + +export function TableLayout({ + tableName, + database, + primaryKeys, +}: TableNameProps): ReactElement { + const { t } = useTranslation(); + const { executeQuery } = useDatabaseContext(); + const [result, setResult] = useState(null); + const [fields, setFields] = useState(null); + const [error, setError] = useState(null); + const [currentOffset, setCurrentOffset] = useState(0); + const [where, setWhere] = useState(''); + + const [yTableScroll, resizeRef] = useTableHeight(); + + const fetchTableData = useCallback( + (offset: number) => { + const query = `SELECT * FROM ${database}.${tableName} ${ + where ? ` WHERE ${where}` : '' + } LIMIT ${DEFAULT_LIMIT} OFFSET ${offset};`; + + executeQuery(query) + .then(([result, fields]) => { + setCurrentOffset(offset); + setFields(fields || null); + setResult((prev) => + offset > 0 && prev ? prev.concat(result) : result + ); + }) + .catch((err) => { + setError(err); + }); + }, + [database, tableName, where, executeQuery] + ); + + useEffect(() => { + fetchTableData(currentOffset); + }, [fetchTableData, currentOffset]); + + if (error) { + return
{error.message}
; + } + + return ( + +
+

{tableName}

+ + { + setCurrentOffset(0); + setWhere(where); + }} + /> +
+ +
+ +
+ + + + +
+ ); +} diff --git a/src/component/TableLayout/index.tsx b/src/component/TableLayout/index.tsx new file mode 100644 index 0000000..1213e26 --- /dev/null +++ b/src/component/TableLayout/index.tsx @@ -0,0 +1,38 @@ +import { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { useConfiguration } from '../../contexts/ConfigurationContext'; +import { useDatabaseContext } from '../../contexts/DatabaseContext'; +import { useConnectionContext } from '../../contexts/ConnectionContext'; +import invariant from 'tiny-invariant'; +import { TableLayout } from './TableLayout'; + +type Props = { primaryKeys: Array }; + +function TableLayoutPageContent({ primaryKeys }: Props) { + const { currentConnectionName } = useConnectionContext(); + const { database } = useDatabaseContext(); + const { tableName } = useParams(); + const { updateConnectionState } = useConfiguration(); + + useEffect(() => { + invariant(currentConnectionName, 'Connection name is required'); + invariant(tableName, 'Table name is required'); + + updateConnectionState(currentConnectionName, 'openedTable', tableName); + }, [currentConnectionName, tableName, updateConnectionState]); + + if (!currentConnectionName || !database || !tableName) { + return null; + } + + return ( + + ); +} + +export default TableLayoutPageContent; diff --git a/src/component/TableLayout/useTableHeight.tsx b/src/component/TableLayout/useTableHeight.tsx new file mode 100644 index 0000000..1a11985 --- /dev/null +++ b/src/component/TableLayout/useTableHeight.tsx @@ -0,0 +1,40 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; + +export function useTableHeight(): [number, React.RefObject] { + const [yTableScroll, setYTableScroll] = useState(0); + const resizeRef = useRef(null); + + const resizeObserver = useMemo( + () => + new ResizeObserver((entries) => { + const entry = entries[0]; + const divHeight = entry.borderBoxSize[0].blockSize; + + const tableHeader = + resizeRef.current?.querySelector('.ant-table-header'); + + const tableHeaderHeight = + tableHeader?.getBoundingClientRect().height ?? 0; + + // I don't know why we need to subtract 1 from the height, but if not, the div will have a scrollbar + setYTableScroll(divHeight - tableHeaderHeight - 1); + }), + [] + ); + + useEffect(() => { + const resizeRefCurrent = resizeRef.current; + + if (resizeRefCurrent) { + resizeObserver.observe(resizeRefCurrent); + } + + return () => { + if (resizeRefCurrent) { + resizeObserver.unobserve(resizeRefCurrent); + } + }; + }, [resizeObserver]); + + return [yTableScroll, resizeRef]; +}