diff --git a/src/components/landing/features.tsx b/src/components/landing/features.tsx index 7f3eecb..d4c4cfb 100644 --- a/src/components/landing/features.tsx +++ b/src/components/landing/features.tsx @@ -13,17 +13,19 @@ interface FeatureProps { icon: React.ElementType; } -const Feature = ({ icon: Icon, title, description }: FeatureProps) => ( -
-
- -
-
-

{title}

-

{description}

+function Feature({ icon: Icon, title, description }: FeatureProps) { + return ( +
+
+ +
+
+

{title}

+

{description}

+
-
-); + ); +} export default function Features() { return ( diff --git a/src/components/landing/proxy-message.tsx b/src/components/landing/proxy-message.tsx index 31eb97f..5dec3ce 100644 --- a/src/components/landing/proxy-message.tsx +++ b/src/components/landing/proxy-message.tsx @@ -1,3 +1,5 @@ +import { useCallback } from "react"; + import { AlertDialog, AlertDialogAction, @@ -20,6 +22,10 @@ export default function ProxyMessage({ setShowDialog, fn }: ProxyMessageProps) { + const handleClose = useCallback(() => { + setShowDialog(false); + }, [setShowDialog]); + return ( @@ -34,9 +40,7 @@ export default function ProxyMessage({ Using the proxy may expose your database to corsproxy.io services.
- setShowDialog(false)}> - Cancel - + Cancel Confirm diff --git a/src/components/table/page-select.tsx b/src/components/table/page-select.tsx index 82a82ec..1f3852d 100644 --- a/src/components/table/page-select.tsx +++ b/src/components/table/page-select.tsx @@ -1,3 +1,4 @@ +import { useMemo, useCallback } from "react"; import useSQLiteStore from "@/store/useSQLiteStore"; import { Button } from "@/components/ui/button"; @@ -16,39 +17,41 @@ export default function PageSelect({ rowsPerPage }: PageSelectProps) { const { totalRows } = useSQLiteStore(); - const totalPages = Math.ceil(totalRows / rowsPerPage); - const currentPage = Math.floor(page / rowsPerPage) + 1; - const nextPage = () => { - if (currentPage < totalPages) { - setPage(page + rowsPerPage); + const totalPages = useMemo( + () => Math.ceil(totalRows / rowsPerPage), + [totalRows, rowsPerPage] + ); + const currentPage = useMemo( + () => Math.ceil(page / rowsPerPage) + 1, + [page, rowsPerPage] + ); + + const canGoNext = currentPage < totalPages; + const canGoPrev = currentPage > 1; + + const nextPage = useCallback(() => { + if (canGoNext) { + setPage((prevPage) => prevPage + rowsPerPage); } - }; + }, [canGoNext, rowsPerPage, setPage]); - const prevPage = () => { - if (currentPage > 1) { - setPage(page - rowsPerPage); + const prevPage = useCallback(() => { + if (canGoPrev) { + setPage((prevPage) => prevPage - rowsPerPage); } - }; + }, [canGoPrev, rowsPerPage, setPage]); return (
- Page {currentPage} of {totalPages} -
diff --git a/src/components/table/repl.tsx b/src/components/table/repl.tsx index 7143ad3..f9a6db8 100644 --- a/src/components/table/repl.tsx +++ b/src/components/table/repl.tsx @@ -1,12 +1,13 @@ -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import useSQLiteStore from "@/store/useSQLiteStore"; import useTheme from "@/hooks/useTheme"; import { format } from "sql-formatter"; +import { sql, SQLite } from "@codemirror/lang-sql"; import CodeMirror from "@uiw/react-codemirror"; -import { sql, SQLite } from "@codemirror/lang-sql"; import { autocompletion, CompletionContext } from "@codemirror/autocomplete"; + import { nord } from "@uiw/codemirror-theme-nord"; // SQL Keywords used for autocompletion @@ -43,41 +44,46 @@ export default function SqlRepl() { const { customQuery, setCustomQuery, queryHistory } = useSQLiteStore(); const isDark = useTheme(); - // Format query on change + // update customQuery formatted on queryHistory change useEffect(() => { setCustomQuery(sqlFormat(customQuery)); }, [queryHistory]); + useEffect(() => { + console.log(customQuery); + }, [customQuery]); + const myCompletions = useCallback((context: CompletionContext) => { const word = context.matchBefore(/\w*/); if (!word || (word.from === word.to && !context.explicit)) return null; - return { from: word.from, to: word.to, - options: [ - ...KEYWORDS.map((keyword) => ({ label: keyword, type: "keyword" })) - ] + options: KEYWORDS.map((keyword) => ({ label: keyword, type: "keyword" })) }; }, []); const handleBlur = useCallback(() => { setCustomQuery(sqlFormat(customQuery)); - }, [customQuery, sqlFormat]); + }, [customQuery, setCustomQuery]); - const handleChange = useCallback((newValue: string) => { - setCustomQuery(newValue); - }, []); + const handleChange = useCallback( + (newValue: string) => { + setCustomQuery(newValue); + }, + [setCustomQuery] + ); + + const extensions = useMemo( + () => [SQLite, sql(), autocompletion({ override: [myCompletions] })], + [myCompletions] + ); return ( { + setFiltersNeedClear(true); + setFilters({}); + }, [setFiltersNeedClear, setFilters]); + return (
@@ -150,10 +155,7 @@ export default function DBTableComponent({ diff --git a/src/components/table/table-operations.tsx b/src/components/table/table-operations.tsx index a01500c..d8b0063 100644 --- a/src/components/table/table-operations.tsx +++ b/src/components/table/table-operations.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import useSQLiteStore from "@/store/useSQLiteStore"; import { Input } from "@/components/ui/input"; @@ -18,11 +18,12 @@ export function TableFilter({ columnName }: { columnName: string }) { } = useSQLiteStore(); const [inputValue, setInputValue] = useState(""); - // reset the input value when the table changes + // Reset the input value when the table changes useEffect(() => { setInputValue(""); }, [selectedTable]); + // Clear filters when required useEffect(() => { if (filtersNeedClear) { setInputValue(""); @@ -30,15 +31,19 @@ export function TableFilter({ columnName }: { columnName: string }) { } }, [filtersNeedClear]); - const handleInputChange = (e: React.ChangeEvent) => { - setInputValue(e.target.value); - appendToFilters(columnName, e.target.value); - }; + const handleFilterChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + setInputValue(value); + appendToFilters(columnName, value); + }, + [appendToFilters, columnName] + ); return ( @@ -49,21 +54,25 @@ export function TableOrderBy({ columnName }: { columnName: string }) { const { orderBy, setOrderBy, orderByDirection, setOrderByDirection } = useSQLiteStore(); - const handleButtonClick = () => { + const handleOrderByClick = useCallback(() => { if (orderBy === columnName) { if (orderByDirection === "ASC") { setOrderByDirection("DESC"); + } else if (orderByDirection === "DESC") { + setOrderBy(null); + setOrderByDirection("ASC"); } else { + setOrderBy(columnName); setOrderByDirection("ASC"); } } else { - setOrderBy(columnName); + setOrderBy(columnName); // Start sorting with ASC for the new column setOrderByDirection("ASC"); } - }; + }, [orderBy, orderByDirection, columnName, setOrderBy, setOrderByDirection]); return ( -
+
{orderBy === columnName ? ( orderByDirection === "ASC" ? (