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 (
-
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({
{
- setFiltersNeedClear(true);
- setFilters({});
- }}
+ onClick={handleFiltersClear}
>
Clear filters
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" ? (
diff --git a/src/components/table/table-section.tsx b/src/components/table/table-section.tsx
index fac6902..f810963 100644
--- a/src/components/table/table-section.tsx
+++ b/src/components/table/table-section.tsx
@@ -90,15 +90,6 @@ export default function DBTable() {
() => (