From 718fc832283b2db860a1a24d85bfab04754dc70a Mon Sep 17 00:00:00 2001 From: "Visal .In" Date: Tue, 11 Mar 2025 08:39:54 +0700 Subject: [PATCH 1/2] combine useConfig and useDatabaseDriver into one context --- src/components/gui/connection-dialog.tsx | 6 +- src/components/gui/database-gui.tsx | 21 +++-- src/components/gui/main-connection.tsx | 7 +- src/components/gui/query-result-table.tsx | 7 +- src/components/gui/save-doc-button.tsx | 12 +-- .../column-default-value-input.tsx | 4 +- src/components/gui/schema-editor/index.tsx | 4 +- .../schema-create/schema-create-form.tsx | 88 +++++++++++-------- .../schema-editor-column-list.tsx | 4 +- .../gui/schema-editor/schema-save-dialog.tsx | 4 +- src/components/gui/schema-sidebar-list.tsx | 6 +- src/components/gui/schema-sidebar.tsx | 40 ++++----- src/components/gui/sidebar-tab.tsx | 10 +-- .../saved-doc-tab/create-namespace-button.tsx | 6 +- .../gui/sidebar/saved-doc-tab/index.tsx | 36 ++++---- .../saved-doc-tab/remove-doc-dialog.tsx | 4 +- .../saved-doc-tab/remove-namespace-dialog.tsx | 4 +- .../saved-doc-tab/rename-namespace-dialog.tsx | 6 +- src/components/gui/studio.tsx | 28 +++--- .../gui/table-cell/generic-cell.tsx | 12 +-- .../table-combobox/TableColumnCombobox.tsx | 4 +- .../gui/table-result/context-menu.tsx | 14 +-- .../gui/tabs-result/explain-result-tab.tsx | 8 +- .../gui/tabs-result/query-result-tab.tsx | 10 +-- src/components/gui/tabs/mass-drop-table.tsx | 38 ++++---- src/components/gui/tabs/query-tab.tsx | 7 +- .../context-menu-diagram.tsx | 8 +- src/components/gui/tabs/schema-editor-tab.tsx | 4 +- src/components/gui/tabs/table-data-tab.tsx | 6 +- src/context/config-provider.tsx | 35 -------- src/context/driver-provider.tsx | 40 ++++++--- src/context/schema-provider.tsx | 6 +- .../data-catalog/data-catalog-tab.tsx | 4 +- .../data-catalog-table-column-modal.tsx | 4 +- .../data-catalog/data-model-tab.tsx | 4 +- src/extensions/dolt/dolt-create-branch.tsx | 10 +-- src/extensions/dolt/dolt-sidebar.tsx | 10 +-- .../trigger-editor/trigger-editor.tsx | 4 +- .../trigger-editor/trigger-save-dialog.tsx | 30 +++---- src/extensions/trigger-editor/trigger-tab.tsx | 10 +-- src/extensions/view-editor/view-editor.tsx | 10 +-- src/extensions/view-editor/view-tab.tsx | 14 +-- 42 files changed, 284 insertions(+), 305 deletions(-) delete mode 100644 src/context/config-provider.tsx diff --git a/src/components/gui/connection-dialog.tsx b/src/components/gui/connection-dialog.tsx index c502e853..f65b6432 100644 --- a/src/components/gui/connection-dialog.tsx +++ b/src/components/gui/connection-dialog.tsx @@ -1,5 +1,5 @@ import { Button } from "@/components/ui/button"; -import { useConfig } from "@/context/config-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useMemo } from "react"; import ServerLoadingAnimation from "../icons/server-loading"; @@ -11,7 +11,7 @@ export default function ConnectingDialog({ message?: string; onRetry?: () => void; }>) { - const { name, onBack } = useConfig(); + const { name, onBack } = useStudioContext(); const isElectron = useMemo(() => { return typeof window !== "undefined" && window.outerbaseIpc; @@ -56,7 +56,7 @@ export default function ConnectingDialog({ } return ( -
+
{body}
diff --git a/src/components/gui/database-gui.tsx b/src/components/gui/database-gui.tsx index 404ccf7c..6bd94c5d 100644 --- a/src/components/gui/database-gui.tsx +++ b/src/components/gui/database-gui.tsx @@ -1,29 +1,28 @@ "use client"; +import QueryWindow from "@/components/gui/tabs/query-tab"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import WindowTabs, { WindowTabItemProps } from "./windows-tab"; -import QueryWindow from "@/components/gui/tabs/query-tab"; -import SidebarTab, { SidebarTabItem } from "./sidebar-tab"; import SchemaView from "./schema-sidebar"; +import SidebarTab, { SidebarTabItem } from "./sidebar-tab"; import ToolSidebar from "./sidebar/tools-sidebar"; +import WindowTabs, { WindowTabItemProps } from "./windows-tab"; -import { useDatabaseDriver } from "@/context/driver-provider"; -import SavedDocTab from "./sidebar/saved-doc-tab"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; -import { Binoculars, GearSix, Table } from "@phosphor-icons/react"; -import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking"; -import { useConfig } from "@/context/config-provider"; -import { cn } from "@/lib/utils"; import { scc } from "@/core/command"; import { tabCloseChannel, tabOpenChannel, tabReplaceChannel, } from "@/core/extension-tab"; +import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking"; +import { cn } from "@/lib/utils"; +import { Binoculars, GearSix, Table } from "@phosphor-icons/react"; +import SavedDocTab from "./sidebar/saved-doc-tab"; export default function DatabaseGui() { const DEFAULT_WIDTH = 300; @@ -34,8 +33,8 @@ export default function DatabaseGui() { setDefaultWidthPercentage((DEFAULT_WIDTH / window.innerWidth) * 100); }, []); - const { databaseDriver, docDriver } = useDatabaseDriver(); - const { extensions, containerClassName } = useConfig(); + const { databaseDriver, docDriver, extensions, containerClassName } = + useStudioContext(); const [selectedTabIndex, setSelectedTabIndex] = useState(0); const { currentSchemaName } = useSchema(); diff --git a/src/components/gui/main-connection.tsx b/src/components/gui/main-connection.tsx index 9477d5f7..d13316b7 100644 --- a/src/components/gui/main-connection.tsx +++ b/src/components/gui/main-connection.tsx @@ -2,8 +2,7 @@ import { TooltipProvider } from "@/components/ui/tooltip"; import { WEBSITE_NAME } from "@/const"; import { AutoCompleteProvider } from "@/context/auto-complete-provider"; -import { useConfig } from "@/context/config-provider"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { SchemaProvider } from "@/context/schema-provider"; import { useEffect } from "react"; import { DialogProvider } from "../create-dialog"; @@ -11,7 +10,7 @@ import ContextMenuHandler from "./context-menu-handler"; import DatabaseGui from "./database-gui"; function MainConnection() { - const { databaseDriver: driver } = useDatabaseDriver(); + const { databaseDriver: driver } = useStudioContext(); useEffect(() => { return () => { @@ -28,7 +27,7 @@ function MainConnection() { } function MainConnectionContainer() { - const { name } = useConfig(); + const { name } = useStudioContext(); useEffect(() => { document.title = name + " - " + WEBSITE_NAME; diff --git a/src/components/gui/query-result-table.tsx b/src/components/gui/query-result-table.tsx index 1bce90c4..c4008e4b 100644 --- a/src/components/gui/query-result-table.tsx +++ b/src/components/gui/query-result-table.tsx @@ -2,7 +2,7 @@ import OptimizeTable, { OptimizeTableHeaderWithIndexProps, } from "@/components/gui/table-optimized"; import OptimizeTableState from "@/components/gui/table-optimized/OptimizeTableState"; -import { useConfig } from "@/context/config-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { ColumnSortOption } from "@/drivers/base-driver"; import { exportDataAsDelimitedText } from "@/lib/export-helper"; import { KEY_BINDING } from "@/lib/key-matcher"; @@ -52,7 +52,8 @@ function Header({ if (internalState.getSelectedColIndex().includes(colIndex)) { if (internalState.isFullSelectionCol(colIndex)) { - textClass = "grow line-clamp-1 font-mono font-bold text-black dark:text-white font-bold"; + textClass = + "grow line-clamp-1 font-mono font-bold text-black dark:text-white font-bold"; thClass = "flex grow items-center px-2 overflow-hidden bg-neutral-100 dark:bg-neutral-900"; } else { @@ -126,7 +127,7 @@ export default function ResultTable({ }: ResultTableProps) { const [stickyHeaderIndex, setStickHeaderIndex] = useState(); - const { extensions } = useConfig(); + const { extensions } = useStudioContext(); const headerIndex = useMemo(() => { if (visibleColumnIndexList) return visibleColumnIndexList; diff --git a/src/components/gui/save-doc-button.tsx b/src/components/gui/save-doc-button.tsx index 601eee5a..7c8e356b 100644 --- a/src/components/gui/save-doc-button.tsx +++ b/src/components/gui/save-doc-button.tsx @@ -1,12 +1,12 @@ +import { useStudioContext } from "@/context/driver-provider"; import { SavedDocData, SavedDocInput, SavedDocNamespace, } from "@/drivers/saved-doc/saved-doc-driver"; -import { Button } from "../ui/button"; -import { useDatabaseDriver } from "@/context/driver-provider"; -import { useCallback, useState } from "react"; import { LucideFolderGit, LucideLoader } from "lucide-react"; +import { useCallback, useState } from "react"; +import { Button } from "../ui/button"; import { DropdownMenu, DropdownMenuContent, @@ -29,7 +29,7 @@ export default function SaveDocButton({ }: Props) { const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [namespaceList, setNamespaceList] = useState([]); const onSaveQuery = useCallback( @@ -86,7 +86,7 @@ export default function SaveDocButton({ variant={"secondary"} > {loading ? ( - + ) : ( <> )} @@ -104,7 +104,7 @@ export default function SaveDocButton({ onSaveQuery(n.id); }} > - {n.name} + {n.name} ); })} diff --git a/src/components/gui/schema-editor/column-default-value-input.tsx b/src/components/gui/schema-editor/column-default-value-input.tsx index c7aa7e3b..deefd728 100644 --- a/src/components/gui/schema-editor/column-default-value-input.tsx +++ b/src/components/gui/schema-editor/column-default-value-input.tsx @@ -5,7 +5,7 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseTableColumnConstraint } from "@/drivers/base-driver"; import { ChevronsUpDown } from "lucide-react"; import { ChangeEvent, useCallback, useMemo } from "react"; @@ -20,7 +20,7 @@ export default function ColumnDefaultValueInput({ disabled?: boolean; onChange: (constraint: DatabaseTableColumnConstraint) => void; }>) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const display = useMemo(() => { if ( constraint?.defaultValue !== undefined && diff --git a/src/components/gui/schema-editor/index.tsx b/src/components/gui/schema-editor/index.tsx index 836c7430..3624ce5e 100644 --- a/src/components/gui/schema-editor/index.tsx +++ b/src/components/gui/schema-editor/index.tsx @@ -1,4 +1,4 @@ -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseTableSchemaChange } from "@/drivers/base-driver"; import { checkSchemaChange } from "@/lib/sql/sql-generate.schema"; import { LucideCode, LucideCopy, LucidePlus, LucideSave } from "lucide-react"; @@ -27,7 +27,7 @@ export default function SchemaEditor({ onSave, onDiscard, }: Readonly) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const isCreateScript = value.name.old === ""; const onAddColumn = useCallback(() => { diff --git a/src/components/gui/schema-editor/schema-create/schema-create-form.tsx b/src/components/gui/schema-editor/schema-create/schema-create-form.tsx index ade83957..79a61f50 100644 --- a/src/components/gui/schema-editor/schema-create/schema-create-form.tsx +++ b/src/components/gui/schema-editor/schema-create/schema-create-form.tsx @@ -1,23 +1,29 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; +import { DatabaseSchemaChange } from "@/drivers/base-driver"; import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react"; import { useCallback, useMemo, useState } from "react"; -import { useDatabaseDriver } from "@/context/driver-provider"; -import { DatabaseSchemaChange } from "@/drivers/base-driver"; -export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?: string; onClose: () => void; }) { - const { databaseDriver } = useDatabaseDriver(); +export function SchemaDatabaseCreateForm({ + schemaName, + onClose, +}: { + schemaName?: string; + onClose: () => void; +}) { + const { databaseDriver } = useStudioContext(); const { schema, refresh: refreshSchema } = useSchema(); const [isExecuting, setIsExecuting] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [value, setValue] = useState({ name: { - new: '', - old: '' + new: "", + old: "", }, - createScript: '', - collate: '' + createScript: "", + collate: "", }); const previewScript = useMemo(() => { @@ -27,52 +33,60 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?: const onSave = useCallback(() => { { setIsExecuting(true); - databaseDriver.transaction([previewScript]).then(() => { - refreshSchema(); - onClose(); - }).catch((err) => setErrorMessage((err as Error).message)).finally(() => { - setIsExecuting(false); - }) + databaseDriver + .transaction([previewScript]) + .then(() => { + refreshSchema(); + onClose(); + }) + .catch((err) => setErrorMessage((err as Error).message)) + .finally(() => { + setIsExecuting(false); + }); } - }, [databaseDriver, onClose, previewScript, refreshSchema]) + }, [databaseDriver, onClose, previewScript, refreshSchema]); - const schemaNames = Object.keys(schema).filter(s => s !== schemaName).map(s => s); - const schemaNameExists = schemaNames.includes(value.name.new || ''); + const schemaNames = Object.keys(schema) + .filter((s) => s !== schemaName) + .map((s) => s); + const schemaNameExists = schemaNames.includes(value.name.new || ""); return ( -
+
{errorMessage && ( -
- +
+

{errorMessage}

)} -
+
-
Schema Name
+
Schema Name
{ setValue({ ...value, name: { ...value.name, - new: e.currentTarget.value - } - }) + new: e.currentTarget.value, + }, + }); }} disabled={!!schemaName} - className={`w-full ${schemaNameExists ? 'border-red-600' : ''}`} + className={`w-full ${schemaNameExists ? "border-red-600" : ""}`} /> - { - schemaNameExists && The schema name `{value.name.new}` already exists. - } + {schemaNameExists && ( + + The schema name `{value.name.new}` already exists. + + )}
-
-
-
+
+
+
@@ -90,5 +104,5 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:
- ) -} \ No newline at end of file + ); +} diff --git a/src/components/gui/schema-editor/schema-editor-column-list.tsx b/src/components/gui/schema-editor/schema-editor-column-list.tsx index a8c0f629..efa925fe 100644 --- a/src/components/gui/schema-editor/schema-editor-column-list.tsx +++ b/src/components/gui/schema-editor/schema-editor-column-list.tsx @@ -1,6 +1,6 @@ import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseTableColumn, DatabaseTableColumnChange, @@ -104,7 +104,7 @@ function ColumnItemType({ onChange: (type: string) => void; disabled?: boolean; }) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); if ( databaseDriver.columnTypeSelector.type === "dropdown" && diff --git a/src/components/gui/schema-editor/schema-save-dialog.tsx b/src/components/gui/schema-editor/schema-save-dialog.tsx index d083d390..5df75b75 100644 --- a/src/components/gui/schema-editor/schema-save-dialog.tsx +++ b/src/components/gui/schema-editor/schema-save-dialog.tsx @@ -6,7 +6,7 @@ import { AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { DatabaseTableSchemaChange } from "@/drivers/base-driver"; import { @@ -31,7 +31,7 @@ export default function SchemaSaveDialog({ onClose: () => void; fetchTable: (schemeName: string, tableName: string) => Promise; }) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const { refresh: refreshSchema } = useSchema(); const [isExecuting, setIsExecuting] = useState(false); const [errorMessage, setErrorMessage] = useState(""); diff --git a/src/components/gui/schema-sidebar-list.tsx b/src/components/gui/schema-sidebar-list.tsx index a965dd8f..bcf9b7b4 100644 --- a/src/components/gui/schema-sidebar-list.tsx +++ b/src/components/gui/schema-sidebar-list.tsx @@ -1,5 +1,4 @@ -import { useConfig } from "@/context/config-provider"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { OpenContextMenuList } from "@/core/channel-builtin"; import { scc } from "@/core/command"; @@ -124,8 +123,7 @@ function flattenSchemaGroup( } export default function SchemaList({ search }: Readonly) { - const { databaseDriver } = useDatabaseDriver(); - const { extensions } = useConfig(); + const { databaseDriver, extensions } = useStudioContext(); const [selected, setSelected] = useState(""); const { refresh, schema, currentSchemaName } = useSchema(); const [editSchema, setEditSchema] = useState(null); diff --git a/src/components/gui/schema-sidebar.tsx b/src/components/gui/schema-sidebar.tsx index d8ccf9fe..1d31e156 100644 --- a/src/components/gui/schema-sidebar.tsx +++ b/src/components/gui/schema-sidebar.tsx @@ -1,28 +1,26 @@ -import { LucideSearch } from "lucide-react"; -import { useMemo, useState } from "react"; -import SchemaList from "./schema-sidebar-list"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; +import { scc } from "@/core/command"; +import { StudioExtensionMenuItem } from "@/core/extension-manager"; import { cn } from "@/lib/utils"; -import { buttonVariants } from "../ui/button"; import { Plus } from "@phosphor-icons/react"; -import { useDatabaseDriver } from "@/context/driver-provider"; -import SchemaCreateDialog from "./schema-editor/schema-create"; +import { LucideSearch } from "lucide-react"; +import { useMemo, useState } from "react"; +import { buttonVariants } from "../ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "../ui/dropdown-menu"; -import { scc } from "@/core/command"; -import { StudioExtensionMenuItem } from "@/core/extension-manager"; -import { useConfig } from "@/context/config-provider"; +import SchemaCreateDialog from "./schema-editor/schema-create"; +import SchemaList from "./schema-sidebar-list"; export default function SchemaView() { const [search, setSearch] = useState(""); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver, extensions } = useStudioContext(); const { currentSchemaName } = useSchema(); const [isCreateSchema, setIsCreateSchema] = useState(false); - const { extensions } = useConfig(); const contentMenu = useMemo(() => { const items: StudioExtensionMenuItem[] = []; @@ -62,7 +60,7 @@ export default function SchemaView() { buttonVariants({ size: "icon", }), - "rounded-full h-8 w-8 bg-neutral-800 dark:bg-neutral-200" + "h-8 w-8 rounded-full bg-neutral-800 dark:bg-neutral-200" )} onClick={contentMenu[0].onClick} > @@ -79,7 +77,7 @@ export default function SchemaView() { buttonVariants({ size: "icon", }), - "rounded-full h-8 w-8 bg-neutral-800 dark:bg-neutral-200" + "h-8 w-8 rounded-full bg-neutral-800 dark:bg-neutral-200" )} > @@ -98,7 +96,7 @@ export default function SchemaView() { ); }, [contentMenu]); return ( -
+
{isCreateSchema && ( { @@ -107,22 +105,22 @@ export default function SchemaView() { /> )} -
-
-

Tables

+
+
+

Tables

{activatorButton}
-
-
+
+
{ diff --git a/src/components/gui/sidebar-tab.tsx b/src/components/gui/sidebar-tab.tsx index 72feb877..56378e92 100644 --- a/src/components/gui/sidebar-tab.tsx +++ b/src/components/gui/sidebar-tab.tsx @@ -1,4 +1,4 @@ -import { useConfig } from "@/context/config-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { cn } from "@/lib/utils"; import { ArrowLeft } from "@phosphor-icons/react"; import { useTheme } from "next-themes"; @@ -40,14 +40,10 @@ export default function SidebarTab({ tabs }: Readonly) { const disableToggle = searchParams.get("disableThemeToggle") === "1" || forcedTheme; - const config = useConfig(); + const config = useStudioContext(); return ( -
+
diff --git a/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx b/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx index 005af665..71febeaa 100644 --- a/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/create-namespace-button.tsx @@ -7,7 +7,7 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { SavedDocNamespace } from "@/drivers/saved-doc/saved-doc-driver"; import { useCallback, useState } from "react"; @@ -20,7 +20,7 @@ export default function CreateNamespaceDialog({ onCreated, onClose, }: CreateNamespaceButtonProps) { - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [namespace, setNamespace] = useState(""); const [error, setError] = useState(""); @@ -64,7 +64,7 @@ export default function CreateNamespaceDialog({ onChange={(e) => setNamespace(e.currentTarget.value)} /> - {error &&
{error}
} + {error &&
{error}
} diff --git a/src/components/gui/sidebar/saved-doc-tab/index.tsx b/src/components/gui/sidebar/saved-doc-tab/index.tsx index a87128b9..d8ecd9aa 100644 --- a/src/components/gui/sidebar/saved-doc-tab/index.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/index.tsx @@ -1,28 +1,28 @@ -import { useDatabaseDriver } from "@/context/driver-provider"; -import { - SavedDocData, - SavedDocGroupByNamespace, - SavedDocNamespace, -} from "@/drivers/saved-doc/saved-doc-driver"; import { ListView, ListViewItem } from "@/components/listview"; -import { LucideTrash } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; -import RenameNamespaceDialog from "./rename-namespace-dialog"; -import RemoveDocDialog from "./remove-doc-dialog"; -import { TAB_PREFIX_SAVED_QUERY } from "@/const"; -import RemoveNamespaceDialog from "./remove-namespace-dialog"; -import { cn } from "@/lib/utils"; import { buttonVariants } from "@/components/ui/button"; -import { Binoculars, Folder, Plus } from "@phosphor-icons/react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import CreateNamespaceDialog from "./create-namespace-button"; -import { scc } from "@/core/command"; +import { TAB_PREFIX_SAVED_QUERY } from "@/const"; +import { useStudioContext } from "@/context/driver-provider"; import { OpenContextMenuList } from "@/core/channel-builtin"; +import { scc } from "@/core/command"; +import { + SavedDocData, + SavedDocGroupByNamespace, + SavedDocNamespace, +} from "@/drivers/saved-doc/saved-doc-driver"; +import { cn } from "@/lib/utils"; +import { Binoculars, Folder, Plus } from "@phosphor-icons/react"; +import { LucideTrash } from "lucide-react"; +import { useCallback, useEffect, useState } from "react"; +import CreateNamespaceDialog from "./create-namespace-button"; +import RemoveDocDialog from "./remove-doc-dialog"; +import RemoveNamespaceDialog from "./remove-namespace-dialog"; +import RenameNamespaceDialog from "./rename-namespace-dialog"; type SavedDocListData = | { @@ -56,7 +56,7 @@ function mapDoc( } export default function SavedDocTab() { - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [selected, setSelected] = useState(); const [collapsed, setCollapsed] = useState>(new Set()); @@ -157,7 +157,7 @@ export default function SavedDocTab() {
-

Queries

+

Queries

diff --git a/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx index c788954b..4f816562 100644 --- a/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/remove-doc-dialog.tsx @@ -6,7 +6,7 @@ import { DialogFooter, DialogTitle, } from "@/components/ui/dialog"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { SavedDocData } from "@/drivers/saved-doc/saved-doc-driver"; import { useCallback, useState } from "react"; @@ -17,7 +17,7 @@ interface Props { } export default function RemoveDocDialog({ doc, onClose, onComplete }: Props) { - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [loading, setLoading] = useState(false); const onDeleteClicked = useCallback(() => { diff --git a/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx index 9b5b89c3..5fe4df8c 100644 --- a/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/remove-namespace-dialog.tsx @@ -6,7 +6,7 @@ import { DialogFooter, DialogTitle, } from "@/components/ui/dialog"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { SavedDocData, SavedDocNamespace, @@ -24,7 +24,7 @@ export default function RemoveNamespaceDialog({ onComplete, value, }: Props) { - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [loading, setLoading] = useState(false); const onDeleteClicked = useCallback(() => { diff --git a/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx b/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx index e2b75a5f..97f6d7f8 100644 --- a/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/rename-namespace-dialog.tsx @@ -7,7 +7,7 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { SavedDocNamespace } from "@/drivers/saved-doc/saved-doc-driver"; import { useCallback, useState } from "react"; @@ -22,7 +22,7 @@ export default function RenameNamespaceDialog({ onComplete, value, }: Props) { - const { docDriver } = useDatabaseDriver(); + const { docDriver } = useStudioContext(); const [namespace, setNamespace] = useState(value.name); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); @@ -66,7 +66,7 @@ export default function RenameNamespaceDialog({ value={namespace} onChange={(e) => setNamespace(e.currentTarget.value)} /> - {error &&
{error}
} + {error &&
{error}
}
@@ -108,7 +108,7 @@ function ForeignKeyColumnSnippet(props: SneakpeakProps) {
{props.fkTableName}
-
+
{open && }
@@ -143,7 +143,7 @@ function BlobCellValue({ return (
- + vec({floatArray.length})
@@ -159,7 +159,7 @@ function BlobCellValue({ {prettifyBytes(bytes.subarray(0, 64))}
- + {bytes.length.toLocaleString(undefined, { maximumFractionDigits: 0, })} diff --git a/src/components/gui/table-combobox/TableColumnCombobox.tsx b/src/components/gui/table-combobox/TableColumnCombobox.tsx index 71733e47..ba1531fe 100644 --- a/src/components/gui/table-combobox/TableColumnCombobox.tsx +++ b/src/components/gui/table-combobox/TableColumnCombobox.tsx @@ -1,4 +1,4 @@ -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import type { DatabaseTableSchema } from "@/drivers/base-driver"; import { cn } from "@/lib/utils"; import { Popover, PopoverTrigger } from "@radix-ui/react-popover"; @@ -29,7 +29,7 @@ export default function TableColumnCombobox({ disabled?: boolean; borderless?: boolean; }>) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [open, setOpen] = useState(false); const [schema, setSchema] = useState(); diff --git a/src/components/gui/table-result/context-menu.tsx b/src/components/gui/table-result/context-menu.tsx index 9d795b25..bd20f39f 100644 --- a/src/components/gui/table-result/context-menu.tsx +++ b/src/components/gui/table-result/context-menu.tsx @@ -1,16 +1,16 @@ -import { useCallback } from "react"; -import { KEY_BINDING } from "@/lib/key-matcher"; -import OptimizeTableState from "../table-optimized/OptimizeTableState"; -import { useFullEditor } from "../providers/full-editor-provider"; +import { useStudioContext } from "@/context/driver-provider"; +import { openContextMenuFromEvent } from "@/core/channel-builtin"; import { exportRowsToExcel, exportRowsToJson, exportRowsToSqlInsert, } from "@/lib/export-helper"; +import { KEY_BINDING } from "@/lib/key-matcher"; import { LucidePlus, LucideTrash2 } from "lucide-react"; +import { useCallback } from "react"; +import { useFullEditor } from "../providers/full-editor-provider"; +import OptimizeTableState from "../table-optimized/OptimizeTableState"; import TableStateActions from "../table-optimized/table-state-actions"; -import { openContextMenuFromEvent } from "@/core/channel-builtin"; -import { useConfig } from "@/context/config-provider"; export default function useTableResultContextMenu({ tableName, @@ -24,7 +24,7 @@ export default function useTableResultContextMenu({ pasteCallback: (state: OptimizeTableState) => void; }) { const { openEditor } = useFullEditor(); - const { extensions } = useConfig(); + const { extensions } = useStudioContext(); return useCallback( ({ diff --git a/src/components/gui/tabs-result/explain-result-tab.tsx b/src/components/gui/tabs-result/explain-result-tab.tsx index b8528f4c..b3443091 100644 --- a/src/components/gui/tabs-result/explain-result-tab.tsx +++ b/src/components/gui/tabs-result/explain-result-tab.tsx @@ -1,15 +1,15 @@ -import { useDatabaseDriver } from "@/context/driver-provider"; -import { QueryExplanation } from "../query-explanation"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseResultSet } from "@/drivers/base-driver"; +import { QueryExplanation } from "../query-explanation"; export default function ExplainResultTab({ data, }: { data: DatabaseResultSet; }) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); return ( -
+
You are about to drop the following tables. -
+
{selectedItems.map((t) => ( -
+
{t.name}
))}
-

+

ln(x) + ex-1 - cos(x) = 0

Solve this equaltion or type confirm

@@ -81,7 +81,7 @@ function ConfirmDialog({ } export default function MassDropTableTab() { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const { schema: initialSchema, currentSchemaName, refresh } = useSchema(); const [schema] = useState(initialSchema); const [selectedSchema] = useState(currentSchemaName); @@ -168,27 +168,27 @@ export default function MassDropTableTab() { if (status === "Failed") statusIcon = ( - + ); else if (status.includes("...")) statusIcon = ( - + ); else if (status === "Dropped" || status === "Emptied") statusIcon = ( - + ); return ( <> - + {t.name} - - @@ -217,7 +217,7 @@ export default function MassDropTableTab() { )}
-

+

Drop & Empty Multiple Tables

@@ -240,7 +240,7 @@ export default function MassDropTableTab() {
-
+
(null); @@ -91,8 +90,6 @@ export default function QueryWindow({ ); const [savedKey, setSavedKey] = useState(initialSavedKey); const [placeholders, setPlaceholders] = useState>({}); - - const { agentDriver } = useConfig(); const { schema } = useSchema(); useEffect(() => { diff --git a/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx b/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx index 4025b28a..be9552d4 100644 --- a/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx +++ b/src/components/gui/tabs/relational-diagram-tab/context-menu-diagram.tsx @@ -1,20 +1,20 @@ -import React, { PropsWithChildren } from "react"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; +import { PropsWithChildren } from "react"; +import { scc } from "@/core/command"; import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, } from "../../../ui/context-menu"; -import { scc } from "@/core/command"; export default function ContextMenuERD({ schemaName, tableName, children, }: PropsWithChildren<{ schemaName: string; tableName: string }>) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const handleEditTable = () => { scc.tabs.openBuiltinSchema({ diff --git a/src/components/gui/tabs/schema-editor-tab.tsx b/src/components/gui/tabs/schema-editor-tab.tsx index bd5a0cd5..5d2960e6 100644 --- a/src/components/gui/tabs/schema-editor-tab.tsx +++ b/src/components/gui/tabs/schema-editor-tab.tsx @@ -1,5 +1,5 @@ import OpacityLoading from "@/components/gui/loading-opacity"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseTableSchemaChange } from "@/drivers/base-driver"; import { createTableSchemaDraft } from "@/lib/sql/sql-generate.schema"; import { cloneDeep } from "lodash"; @@ -26,7 +26,7 @@ export default function SchemaEditorTab({ schemaName, tableName, }: Readonly) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [schema, setSchema] = useState({ ...EMPTY_SCHEMA, schemaName, diff --git a/src/components/gui/tabs/table-data-tab.tsx b/src/components/gui/tabs/table-data-tab.tsx index e47bba6b..43741d0d 100644 --- a/src/components/gui/tabs/table-data-tab.tsx +++ b/src/components/gui/tabs/table-data-tab.tsx @@ -6,7 +6,7 @@ import { AlertDialogDescription, AlertDialogFooter, } from "@/components/ui/alert-dialog"; -// import { Button } from "@/components/ui/button"; + import { Button } from "@/components/orbit/button"; import { Tooltip, @@ -14,7 +14,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { useAutoComplete } from "@/context/auto-complete-provider"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { ColumnSortOption, @@ -50,7 +50,7 @@ export default function TableDataWindow({ }: TableDataContentProps) { const { updateTableSchema } = useAutoComplete(); const { schema } = useSchema(); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [error, setError] = useState(); const [executeError, setExecuteError] = useState(null); diff --git a/src/context/config-provider.tsx b/src/context/config-provider.tsx deleted file mode 100644 index 89fa96fd..00000000 --- a/src/context/config-provider.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { StudioExtensionManager } from "@/core/extension-manager"; -import AgentDriverList from "@/drivers/agent/list"; -import { noop } from "lodash"; -import type { PropsWithChildren } from "react"; -import { createContext, useContext } from "react"; - -interface ConfigContextProps { - color: string; - name: string; - onBack?: () => void; - extensions: StudioExtensionManager; - containerClassName?: string; - agentDriver?: AgentDriverList; -} - -const ConfigContext = createContext({ - name: "", - color: "", - extensions: new StudioExtensionManager([]), - onBack: noop, - containerClassName: "", -}); - -export function useConfig() { - return useContext(ConfigContext); -} - -export function ConfigProvider({ - children, - config, -}: PropsWithChildren<{ config: ConfigContextProps }>) { - return ( - {children} - ); -} diff --git a/src/context/driver-provider.tsx b/src/context/driver-provider.tsx index 9e380019..f294a345 100644 --- a/src/context/driver-provider.tsx +++ b/src/context/driver-provider.tsx @@ -1,29 +1,41 @@ +import { StudioExtensionManager } from "@/core/extension-manager"; +import AgentDriverList from "@/drivers/agent/list"; import type { BaseDriver } from "@/drivers/base-driver"; import { SavedDocDriver } from "@/drivers/saved-doc/saved-doc-driver"; +import { noop } from "lodash"; import { type PropsWithChildren, createContext, useContext } from "react"; -const DriverContext = createContext<{ +export interface StudioContextProps { databaseDriver: BaseDriver; docDriver?: SavedDocDriver; -}>({ + + // Moving from useConfig previously + color: string; + name: string; + onBack?: () => void; + extensions: StudioExtensionManager; + containerClassName?: string; + agentDriver?: AgentDriverList; +} + +const StudioContext = createContext({ databaseDriver: {} as unknown as BaseDriver, + name: "", + color: "", + extensions: new StudioExtensionManager([]), + onBack: noop, + containerClassName: "", }); -export function useDatabaseDriver() { - return useContext(DriverContext); +export function useStudioContext() { + return useContext(StudioContext); } -export function DriverProvider({ +export function StudioContextProvider({ children, - driver, - docDriver, -}: PropsWithChildren<{ - driver: BaseDriver; - docDriver?: SavedDocDriver; -}>) { + value, +}: PropsWithChildren<{ value: StudioContextProps }>) { return ( - - {children} - + {children} ); } diff --git a/src/context/schema-provider.tsx b/src/context/schema-provider.tsx index b62695da..7cb89dcb 100644 --- a/src/context/schema-provider.tsx +++ b/src/context/schema-provider.tsx @@ -10,8 +10,7 @@ import { useState, } from "react"; import { useAutoComplete } from "./auto-complete-provider"; -import { useConfig } from "./config-provider"; -import { useDatabaseDriver } from "./driver-provider"; +import { useStudioContext } from "./driver-provider"; type AutoCompletionSchema = Record | string[]>; @@ -68,8 +67,7 @@ export function SchemaProvider({ children }: Readonly) { const { updateTableList, updateTableSchema } = useAutoComplete(); const [error, setError] = useState(); const [loading, setLoading] = useState(true); - const { databaseDriver } = useDatabaseDriver(); - const { extensions } = useConfig(); + const { databaseDriver, extensions } = useStudioContext(); const [schema, setSchema] = useState({}); const [currentSchema, setCurrentSchema] = useState([]); diff --git a/src/extensions/data-catalog/data-catalog-tab.tsx b/src/extensions/data-catalog/data-catalog-tab.tsx index eb4df4ab..0f621ac6 100644 --- a/src/extensions/data-catalog/data-catalog-tab.tsx +++ b/src/extensions/data-catalog/data-catalog-tab.tsx @@ -1,7 +1,7 @@ import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar"; import { Button } from "@/components/orbit/button"; import { Dialog, DialogContent } from "@/components/ui/dialog"; -import { useConfig } from "@/context/config-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useEffect, useState } from "react"; import DataCatalogExtension from "."; import { DataCatalogEntryModal } from "./data-catalog-entry-modal"; @@ -10,7 +10,7 @@ import EmptyTermDefinition from "./empty-definition"; import TermDefinitionList from "./term-definition-list"; export default function DataCatalogTab() { - const { extensions } = useConfig(); + const { extensions } = useStudioContext(); const dataCatalogExtension = extensions.getExtension("data-catalog"); const driver = dataCatalogExtension?.driver; diff --git a/src/extensions/data-catalog/data-catalog-table-column-modal.tsx b/src/extensions/data-catalog/data-catalog-table-column-modal.tsx index fcb2e4ca..a44af2e0 100644 --- a/src/extensions/data-catalog/data-catalog-table-column-modal.tsx +++ b/src/extensions/data-catalog/data-catalog-table-column-modal.tsx @@ -7,7 +7,7 @@ import { } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { MagicWand } from "@phosphor-icons/react"; import { produce } from "immer"; import { LucideLoader } from "lucide-react"; @@ -30,7 +30,7 @@ export default function DataCatalogTableColumnModal({ const { driver } = useDataCatalogContext(); const modelColumn = driver.getColumn(schemaName, tableName, columnName); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [column, setColumn] = useState(() => ({ definition: modelColumn?.definition || "", samples: modelColumn?.samples || [], diff --git a/src/extensions/data-catalog/data-model-tab.tsx b/src/extensions/data-catalog/data-model-tab.tsx index 125fe8fc..311339c2 100644 --- a/src/extensions/data-catalog/data-model-tab.tsx +++ b/src/extensions/data-catalog/data-model-tab.tsx @@ -2,7 +2,7 @@ import SchemaNameSelect from "@/components/gui/schema-editor/schema-name-select" import { Toolbar, ToolbarFiller } from "@/components/gui/toolbar"; import { Input } from "@/components/orbit/input"; import { Toggle } from "@/components/orbit/toggle"; -import { useConfig } from "@/context/config-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { DatabaseTableSchema } from "@/drivers/base-driver"; import { MagnifyingGlass } from "@phosphor-icons/react"; @@ -31,7 +31,7 @@ export default function DataCatalogModelTab() { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, setRevision] = useState(1); - const { extensions } = useConfig(); + const { extensions } = useStudioContext(); const driver = useMemo(() => { const dataCatalogExtension = diff --git a/src/extensions/dolt/dolt-create-branch.tsx b/src/extensions/dolt/dolt-create-branch.tsx index 0055c80a..7eeccfcb 100644 --- a/src/extensions/dolt/dolt-create-branch.tsx +++ b/src/extensions/dolt/dolt-create-branch.tsx @@ -8,12 +8,12 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { GitBranch, Loader } from "lucide-react"; import { useCallback, useMemo, useState } from "react"; export default function useDoltCreateBranchModal(refreshStatus: () => void) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(); @@ -56,7 +56,7 @@ export default function useDoltCreateBranchModal(refreshStatus: () => void) { {commitHash ? ( <> Creating new branch from{" "} - + {commitHash} @@ -80,9 +80,9 @@ export default function useDoltCreateBranchModal(refreshStatus: () => void) { diff --git a/src/extensions/dolt/dolt-sidebar.tsx b/src/extensions/dolt/dolt-sidebar.tsx index 95d3605b..04575437 100644 --- a/src/extensions/dolt/dolt-sidebar.tsx +++ b/src/extensions/dolt/dolt-sidebar.tsx @@ -19,7 +19,7 @@ import { ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { cn } from "@/lib/utils"; import { GitBranch, Minus, Plus, Table } from "@phosphor-icons/react"; @@ -51,7 +51,7 @@ function DoltCommitLog({ const { modal: createBranchModal, openModal: openCreateBranch } = useDoltCreateBranchModal(refreshStatus); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const { showDialog: showCommonDialog } = useCommonDialog(); const onResetClicked = useCallback( @@ -167,7 +167,7 @@ function DoltChangeItem({ status: DoltStatusResultItem; refreshStatus: () => void; }) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [loading, setLoading] = useState(false); const onStageClicked = useCallback( @@ -251,7 +251,7 @@ function DoltChanges({ statusList: DoltStatusResultItem[]; refreshStatus: () => void; }) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [loading, setLoading] = useState(false); const [commitMessage, setCommitMessage] = useState(""); @@ -344,7 +344,7 @@ function DoltChanges({ export default function DoltSidebar() { const { currentSchemaName } = useSchema(); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [branchList, setBranchList] = useState([]); const [selectedBranch, setSelectedBranch] = useState("main"); const [commitList, setCommitList] = useState([]); diff --git a/src/extensions/trigger-editor/trigger-editor.tsx b/src/extensions/trigger-editor/trigger-editor.tsx index e7a0bc2b..4bbe634e 100644 --- a/src/extensions/trigger-editor/trigger-editor.tsx +++ b/src/extensions/trigger-editor/trigger-editor.tsx @@ -9,7 +9,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { DatabaseTriggerSchema, @@ -25,7 +25,7 @@ export interface TriggerEditorProps { } export default function TriggerEditor({ value, onChange }: TriggerEditorProps) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const { autoCompleteSchema, schema } = useSchema(); const extendedAutoCompleteSchema = useMemo(() => { diff --git a/src/extensions/trigger-editor/trigger-save-dialog.tsx b/src/extensions/trigger-editor/trigger-save-dialog.tsx index bdb20e96..cad56d07 100644 --- a/src/extensions/trigger-editor/trigger-save-dialog.tsx +++ b/src/extensions/trigger-editor/trigger-save-dialog.tsx @@ -1,3 +1,4 @@ +import CodePreview from "@/components/gui/code-preview"; import { AlertDialog, AlertDialogCancel, @@ -5,13 +6,12 @@ import { AlertDialogFooter, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react"; -import { useCallback, useState } from "react"; import { Button } from "@/components/ui/button"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { useSchema } from "@/context/schema-provider"; import { DatabaseTriggerSchema } from "@/drivers/base-driver"; -import CodePreview from "@/components/gui/code-preview"; +import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react"; +import { useCallback, useState } from "react"; import { triggerEditorExtensionTab } from "."; interface Props { @@ -22,30 +22,28 @@ interface Props { export function TriggerSaveDialog(props: Props) { const { refresh: refreshSchema } = useSchema(); - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [isExecuting, setIsExecuting] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const onSave = useCallback(() => { setIsExecuting(true); databaseDriver - .transaction( - props.previewScript - ) + .transaction(props.previewScript) .then(() => { refreshSchema(); triggerEditorExtensionTab.replace({ schemaName: props.trigger.schemaName, - name: props.trigger.name ?? '', - tableName: props.trigger.tableName - }) + name: props.trigger.name ?? "", + tableName: props.trigger.tableName, + }); props.onClose(); }) .catch((err) => setErrorMessage((err as Error).message)) .finally(() => { setIsExecuting(false); }); - }, [databaseDriver, props, refreshSchema]) + }, [databaseDriver, props, refreshSchema]); return ( @@ -53,8 +51,8 @@ export function TriggerSaveDialog(props: Props) { Preview {errorMessage && ( -
- +
+

{errorMessage}

)} @@ -65,9 +63,9 @@ export function TriggerSaveDialog(props: Props) { Cancel diff --git a/src/extensions/trigger-editor/trigger-tab.tsx b/src/extensions/trigger-editor/trigger-tab.tsx index 84d3463b..d2712c54 100644 --- a/src/extensions/trigger-editor/trigger-tab.tsx +++ b/src/extensions/trigger-editor/trigger-tab.tsx @@ -1,12 +1,12 @@ import OpacityLoading from "@/components/gui/loading-opacity"; -import { useDatabaseDriver } from "@/context/driver-provider"; +import { useStudioContext } from "@/context/driver-provider"; import { DatabaseTriggerSchema } from "@/drivers/base-driver"; import { produce } from "immer"; import { isEqual } from "lodash"; import { useCallback, useEffect, useMemo, useState } from "react"; import { TriggerController } from "./trigger-controller"; -import { TriggerSaveDialog } from "./trigger-save-dialog"; import TriggerEditor from "./trigger-editor"; +import { TriggerSaveDialog } from "./trigger-save-dialog"; export interface TriggerTabProps { name: string; @@ -29,7 +29,7 @@ export default function TriggerTab({ schemaName, tableName, }: TriggerTabProps) { - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const [isSaving, setIsSaving] = useState(false); // If name is specified, it means the trigger is already exist @@ -76,7 +76,7 @@ export default function TriggerTab({ } return ( -
+
{isSaving && (
); -} \ No newline at end of file +} diff --git a/src/extensions/view-editor/view-editor.tsx b/src/extensions/view-editor/view-editor.tsx index c3cffe6d..90c567ce 100644 --- a/src/extensions/view-editor/view-editor.tsx +++ b/src/extensions/view-editor/view-editor.tsx @@ -1,11 +1,11 @@ import { Input } from "@/components/ui/input"; +import { useStudioContext } from "@/context/driver-provider"; +import { useSchema } from "@/context/schema-provider"; import { DatabaseViewSchema } from "@/drivers/base-driver"; import { produce } from "immer"; -import SqlEditor from "../../components/gui/sql-editor"; -import { useDatabaseDriver } from "@/context/driver-provider"; import { useMemo } from "react"; -import { useSchema } from "@/context/schema-provider"; import SchemaNameSelect from "../../components/gui/schema-editor/schema-name-select"; +import SqlEditor from "../../components/gui/sql-editor"; interface Props { value: DatabaseViewSchema; @@ -14,7 +14,7 @@ interface Props { export default function ViewEditor(props: Props) { const { value, onChange } = props; - const { databaseDriver } = useDatabaseDriver(); + const { databaseDriver } = useStudioContext(); const { autoCompleteSchema, schema } = useSchema(); const extendedAutoCompleteSchema = useMemo(() => { @@ -26,7 +26,7 @@ export default function ViewEditor(props: Props) { return ( <> -
+
Date: Tue, 11 Mar 2025 21:35:03 +0700 Subject: [PATCH 2/2] Merge pull request #398 from outerbase/custom-format-cell feat: custom format cell --- src/components/gui/query-result-table.tsx | 2 +- .../gui/table-cell/generic-cell.tsx | 5 + .../table-optimized/OptimizeTableState.tsx | 25 +++- src/components/gui/table-optimized/index.tsx | 10 +- src/components/ui/dropdown-menu.tsx | 2 +- src/core/extension-manager.tsx | 11 +- src/core/standard-extension.tsx | 2 + src/extensions/data-decorator/editor.tsx | 126 ++++++++++++++++++ src/extensions/data-decorator/index.tsx | 17 +++ src/lib/build-table-result.ts | 1 + 10 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 src/extensions/data-decorator/editor.tsx create mode 100644 src/extensions/data-decorator/index.tsx diff --git a/src/components/gui/query-result-table.tsx b/src/components/gui/query-result-table.tsx index c4008e4b..7ecba951 100644 --- a/src/components/gui/query-result-table.tsx +++ b/src/components/gui/query-result-table.tsx @@ -136,7 +136,7 @@ export default function ResultTable({ const renderHeader = useCallback( (header: OptimizeTableHeaderWithIndexProps) => { - const extensionMenu = extensions.getQueryHeaderContextMenu(header); + const extensionMenu = extensions.getQueryHeaderContextMenu(header, data); const extensionMenuItems = extensionMenu.map((item) => { if (item.component) { return ( diff --git a/src/components/gui/table-cell/generic-cell.tsx b/src/components/gui/table-cell/generic-cell.tsx index 8c4e9c3c..9355f3d7 100644 --- a/src/components/gui/table-cell/generic-cell.tsx +++ b/src/components/gui/table-cell/generic-cell.tsx @@ -203,6 +203,11 @@ export default function GenericCell({ }, [header, value]); const content = useMemo(() => { + if (header.decorator) { + const decoratorContent = header.decorator(value); + if (decoratorContent !== null) return decoratorContent; + } + if (value === null) { return NULL; } diff --git a/src/components/gui/table-optimized/OptimizeTableState.tsx b/src/components/gui/table-optimized/OptimizeTableState.tsx index 22c6b1f1..5f6529ce 100644 --- a/src/components/gui/table-optimized/OptimizeTableState.tsx +++ b/src/components/gui/table-optimized/OptimizeTableState.tsx @@ -5,7 +5,7 @@ import { import { formatNumber } from "@/lib/convertNumber"; import { selectArrayFromIndexList } from "@/lib/export-helper"; import deepEqual from "deep-equal"; -import { OptimizeTableHeaderProps } from "."; +import { OptimizeTableHeaderProps, TableCellDecorator } from "."; export interface OptimizeTableRowValue { raw: Record; @@ -42,6 +42,7 @@ export default class OptimizeTableState { public gutterColumnWidth = 40; protected headers: OptimizeTableHeaderProps[] = []; + public headerRevision = 1; protected headerWidth: number[] = []; protected editMode = false; @@ -203,6 +204,28 @@ export default class OptimizeTableState { return this.headers; } + updateHeaderDecorator( + header: OptimizeTableHeaderProps, + decorator: TableCellDecorator | undefined + ) { + const idx = this.headers.findIndex((h) => h.name === header.name); + + if (idx >= 0) { + this.headers = this.headers.map((h) => { + if (h.name === header.name) { + return { + ...h, + decorator, + }; + } + return h; + }); + + this.headerRevision++; + this.broadcastChange(); + } + } + getValue(y: number, x: number): unknown { const rowChange = this.data[y]?.change; if (rowChange) { diff --git a/src/components/gui/table-optimized/index.tsx b/src/components/gui/table-optimized/index.tsx index cd93330a..fb012a57 100644 --- a/src/components/gui/table-optimized/index.tsx +++ b/src/components/gui/table-optimized/index.tsx @@ -19,6 +19,8 @@ import TableFakeRowPadding from "./table-fake-row-padding"; import TableHeaderList from "./table-header-list"; import useTableVisibilityRecalculation from "./useTableVisibilityRecalculation"; +export type TableCellDecorator = (value: unknown) => ReactElement | null; + export interface TableHeaderMetadata { from?: { schema: string; @@ -41,6 +43,7 @@ export interface TableHeaderMetadata { columnSchema?: DatabaseTableColumn; } + export interface OptimizeTableHeaderProps { name: string; @@ -52,6 +55,8 @@ export interface OptimizeTableHeaderProps { tooltip?: string; }; + decorator?: TableCellDecorator; + setting: { resizable: boolean; readonly: boolean; @@ -60,6 +65,7 @@ export interface OptimizeTableHeaderProps { onContextMenu?: (e: React.MouseEvent, headerIndex: number) => void; metadata: TableHeaderMetadata; + store: Map; } export interface OptimizeTableHeaderWithIndexProps @@ -279,12 +285,14 @@ export default function OptimizeTable({ return () => internalState.removeChangeListener(changeCallback); }, [internalState, rerender]); + const headerRevision = internalState.headerRevision; const headerWithIndex = useMemo(() => { // Attach the actual index const headers = internalState.getHeaders().map((header, idx) => ({ ...header, index: idx, sticky: idx === stickyHeaderIndex, + headerRevision, // this is basically useless, but we do it to ignore deps warning })); // We will rearrange the index based on specified index @@ -297,7 +305,7 @@ export default function OptimizeTable({ ...(stickyHeaderIndex !== undefined ? [headers[stickyHeaderIndex]] : []), ...headerAfterArranged.filter((x) => x.index !== stickyHeaderIndex), ]; - }, [internalState, arrangeHeaderIndex, stickyHeaderIndex]); + }, [internalState, arrangeHeaderIndex, stickyHeaderIndex, headerRevision]); const { visibileRange, onHeaderResize } = useTableVisibilityRecalculation({ containerRef, diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index e5704bcf..0e4a237f 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -31,7 +31,7 @@ const DropdownMenuSubTrigger = React.forwardRef< StudioExtensionMenuItem | undefined; type QueryHeaderResultMenuHandler = ( - header: OptimizeTableHeaderProps + header: OptimizeTableHeaderProps, + state: OptimizeTableState ) => StudioExtensionMenuItem | undefined; type AfterFetchSchemaHandler = (schema: DatabaseSchemas) => void; @@ -120,9 +122,12 @@ export class StudioExtensionManager extends StudioExtensionContext { .filter(Boolean) as StudioExtensionMenuItem[]; } - getQueryHeaderContextMenu(header: OptimizeTableHeaderProps) { + getQueryHeaderContextMenu( + header: OptimizeTableHeaderProps, + state: OptimizeTableState + ) { return this.queryResultHeaderContextMenu - .map((handler) => handler(header)) + .map((handler) => handler(header, state)) .filter(Boolean) as StudioExtensionMenuItem[]; } diff --git a/src/core/standard-extension.tsx b/src/core/standard-extension.tsx index 3923b6f2..3f121c8a 100644 --- a/src/core/standard-extension.tsx +++ b/src/core/standard-extension.tsx @@ -3,6 +3,7 @@ */ import ColumnDescriptorExtension from "@/extensions/column-descriptor"; +import DataDecoratorExtension from "@/extensions/data-decorator"; import QueryHistoryConsoleLogExtension from "@/extensions/query-console-log"; import TriggerEditorExtension from "@/extensions/trigger-editor"; import ViewEditorExtension from "@/extensions/view-editor"; @@ -12,6 +13,7 @@ export function createStandardExtensions() { new QueryHistoryConsoleLogExtension(), new ViewEditorExtension(), new ColumnDescriptorExtension(), + new DataDecoratorExtension(), ]; } diff --git a/src/extensions/data-decorator/editor.tsx b/src/extensions/data-decorator/editor.tsx new file mode 100644 index 00000000..fad9697b --- /dev/null +++ b/src/extensions/data-decorator/editor.tsx @@ -0,0 +1,126 @@ +import { OptimizeTableHeaderProps } from "@/components/gui/table-optimized"; +import OptimizeTableState from "@/components/gui/table-optimized/OptimizeTableState"; +import { + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, +} from "@/components/ui/dropdown-menu"; +import { Check } from "@phosphor-icons/react"; +import { useState } from "react"; +import z from "zod"; + +function currencyDecorator(value: unknown) { + if (typeof value === "number" || typeof value === "bigint") { + return ( +
+ {new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(Number(value))} +
+ ); + } + + return null; +} + +function unixToDateTimeDecorator(value: unknown) { + if (typeof value === "number" || typeof value === "bigint") { + return ( +
+ {new Date(Number(value) * 1000).toISOString()} +
+ ); + } + + return null; +} + +function unixMsToDateTimeDecorator(value: unknown) { + if (typeof value === "number" || typeof value === "bigint") { + return ( +
+ {new Date(Number(value)).toISOString()} +
+ ); + } + + return null; +} + +export function DecoratorEditor({ + header, + state, +}: { + header: OptimizeTableHeaderProps; + state: OptimizeTableState; +}) { + const [type, setType] = useState(() => { + const setting = header.store.get("decorator"); + + const schema = z.object({ type: z.enum(["currency", "unix", "unix-ms"]) }); + const validated = schema.safeParse(setting); + + if (validated.error) { + return null; + } + + return validated.data.type; + }); + + return ( + + Format + + { + state.updateHeaderDecorator(header, undefined); + setType(null); + header.store.set("decorator", undefined); + }} + > + {type === null ? : null} + None + + + { + state.updateHeaderDecorator(header, currencyDecorator); + setType("currency"); + header.store.set("decorator", { type: "currency" }); + }} + > + {type === "currency" ? : null} + Currency + + + { + state.updateHeaderDecorator(header, unixMsToDateTimeDecorator); + setType("unix-ms"); + header.store.set("decorator", { type: "unix-ms" }); + }} + > + {type === "unix-ms" ? : null} + Unix Timestamp (ms) to datetime + + { + state.updateHeaderDecorator(header, unixToDateTimeDecorator); + setType("unix"); + header.store.set("decorator", { type: "unix" }); + }} + > + {type === "unix" ? : null} + Unix Timestamp (s) to datetime + + + + ); +} diff --git a/src/extensions/data-decorator/index.tsx b/src/extensions/data-decorator/index.tsx new file mode 100644 index 00000000..9d36833a --- /dev/null +++ b/src/extensions/data-decorator/index.tsx @@ -0,0 +1,17 @@ +import { StudioExtension } from "@/core/extension-base"; +import { StudioExtensionContext } from "@/core/extension-manager"; +import { DecoratorEditor } from "./editor"; + +export default class DataDecoratorExtension extends StudioExtension { + extensionName = "data-decorator"; + + init(studio: StudioExtensionContext): void { + studio.registerQueryHeaderContextMenu((header, state) => { + return { + key: "data-decorator", + title: "Decorate", + component: , + }; + }); + } +} diff --git a/src/lib/build-table-result.ts b/src/lib/build-table-result.ts index 579749a6..3013be75 100644 --- a/src/lib/build-table-result.ts +++ b/src/lib/build-table-result.ts @@ -269,6 +269,7 @@ export function buildTableResultHeader( } return { + store: new Map(), name: column.name, display: { text: column.displayName,
+ {t.type} + {statusIcon} {status}