Skip to content

Commit 2037352

Browse files
committed
feat: improve context menu and setup dummy table schema tab
1 parent 3be0b51 commit 2037352

File tree

7 files changed

+136
-31
lines changed

7 files changed

+136
-31
lines changed

src/renderer/components/TreeView/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface TreeViewProps<T> {
1717
onCollapsedChange?: (value?: string[]) => void;
1818
onSelectChange?: (value?: TreeViewItemData<T>) => void;
1919
onDoubleClick?: (value: TreeViewItemData<T>) => void;
20+
onContextMenu?: React.MouseEventHandler;
2021
}
2122

2223
function TreeViewItem<T>({
@@ -28,7 +29,6 @@ function TreeViewItem<T>({
2829

2930
collapsedKeys,
3031
onCollapsedChange,
31-
3232
onDoubleClick,
3333
}: {
3434
item: TreeViewItemData<T>;
@@ -39,7 +39,6 @@ function TreeViewItem<T>({
3939

4040
onCollapsedChange?: (value?: string[]) => void;
4141
collapsedKeys?: string[];
42-
4342
onDoubleClick?: (value: TreeViewItemData<T>) => void;
4443
}) {
4544
const hasCollapsed = item.children && item.children.length > 0;
@@ -67,6 +66,7 @@ function TreeViewItem<T>({
6766
collapsed={isCollapsed}
6867
selected={selected?.id === item.id}
6968
onClick={onSelectChangeCallback}
69+
onContextMenu={onSelectChangeCallback}
7070
onCollapsedClick={() => {
7171
if (onCollapsedChange) {
7272
if (isCollapsed) {
@@ -110,9 +110,10 @@ export default function TreeView<T>({
110110
onCollapsedChange,
111111
collapsedKeys,
112112
onDoubleClick,
113+
onContextMenu,
113114
}: TreeViewProps<T>) {
114115
return (
115-
<div className={`${styles.treeView} scroll`}>
116+
<div className={`${styles.treeView} scroll`} onContextMenu={onContextMenu}>
116117
{items.map((item) => {
117118
return (
118119
<TreeViewItem

src/renderer/components/WindowTab/DatabaseTableList.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import {
1212
faGear,
1313
faTableList,
1414
} from '@fortawesome/free-solid-svg-icons';
15+
import { useContextMenu } from 'renderer/contexts/ContextMenuProvider';
16+
import SqlTableSchemaTab from 'renderer/screens/DatabaseScreen/SqlTableSchemaTab';
1517

1618
export default function DatabaseTableList() {
1719
const { schema, currentDatabase } = useSchmea();
1820
const [selected, setSelected] = useState<
1921
TreeViewItemData<{
22+
database: string;
2023
name: string;
2124
type: string;
2225
}>
@@ -26,6 +29,40 @@ export default function DatabaseTableList() {
2629
currentDatabase ? [`database/${currentDatabase}`] : []
2730
);
2831

32+
const { handleContextMenu } = useContextMenu(() => {
33+
const tableName = selected?.data?.name;
34+
const databaseName = selected?.data?.database;
35+
36+
if (
37+
selected &&
38+
selected.data &&
39+
selected.data.type === 'table' &&
40+
tableName &&
41+
databaseName
42+
) {
43+
return [
44+
{ text: 'Select 200 Rows' },
45+
{
46+
text: 'Open Structure',
47+
onClick: () => {
48+
newWindow(tableName, (key, name) => {
49+
return (
50+
<SqlTableSchemaTab
51+
tabKey={key}
52+
name={name}
53+
database={databaseName}
54+
table={tableName}
55+
/>
56+
);
57+
});
58+
},
59+
},
60+
];
61+
}
62+
63+
return [];
64+
}, [selected, newWindow]);
65+
2966
const schemaListItem = useMemo(() => {
3067
if (!schema) return [];
3168

@@ -42,6 +79,7 @@ export default function DatabaseTableList() {
4279
data: {
4380
name: table.name,
4481
type: table.type === 'TABLE' ? 'table' : 'view',
82+
database: database.name,
4583
},
4684
}));
4785

@@ -54,6 +92,7 @@ export default function DatabaseTableList() {
5492
data: {
5593
name: event,
5694
type: 'event',
95+
database: database.name,
5796
},
5897
}))
5998
);
@@ -67,6 +106,7 @@ export default function DatabaseTableList() {
67106
icon: <FontAwesomeIcon icon={faGear} color="#bdc3c7" />,
68107
data: {
69108
name: trigger,
109+
database: database.name,
70110
type: 'trigger',
71111
},
72112
}))
@@ -94,10 +134,11 @@ export default function DatabaseTableList() {
94134
onSelectChange={setSelected}
95135
collapsedKeys={collapsed}
96136
onCollapsedChange={setCollapsed}
137+
onContextMenu={handleContextMenu}
97138
onDoubleClick={(item) => {
98139
const tableName = item.data?.name;
99140
const type = item.data?.type;
100-
if (type === 'table' && tableName) {
141+
if ((type === 'table' || type === 'view') && tableName) {
101142
newWindow(`SELECT ${tableName}`, (key, name) => (
102143
<QueryWindow
103144
initialSql={new QueryBuilder('mysql')

src/renderer/contexts/ContextMenuProvider.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
createContext,
55
useContext,
66
useCallback,
7+
useEffect,
78
} from 'react';
89
import ContextMenu, {
910
ContextMenuItemProps,
@@ -33,16 +34,24 @@ export function useContextMenu(
3334
) {
3435
const context = useContext(ContextMenuContext);
3536
const createMenuCallback = useCallback(cb, deps);
37+
const [intentToOpenCounter, setIntentToOpenCounter] = useState(0);
3638
const { open, setMenuItem, handleContextMenu, handleClick } = context;
3739

40+
useEffect(() => {
41+
if (intentToOpenCounter > 0) {
42+
const r = createMenuCallback();
43+
setMenuItem(r);
44+
}
45+
}, [intentToOpenCounter, createMenuCallback, setMenuItem]);
46+
3847
const handleContextMenuWithFreshData = useCallback(
3948
(e: React.MouseEvent) => {
4049
if (!open) {
41-
setMenuItem(createMenuCallback());
42-
handleContextMenu(e);
50+
setIntentToOpenCounter((prev) => prev + 1);
51+
setTimeout(() => handleContextMenu(e), 50);
4352
}
4453
},
45-
[createMenuCallback, setMenuItem, open, handleContextMenu]
54+
[setIntentToOpenCounter, open, handleContextMenu]
4655
);
4756

4857
const handleClickWithFreshData = useCallback(
@@ -87,8 +96,6 @@ export function ContextMenuProvider({ children }: PropsWithChildren) {
8796
(e: React.MouseEvent) => {
8897
const bound = e.currentTarget.getBoundingClientRect();
8998

90-
console.log('click', bound);
91-
9299
setStatus((prev) => ({
93100
...prev,
94101
open: true,

src/renderer/contexts/WindowTabProvider.tsx

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useDatabaseSetting } from './DatabaseSettingProvider';
1212
import { db } from 'renderer/db';
1313
import { DatabaseSavedState } from 'types/FileFormatType';
1414
import QueryWindow from 'renderer/screens/DatabaseScreen/QueryWindow';
15+
import SqlTableSchemaTab from 'renderer/screens/DatabaseScreen/SqlTableSchemaTab';
1516

1617
interface WindowTabItemProps {
1718
key: string;
@@ -88,17 +89,34 @@ export function WindowTabProvider({ children }: PropsWithChildren) {
8889
.then((result: DatabaseSavedState | null) => {
8990
if (result) {
9091
setTabs(
91-
result.tabs.map((tab) => ({
92-
key: tab.key,
93-
name: tab.name,
94-
component: (
95-
<QueryWindow
96-
tabKey={tab.key}
97-
name={tab.name}
98-
initialSql={tab.sql}
99-
/>
100-
),
101-
}))
92+
result.tabs.map((tab) => {
93+
let component: ReactElement = <div />;
94+
95+
if (tab.type === 'query' || !tab.type) {
96+
component = (
97+
<QueryWindow
98+
tabKey={tab.key}
99+
name={tab.name}
100+
initialSql={tab.sql}
101+
/>
102+
);
103+
} else if (tab.type === 'table-schema') {
104+
component = (
105+
<SqlTableSchemaTab
106+
tabKey={tab.key}
107+
name={tab.name}
108+
database={tab.database}
109+
table={tab.table}
110+
/>
111+
);
112+
}
113+
114+
return {
115+
key: tab.key,
116+
name: tab.name,
117+
component,
118+
};
119+
})
102120
);
103121
setSelectedTab(result.selectedTabKey);
104122
} else {
@@ -128,15 +146,17 @@ export function WindowTabProvider({ children }: PropsWithChildren) {
128146
.put({
129147
id: setting?.id || '',
130148
selectedTabKey: selectedTab,
131-
tabs: tabs.map((tab) => {
132-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
133-
const { component, ...rest } = tab;
134-
return {
135-
...rest,
136-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
137-
sql: (tabData.data[tab.key] as any)?.sql || undefined,
138-
};
139-
}),
149+
tabs: tabs
150+
.map((tab) => {
151+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
152+
const { component, ...rest } = tab;
153+
return {
154+
...rest,
155+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
156+
...(tabData.data[tab.key] as any),
157+
};
158+
})
159+
.filter((tab) => !!tab.type),
140160
})
141161
.then(() => {
142162
setAllowedClose(true);

src/renderer/screens/DatabaseScreen/QueryWindow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export default function QueryWindow({
143143
}, [executeSql, initialSql, initialRun]);
144144

145145
useEffect(() => {
146-
setTabData(tabKey, { sql: initialSql });
146+
setTabData(tabKey, { sql: initialSql, type: 'query' });
147147
}, [tabKey, setTabData, initialSql]);
148148

149149
return (
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect } from 'react';
2+
import { useWindowTab } from 'renderer/contexts/WindowTabProvider';
3+
4+
interface SqlTableSchemaProps {
5+
name: string;
6+
tabKey: string;
7+
database: string;
8+
table: string;
9+
}
10+
11+
export default function SqlTableSchemaTab({
12+
tabKey,
13+
database,
14+
table,
15+
}: SqlTableSchemaProps) {
16+
const { setTabData } = useWindowTab();
17+
18+
useEffect(() => {
19+
setTabData(tabKey, { type: 'table-schema', database, table });
20+
}, [tabKey, setTabData, database, table]);
21+
22+
return (
23+
<div>
24+
<h1>
25+
{database}.{table}
26+
</h1>
27+
</div>
28+
);
29+
}

src/types/FileFormatType.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ export interface ConfigurationFileFormat {
77
}
88

99
export interface DatabaseSavedState {
10-
tabs: { key: string; name: string; sql: string }[];
10+
tabs: {
11+
key: string;
12+
name: string;
13+
sql: string;
14+
type: string;
15+
database: string;
16+
table: string;
17+
}[];
1118
selectedTabKey: string;
1219
}

0 commit comments

Comments
 (0)