@sqlrooms/sql-editor 0.26.1-rc.3 → 0.26.1-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/SqlEditor.d.ts.map +1 -1
- package/dist/SqlEditor.js +2 -11
- package/dist/SqlEditor.js.map +1 -1
- package/dist/SqlEditorSlice.d.ts +8 -1
- package/dist/SqlEditorSlice.d.ts.map +1 -1
- package/dist/SqlEditorSlice.js +49 -36
- package/dist/SqlEditorSlice.js.map +1 -1
- package/dist/components/CreateTableModal.d.ts +34 -1
- package/dist/components/CreateTableModal.d.ts.map +1 -1
- package/dist/components/CreateTableModal.js +109 -18
- package/dist/components/CreateTableModal.js.map +1 -1
- package/dist/components/QueryEditorPanel.d.ts.map +1 -1
- package/dist/components/QueryEditorPanel.js +5 -6
- package/dist/components/QueryEditorPanel.js.map +1 -1
- package/dist/components/QueryEditorPanelTabsList.d.ts.map +1 -1
- package/dist/components/QueryEditorPanelTabsList.js +19 -116
- package/dist/components/QueryEditorPanelTabsList.js.map +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/package.json +14 -13
- package/dist/components/QueryTabMenuItem.d.ts +0 -13
- package/dist/components/QueryTabMenuItem.d.ts.map +0 -1
- package/dist/components/QueryTabMenuItem.js +0 -13
- package/dist/components/QueryTabMenuItem.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { makeQualifiedTableName } from '@sqlrooms/duckdb';
|
|
4
|
+
import { Alert, AlertDescription, Button, Checkbox, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Form, FormControl, FormField, FormItem, FormLabel, FormMessage, Input, Label, Popover, PopoverContent, PopoverTrigger, Spinner, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn, } from '@sqlrooms/ui';
|
|
5
|
+
import { Check, ChevronsUpDown, HelpCircle } from 'lucide-react';
|
|
6
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
5
7
|
import { useForm } from 'react-hook-form';
|
|
6
8
|
import * as z from 'zod';
|
|
7
|
-
import { SqlMonacoEditor } from '../SqlMonacoEditor';
|
|
8
9
|
import { useStoreWithSqlEditor } from '../SqlEditorSlice';
|
|
10
|
+
import { SqlMonacoEditor } from '../SqlMonacoEditor';
|
|
9
11
|
const VALID_TABLE_OR_COLUMN_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;
|
|
10
12
|
const formSchema = z.object({
|
|
11
13
|
tableName: z
|
|
@@ -13,38 +15,127 @@ const formSchema = z.object({
|
|
|
13
15
|
.min(1, 'Table name is required')
|
|
14
16
|
.regex(VALID_TABLE_OR_COLUMN_REGEX, 'Only letters, digits and underscores are allowed; should not start with a digit'),
|
|
15
17
|
query: z.string().min(1, 'Query is required'),
|
|
18
|
+
schema: z.string().optional(),
|
|
19
|
+
database: z.string().optional(),
|
|
20
|
+
replace: z.boolean(),
|
|
21
|
+
temp: z.boolean(),
|
|
22
|
+
view: z.boolean(),
|
|
16
23
|
});
|
|
17
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Compact searchable combobox for selecting schema or database.
|
|
26
|
+
*/
|
|
27
|
+
const SchemaCombobox = ({ value, onChange, options, placeholder, searchPlaceholder, emptyMessage, disabled, }) => {
|
|
28
|
+
const [open, setOpen] = useState(false);
|
|
29
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", role: "combobox", "aria-expanded": open, className: "h-9 w-full min-w-0 justify-between font-mono text-xs", disabled: disabled, children: [_jsx("span", { className: "min-w-0 truncate", children: value || placeholder }), _jsx(ChevronsUpDown, { className: "ml-1 h-3 w-3 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-[180px] p-0", children: _jsxs(Command, { children: [_jsx(CommandInput, { placeholder: searchPlaceholder, className: "text-xs" }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { className: "text-xs", children: emptyMessage }), _jsx(CommandGroup, { children: options.map((option) => (_jsxs(CommandItem, { value: option, className: "text-xs", onSelect: (currentValue) => {
|
|
30
|
+
onChange(currentValue === value ? undefined : currentValue);
|
|
31
|
+
setOpen(false);
|
|
32
|
+
}, children: [_jsx(Check, { className: cn('mr-2 h-3 w-3', value === option ? 'opacity-100' : 'opacity-0') }), option] }, option))) })] })] }) })] }));
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Compact checkbox option with clickable label and tooltip.
|
|
36
|
+
*/
|
|
37
|
+
const OptionCheckbox = ({ id, label, tooltip, checked, onCheckedChange, disabled }) => (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Checkbox, { id: id, checked: checked, onCheckedChange: onCheckedChange, disabled: disabled, className: "h-3.5 w-3.5" }), _jsx(Label, { htmlFor: id, className: cn('cursor-pointer text-xs font-normal', disabled && 'cursor-not-allowed opacity-50'), children: label }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(HelpCircle, { className: "text-muted-foreground h-3 w-3 cursor-help" }) }), _jsx(TooltipContent, { side: "top", className: "max-w-[200px] text-xs", children: tooltip })] })] }));
|
|
38
|
+
const CreateTableForm = ({ query, onClose, editDataSource, allowMultipleStatements = false, showSchemaSelection = false, onAddOrUpdateSqlQuery, initialValues, }) => {
|
|
18
39
|
const connector = useStoreWithSqlEditor((state) => state.db.connector);
|
|
40
|
+
const createTableFromQuery = useStoreWithSqlEditor((state) => state.db.createTableFromQuery);
|
|
41
|
+
const refreshTableSchemas = useStoreWithSqlEditor((state) => state.db.refreshTableSchemas);
|
|
42
|
+
const tables = useStoreWithSqlEditor((state) => state.db.tables);
|
|
43
|
+
const currentSchema = useStoreWithSqlEditor((state) => state.db.currentSchema);
|
|
44
|
+
const currentDatabase = useStoreWithSqlEditor((state) => state.db.currentDatabase);
|
|
45
|
+
// Extract unique schemas and databases from tables (excluding system ones)
|
|
46
|
+
const { schemas, databases } = useMemo(() => {
|
|
47
|
+
const schemaSet = new Set();
|
|
48
|
+
const databaseSet = new Set();
|
|
49
|
+
for (const table of tables) {
|
|
50
|
+
if (table.table.schema && !table.table.schema.startsWith('pg_')) {
|
|
51
|
+
schemaSet.add(table.table.schema);
|
|
52
|
+
}
|
|
53
|
+
if (table.table.database) {
|
|
54
|
+
databaseSet.add(table.table.database);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Ensure current schema/database are included
|
|
58
|
+
if (currentSchema)
|
|
59
|
+
schemaSet.add(currentSchema);
|
|
60
|
+
if (currentDatabase)
|
|
61
|
+
databaseSet.add(currentDatabase);
|
|
62
|
+
return {
|
|
63
|
+
schemas: Array.from(schemaSet).sort(),
|
|
64
|
+
databases: Array.from(databaseSet).sort(),
|
|
65
|
+
};
|
|
66
|
+
}, [tables, currentSchema, currentDatabase]);
|
|
19
67
|
const form = useForm({
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
68
|
resolver: zodResolver(formSchema),
|
|
22
69
|
defaultValues: {
|
|
23
|
-
tableName: editDataSource?.tableName ?? '',
|
|
70
|
+
tableName: editDataSource?.tableName ?? initialValues?.tableName ?? '',
|
|
24
71
|
query: editDataSource?.sqlQuery ?? query.trim(),
|
|
72
|
+
schema: initialValues?.schema ?? currentSchema,
|
|
73
|
+
database: initialValues?.database ?? currentDatabase,
|
|
74
|
+
replace: initialValues?.replace ?? false,
|
|
75
|
+
temp: initialValues?.temp ?? false,
|
|
76
|
+
view: initialValues?.view ?? false,
|
|
25
77
|
},
|
|
26
78
|
});
|
|
79
|
+
const isSubmitting = form.formState.isSubmitting;
|
|
27
80
|
const onSubmit = useCallback(async (values) => {
|
|
28
81
|
try {
|
|
29
|
-
const { tableName, query } = values;
|
|
30
|
-
|
|
82
|
+
const { tableName, query, schema, database, replace, temp, view } = values;
|
|
83
|
+
if (onAddOrUpdateSqlQuery) {
|
|
84
|
+
// Legacy path: use the callback
|
|
85
|
+
await onAddOrUpdateSqlQuery(tableName, query, editDataSource?.tableName);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// New path: call createTableFromQuery directly
|
|
89
|
+
const qualifiedName = schema || database
|
|
90
|
+
? makeQualifiedTableName({ table: tableName, schema, database })
|
|
91
|
+
: tableName;
|
|
92
|
+
await createTableFromQuery(qualifiedName, query, {
|
|
93
|
+
replace,
|
|
94
|
+
temp,
|
|
95
|
+
view,
|
|
96
|
+
allowMultipleStatements,
|
|
97
|
+
});
|
|
98
|
+
// Refresh table schemas to show the new table
|
|
99
|
+
await refreshTableSchemas();
|
|
100
|
+
}
|
|
31
101
|
form.reset();
|
|
32
102
|
onClose();
|
|
33
103
|
}
|
|
34
104
|
catch (err) {
|
|
35
105
|
form.setError('root', { type: 'manual', message: `${err}` });
|
|
36
106
|
}
|
|
37
|
-
}, [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
107
|
+
}, [
|
|
108
|
+
onAddOrUpdateSqlQuery,
|
|
109
|
+
editDataSource?.tableName,
|
|
110
|
+
createTableFromQuery,
|
|
111
|
+
allowMultipleStatements,
|
|
112
|
+
refreshTableSchemas,
|
|
113
|
+
onClose,
|
|
114
|
+
form,
|
|
115
|
+
]);
|
|
116
|
+
const watchView = form.watch('view');
|
|
117
|
+
const watchTemp = form.watch('temp');
|
|
118
|
+
const watchTableName = form.watch('tableName');
|
|
119
|
+
return (_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(Form, { ...form, children: _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-4", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: editDataSource
|
|
120
|
+
? 'Edit table query'
|
|
121
|
+
: watchView
|
|
122
|
+
? 'Create view from query'
|
|
123
|
+
: 'Create table from query' }), !editDataSource && (_jsx(DialogDescription, { children: watchView
|
|
124
|
+
? 'Create a new view from an SQL query.'
|
|
125
|
+
: 'Create a new table from the results of an SQL query.' }))] }), form.formState.errors.root && (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { className: "whitespace-pre-wrap font-mono text-xs", children: form.formState.errors.root.message }) })), _jsxs("div", { className: "flex items-end gap-3", children: [_jsx(FormField, { control: form.control, name: "tableName", render: ({ field }) => (_jsxs(FormItem, { className: "min-w-0 flex-[2]", children: [_jsx(FormLabel, { className: "text-xs", children: watchView ? 'View name' : 'Table name' }), _jsx(FormControl, { children: _jsx(Input, { ...field, className: "h-9 font-mono text-xs", autoFocus: true, disabled: isSubmitting }) }), _jsx(FormMessage, { className: "text-xs" })] })) }), showSchemaSelection && (_jsxs(_Fragment, { children: [_jsx(FormField, { control: form.control, name: "schema", render: ({ field }) => (_jsxs(FormItem, { className: "min-w-0 flex-1", children: [_jsx(FormLabel, { className: "text-xs", children: "Schema" }), _jsx(FormControl, { children: _jsx(SchemaCombobox, { value: field.value, onChange: field.onChange, options: schemas, placeholder: "Schema...", searchPlaceholder: "Search...", emptyMessage: "No schemas.", disabled: isSubmitting }) })] })) }), (databases.length > 1 || watchTemp) && (_jsx(FormField, { control: form.control, name: "database", render: ({ field }) => (_jsxs(FormItem, { className: "min-w-0 flex-1", children: [_jsx(FormLabel, { className: "text-xs", children: "Database" }), _jsx(FormControl, { children: _jsx(SchemaCombobox, { value: watchTemp ? 'temp' : field.value, onChange: field.onChange, options: databases, placeholder: "Database...", searchPlaceholder: "Search...", emptyMessage: "No databases.", disabled: isSubmitting || watchTemp }) })] })) }))] }))] }), _jsx(FormField, { control: form.control, name: "query", render: ({ field }) => (_jsxs(FormItem, { className: "relative flex h-[200px] flex-col", children: [_jsx(FormControl, { children: _jsx(SqlMonacoEditor, { connector: connector, value: field.value, onChange: field.onChange, className: "absolute inset-0 h-full w-full", options: {
|
|
126
|
+
scrollBeyondLastLine: false,
|
|
127
|
+
automaticLayout: true,
|
|
128
|
+
minimap: { enabled: false },
|
|
129
|
+
wordWrap: 'on',
|
|
130
|
+
folding: false,
|
|
131
|
+
lineNumbers: 'off',
|
|
132
|
+
readOnly: isSubmitting,
|
|
133
|
+
fixedOverflowWidgets: false, // default true doesn't work well in a modal
|
|
134
|
+
} }) }), _jsx(FormMessage, { className: "text-xs" })] })) }), !onAddOrUpdateSqlQuery && (_jsxs("div", { className: "flex items-center gap-6 rounded-md border px-3 py-2", children: [_jsx(FormField, { control: form.control, name: "view", render: ({ field }) => (_jsx(OptionCheckbox, { id: "create-table-view", label: "View", tooltip: "Create a view instead of a table. Views store the query, not the data.", checked: field.value, onCheckedChange: field.onChange, disabled: isSubmitting })) }), _jsx(FormField, { control: form.control, name: "replace", render: ({ field }) => (_jsx(OptionCheckbox, { id: "create-table-replace", label: "Overwrite", tooltip: `Overwrite existing ${watchView ? 'view' : 'table'} with the same name if it exists.`, checked: field.value, onCheckedChange: field.onChange, disabled: isSubmitting })) })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", onClick: onClose, disabled: isSubmitting, children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isSubmitting || !watchTableName?.trim(), children: [isSubmitting && _jsx(Spinner, { className: "mr-2" }), editDataSource ? 'Update' : 'Create'] })] })] }) }) }));
|
|
44
135
|
};
|
|
45
136
|
const CreateTableModal = (props) => {
|
|
46
|
-
const { isOpen, onClose, query, editDataSource, onAddOrUpdateSqlQuery } = props;
|
|
47
|
-
return (_jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: _jsx(DialogContent, { className:
|
|
137
|
+
const { isOpen, onClose, query, editDataSource, allowMultipleStatements, showSchemaSelection, onAddOrUpdateSqlQuery, className, initialValues, } = props;
|
|
138
|
+
return (_jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: _jsx(DialogContent, { className: cn('w-3xl max-w-[80%]', className), children: isOpen && (_jsx(CreateTableForm, { query: query, onClose: onClose, editDataSource: editDataSource, allowMultipleStatements: allowMultipleStatements, showSchemaSelection: showSchemaSelection, onAddOrUpdateSqlQuery: onAddOrUpdateSqlQuery, initialValues: initialValues })) }) }));
|
|
48
139
|
};
|
|
49
140
|
export default CreateTableModal;
|
|
50
141
|
//# sourceMappingURL=CreateTableModal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateTableModal.js","sourceRoot":"","sources":["../../src/components/CreateTableModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import {zodResolver} from '@hookform/resolvers/zod';\nimport {SqlQueryDataSource} from '@sqlrooms/room-shell';\nimport {\n Alert,\n AlertDescription,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n} from '@sqlrooms/ui';\nimport {FC, useCallback} from 'react';\nimport {useForm} from 'react-hook-form';\nimport * as z from 'zod';\nimport {SqlMonacoEditor} from '../SqlMonacoEditor';\nimport {useStoreWithSqlEditor} from '../SqlEditorSlice';\n\nconst VALID_TABLE_OR_COLUMN_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;\n\nconst formSchema = z.object({\n tableName: z\n .string()\n .min(1, 'Table name is required')\n .regex(\n VALID_TABLE_OR_COLUMN_REGEX,\n 'Only letters, digits and underscores are allowed; should not start with a digit',\n ),\n query: z.string().min(1, 'Query is required'),\n});\n\nexport type CreateTableModalProps = {\n query: string;\n isOpen: boolean;\n onClose: () => void;\n editDataSource?: SqlQueryDataSource;\n onAddOrUpdateSqlQuery: (\n tableName: string,\n query: string,\n oldTableName?: string,\n ) => Promise<void>;\n};\n\ntype CreateTableFormProps = {\n query: string;\n onClose: () => void;\n editDataSource?: SqlQueryDataSource;\n onAddOrUpdateSqlQuery: (\n tableName: string,\n query: string,\n oldTableName?: string,\n ) => Promise<void>;\n};\n\nconst CreateTableForm: FC<CreateTableFormProps> = ({\n query,\n onClose,\n editDataSource,\n onAddOrUpdateSqlQuery,\n}) => {\n const connector = useStoreWithSqlEditor((state) => state.db.connector);\n const form = useForm<z.infer<typeof formSchema>>({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolver: zodResolver(formSchema as any),\n defaultValues: {\n tableName: editDataSource?.tableName ?? '',\n query: editDataSource?.sqlQuery ?? query.trim(),\n },\n });\n\n const onSubmit = useCallback(\n async (values: z.infer<typeof formSchema>) => {\n try {\n const {tableName, query} = values;\n await onAddOrUpdateSqlQuery(\n tableName,\n query,\n editDataSource?.tableName,\n );\n form.reset();\n onClose();\n } catch (err) {\n form.setError('root', {type: 'manual', message: `${err}`});\n }\n },\n [onAddOrUpdateSqlQuery, editDataSource?.tableName, onClose, form],\n );\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <DialogHeader>\n <DialogTitle>\n {editDataSource ? 'Edit table query' : 'Create table from query'}\n </DialogTitle>\n {!editDataSource && (\n <DialogDescription>\n Create a new table from the results of an SQL query.\n </DialogDescription>\n )}\n </DialogHeader>\n\n {form.formState.errors.root && (\n <Alert variant=\"destructive\">\n <AlertDescription className=\"whitespace-pre-wrap font-mono text-xs\">\n {form.formState.errors.root.message}\n </AlertDescription>\n </Alert>\n )}\n\n <FormField\n control={form.control}\n name=\"tableName\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Table name:</FormLabel>\n <FormControl>\n <Input {...field} className=\"font-mono\" autoFocus />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField\n control={form.control}\n name=\"query\"\n render={({field}) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>SQL query:</FormLabel>\n <FormControl>\n <SqlMonacoEditor\n connector={connector}\n value={field.value}\n onChange={field.onChange}\n className=\"h-[200px] w-full\"\n options={{\n scrollBeyondLastLine: false,\n automaticLayout: true,\n minimap: {enabled: false},\n wordWrap: 'on',\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={form.formState.isSubmitting}>\n {editDataSource ? 'Update' : 'Create'}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n );\n};\n\nconst CreateTableModal: FC<CreateTableModalProps> = (props) => {\n const {isOpen, onClose, query, editDataSource, onAddOrUpdateSqlQuery} = props;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"sm:max-w-[800px]\">\n {isOpen && (\n <CreateTableForm\n query={query}\n onClose={onClose}\n editDataSource={editDataSource}\n onAddOrUpdateSqlQuery={onAddOrUpdateSqlQuery}\n />\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nexport default CreateTableModal;\n"]}
|
|
1
|
+
{"version":3,"file":"CreateTableModal.js","sourceRoot":"","sources":["../../src/components/CreateTableModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import {zodResolver} from '@hookform/resolvers/zod';\nimport {makeQualifiedTableName} from '@sqlrooms/duckdb';\nimport {SqlQueryDataSource} from '@sqlrooms/room-shell';\nimport {\n Alert,\n AlertDescription,\n Button,\n Checkbox,\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Input,\n Label,\n Popover,\n PopoverContent,\n PopoverTrigger,\n Spinner,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n cn,\n} from '@sqlrooms/ui';\nimport {Check, ChevronsUpDown, HelpCircle} from 'lucide-react';\nimport {FC, useCallback, useMemo, useState} from 'react';\nimport {useForm} from 'react-hook-form';\nimport * as z from 'zod';\nimport {useStoreWithSqlEditor} from '../SqlEditorSlice';\nimport {SqlMonacoEditor} from '../SqlMonacoEditor';\n\nconst VALID_TABLE_OR_COLUMN_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;\n\nconst formSchema = z.object({\n tableName: z\n .string()\n .min(1, 'Table name is required')\n .regex(\n VALID_TABLE_OR_COLUMN_REGEX,\n 'Only letters, digits and underscores are allowed; should not start with a digit',\n ),\n query: z.string().min(1, 'Query is required'),\n schema: z.string().optional(),\n database: z.string().optional(),\n replace: z.boolean(),\n temp: z.boolean(),\n view: z.boolean(),\n});\n\ntype FormValues = z.infer<typeof formSchema>;\n\n/**\n * Initial values for the create table form.\n */\nexport type CreateTableFormInitialValues = Partial<{\n tableName: string;\n replace: boolean;\n temp: boolean;\n view: boolean;\n schema: string;\n database: string;\n}>;\n\nexport type CreateTableModalProps = {\n query: string;\n isOpen: boolean;\n onClose: () => void;\n editDataSource?: SqlQueryDataSource;\n /**\n * Allow multiple statements in the query. When true, preceding statements\n * will be executed before the final SELECT is wrapped in CREATE TABLE/VIEW.\n */\n allowMultipleStatements?: boolean;\n /**\n * Show schema/database selection UI.\n * @default false\n */\n showSchemaSelection?: boolean;\n /**\n * @deprecated Use createTableFromQuery directly instead.\n * When not provided, the modal will call createTableFromQuery directly.\n */\n onAddOrUpdateSqlQuery?: (\n tableName: string,\n query: string,\n oldTableName?: string,\n ) => Promise<void>;\n /**\n * Additional class name for the dialog content.\n */\n className?: string;\n /**\n * Initial values for the form fields.\n */\n initialValues?: CreateTableFormInitialValues;\n};\n\ntype CreateTableFormProps = {\n query: string;\n onClose: () => void;\n editDataSource?: SqlQueryDataSource;\n allowMultipleStatements?: boolean;\n showSchemaSelection?: boolean;\n onAddOrUpdateSqlQuery?: (\n tableName: string,\n query: string,\n oldTableName?: string,\n ) => Promise<void>;\n initialValues?: CreateTableFormInitialValues;\n};\n\n/**\n * Compact searchable combobox for selecting schema or database.\n */\nconst SchemaCombobox: FC<{\n value: string | undefined;\n onChange: (value: string | undefined) => void;\n options: string[];\n placeholder: string;\n searchPlaceholder: string;\n emptyMessage: string;\n disabled?: boolean;\n}> = ({\n value,\n onChange,\n options,\n placeholder,\n searchPlaceholder,\n emptyMessage,\n disabled,\n}) => {\n const [open, setOpen] = useState(false);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n className=\"h-9 w-full min-w-0 justify-between font-mono text-xs\"\n disabled={disabled}\n >\n <span className=\"min-w-0 truncate\">{value || placeholder}</span>\n <ChevronsUpDown className=\"ml-1 h-3 w-3 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[180px] p-0\">\n <Command>\n <CommandInput placeholder={searchPlaceholder} className=\"text-xs\" />\n <CommandList>\n <CommandEmpty className=\"text-xs\">{emptyMessage}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => (\n <CommandItem\n key={option}\n value={option}\n className=\"text-xs\"\n onSelect={(currentValue) => {\n onChange(currentValue === value ? undefined : currentValue);\n setOpen(false);\n }}\n >\n <Check\n className={cn(\n 'mr-2 h-3 w-3',\n value === option ? 'opacity-100' : 'opacity-0',\n )}\n />\n {option}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n};\n\n/**\n * Compact checkbox option with clickable label and tooltip.\n */\nconst OptionCheckbox: FC<{\n id: string;\n label: string;\n tooltip: string;\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n disabled?: boolean;\n}> = ({id, label, tooltip, checked, onCheckedChange, disabled}) => (\n <div className=\"flex items-center gap-1.5\">\n <Checkbox\n id={id}\n checked={checked}\n onCheckedChange={onCheckedChange}\n disabled={disabled}\n className=\"h-3.5 w-3.5\"\n />\n <Label\n htmlFor={id}\n className={cn(\n 'cursor-pointer text-xs font-normal',\n disabled && 'cursor-not-allowed opacity-50',\n )}\n >\n {label}\n </Label>\n <Tooltip>\n <TooltipTrigger asChild>\n <HelpCircle className=\"text-muted-foreground h-3 w-3 cursor-help\" />\n </TooltipTrigger>\n <TooltipContent side=\"top\" className=\"max-w-[200px] text-xs\">\n {tooltip}\n </TooltipContent>\n </Tooltip>\n </div>\n);\n\nconst CreateTableForm: FC<CreateTableFormProps> = ({\n query,\n onClose,\n editDataSource,\n allowMultipleStatements = false,\n showSchemaSelection = false,\n onAddOrUpdateSqlQuery,\n initialValues,\n}) => {\n const connector = useStoreWithSqlEditor((state) => state.db.connector);\n const createTableFromQuery = useStoreWithSqlEditor(\n (state) => state.db.createTableFromQuery,\n );\n const refreshTableSchemas = useStoreWithSqlEditor(\n (state) => state.db.refreshTableSchemas,\n );\n const tables = useStoreWithSqlEditor((state) => state.db.tables);\n const currentSchema = useStoreWithSqlEditor(\n (state) => state.db.currentSchema,\n );\n const currentDatabase = useStoreWithSqlEditor(\n (state) => state.db.currentDatabase,\n );\n\n // Extract unique schemas and databases from tables (excluding system ones)\n const {schemas, databases} = useMemo(() => {\n const schemaSet = new Set<string>();\n const databaseSet = new Set<string>();\n\n for (const table of tables) {\n if (table.table.schema && !table.table.schema.startsWith('pg_')) {\n schemaSet.add(table.table.schema);\n }\n if (table.table.database) {\n databaseSet.add(table.table.database);\n }\n }\n\n // Ensure current schema/database are included\n if (currentSchema) schemaSet.add(currentSchema);\n if (currentDatabase) databaseSet.add(currentDatabase);\n\n return {\n schemas: Array.from(schemaSet).sort(),\n databases: Array.from(databaseSet).sort(),\n };\n }, [tables, currentSchema, currentDatabase]);\n\n const form = useForm<FormValues>({\n resolver: zodResolver(formSchema as any),\n defaultValues: {\n tableName: editDataSource?.tableName ?? initialValues?.tableName ?? '',\n query: editDataSource?.sqlQuery ?? query.trim(),\n schema: initialValues?.schema ?? currentSchema,\n database: initialValues?.database ?? currentDatabase,\n replace: initialValues?.replace ?? false,\n temp: initialValues?.temp ?? false,\n view: initialValues?.view ?? false,\n },\n });\n\n const isSubmitting = form.formState.isSubmitting;\n\n const onSubmit = useCallback(\n async (values: FormValues) => {\n try {\n const {tableName, query, schema, database, replace, temp, view} =\n values;\n\n if (onAddOrUpdateSqlQuery) {\n // Legacy path: use the callback\n await onAddOrUpdateSqlQuery(\n tableName,\n query,\n editDataSource?.tableName,\n );\n } else {\n // New path: call createTableFromQuery directly\n const qualifiedName =\n schema || database\n ? makeQualifiedTableName({table: tableName, schema, database})\n : tableName;\n\n await createTableFromQuery(qualifiedName, query, {\n replace,\n temp,\n view,\n allowMultipleStatements,\n });\n\n // Refresh table schemas to show the new table\n await refreshTableSchemas();\n }\n\n form.reset();\n onClose();\n } catch (err) {\n form.setError('root', {type: 'manual', message: `${err}`});\n }\n },\n [\n onAddOrUpdateSqlQuery,\n editDataSource?.tableName,\n createTableFromQuery,\n allowMultipleStatements,\n refreshTableSchemas,\n onClose,\n form,\n ],\n );\n\n const watchView = form.watch('view');\n const watchTemp = form.watch('temp');\n const watchTableName = form.watch('tableName');\n\n return (\n <TooltipProvider delayDuration={200}>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <DialogHeader>\n <DialogTitle>\n {editDataSource\n ? 'Edit table query'\n : watchView\n ? 'Create view from query'\n : 'Create table from query'}\n </DialogTitle>\n {!editDataSource && (\n <DialogDescription>\n {watchView\n ? 'Create a new view from an SQL query.'\n : 'Create a new table from the results of an SQL query.'}\n </DialogDescription>\n )}\n </DialogHeader>\n\n {form.formState.errors.root && (\n <Alert variant=\"destructive\">\n <AlertDescription className=\"whitespace-pre-wrap font-mono text-xs\">\n {form.formState.errors.root.message}\n </AlertDescription>\n </Alert>\n )}\n\n {/* Table name, schema, database in single row */}\n <div className=\"flex items-end gap-3\">\n <FormField\n control={form.control}\n name=\"tableName\"\n render={({field}) => (\n <FormItem className=\"min-w-0 flex-[2]\">\n <FormLabel className=\"text-xs\">\n {watchView ? 'View name' : 'Table name'}\n </FormLabel>\n <FormControl>\n <Input\n {...field}\n className=\"h-9 font-mono text-xs\"\n autoFocus\n disabled={isSubmitting}\n />\n </FormControl>\n <FormMessage className=\"text-xs\" />\n </FormItem>\n )}\n />\n\n {showSchemaSelection && (\n <>\n <FormField\n control={form.control}\n name=\"schema\"\n render={({field}) => (\n <FormItem className=\"min-w-0 flex-1\">\n <FormLabel className=\"text-xs\">Schema</FormLabel>\n <FormControl>\n <SchemaCombobox\n value={field.value}\n onChange={field.onChange}\n options={schemas}\n placeholder=\"Schema...\"\n searchPlaceholder=\"Search...\"\n emptyMessage=\"No schemas.\"\n disabled={isSubmitting}\n />\n </FormControl>\n </FormItem>\n )}\n />\n\n {(databases.length > 1 || watchTemp) && (\n <FormField\n control={form.control}\n name=\"database\"\n render={({field}) => (\n <FormItem className=\"min-w-0 flex-1\">\n <FormLabel className=\"text-xs\">Database</FormLabel>\n <FormControl>\n <SchemaCombobox\n value={watchTemp ? 'temp' : field.value}\n onChange={field.onChange}\n options={databases}\n placeholder=\"Database...\"\n searchPlaceholder=\"Search...\"\n emptyMessage=\"No databases.\"\n disabled={isSubmitting || watchTemp}\n />\n </FormControl>\n </FormItem>\n )}\n />\n )}\n </>\n )}\n </div>\n\n <FormField\n control={form.control}\n name=\"query\"\n render={({field}) => (\n <FormItem className=\"relative flex h-[200px] flex-col\">\n <FormControl>\n <SqlMonacoEditor\n connector={connector}\n value={field.value}\n onChange={field.onChange}\n className=\"absolute inset-0 h-full w-full\"\n options={{\n scrollBeyondLastLine: false,\n automaticLayout: true,\n minimap: {enabled: false},\n wordWrap: 'on',\n folding: false,\n lineNumbers: 'off',\n readOnly: isSubmitting,\n fixedOverflowWidgets: false, // default true doesn't work well in a modal\n }}\n />\n </FormControl>\n <FormMessage className=\"text-xs\" />\n </FormItem>\n )}\n />\n\n {/* Compact options row */}\n {!onAddOrUpdateSqlQuery && (\n <div className=\"flex items-center gap-6 rounded-md border px-3 py-2\">\n <FormField\n control={form.control}\n name=\"view\"\n render={({field}) => (\n <OptionCheckbox\n id=\"create-table-view\"\n label=\"View\"\n tooltip=\"Create a view instead of a table. Views store the query, not the data.\"\n checked={field.value}\n onCheckedChange={field.onChange}\n disabled={isSubmitting}\n />\n )}\n />\n\n {/* <FormField\n control={form.control}\n name=\"temp\"\n render={({field}) => (\n <OptionCheckbox\n id=\"create-table-temp\"\n label=\"Temporary\"\n tooltip={`${watchView ? 'View' : 'Table'} will be deleted when the session ends.`}\n checked={field.value}\n onCheckedChange={field.onChange}\n disabled={isSubmitting}\n />\n )}\n /> */}\n\n <FormField\n control={form.control}\n name=\"replace\"\n render={({field}) => (\n <OptionCheckbox\n id=\"create-table-replace\"\n label=\"Overwrite\"\n tooltip={`Overwrite existing ${watchView ? 'view' : 'table'} with the same name if it exists.`}\n checked={field.value}\n onCheckedChange={field.onChange}\n disabled={isSubmitting}\n />\n )}\n />\n </div>\n )}\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={isSubmitting}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isSubmitting || !watchTableName?.trim()}\n >\n {isSubmitting && <Spinner className=\"mr-2\" />}\n {editDataSource ? 'Update' : 'Create'}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </TooltipProvider>\n );\n};\n\nconst CreateTableModal: FC<CreateTableModalProps> = (props) => {\n const {\n isOpen,\n onClose,\n query,\n editDataSource,\n allowMultipleStatements,\n showSchemaSelection,\n onAddOrUpdateSqlQuery,\n className,\n initialValues,\n } = props;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className={cn('w-3xl max-w-[80%]', className)}>\n {isOpen && (\n <CreateTableForm\n query={query}\n onClose={onClose}\n editDataSource={editDataSource}\n allowMultipleStatements={allowMultipleStatements}\n showSchemaSelection={showSchemaSelection}\n onAddOrUpdateSqlQuery={onAddOrUpdateSqlQuery}\n initialValues={initialValues}\n />\n )}\n </DialogContent>\n </Dialog>\n );\n};\n\nexport default CreateTableModal;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEditorPanel.d.ts","sourceRoot":"","sources":["../../src/components/QueryEditorPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"QueryEditorPanel.d.ts","sourceRoot":"","sources":["../../src/components/QueryEditorPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAuC5D,CAAC"}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn
|
|
2
|
+
import { cn } from '@sqlrooms/ui';
|
|
3
3
|
import { useStoreWithSqlEditor } from '../SqlEditorSlice';
|
|
4
4
|
import { QueryEditorPanelActions } from './QueryEditorPanelActions';
|
|
5
5
|
import { QueryEditorPanelEditor } from './QueryEditorPanelEditor';
|
|
6
6
|
import { QueryEditorPanelTabsList } from './QueryEditorPanelTabsList';
|
|
7
7
|
export const QueryEditorPanel = ({ className, }) => {
|
|
8
|
-
// Get state and actions from store in a single call
|
|
9
8
|
const selectedQueryId = useStoreWithSqlEditor((s) => s.sqlEditor.config.selectedQueryId);
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
return (_jsxs(
|
|
9
|
+
const openTabs = useStoreWithSqlEditor((s) => s.sqlEditor.config.openTabs);
|
|
10
|
+
const isSelectedOpen = openTabs.includes(selectedQueryId);
|
|
11
|
+
return (_jsxs("div", { className: cn('flex h-full flex-col',
|
|
13
12
|
// this is for Monaco's completion menu to not being cut off
|
|
14
|
-
'overflow-visible', className), children: [_jsxs("div", { className: "border-border flex items-center gap-4 border-b px-2 pt-1", children: [_jsx(QueryEditorPanelTabsList, {}), _jsx("div", { className: "flex-1" }), _jsx(QueryEditorPanelActions, {})] }),
|
|
13
|
+
'overflow-visible', className), children: [_jsxs("div", { className: "border-border flex items-center gap-4 border-b px-2 pt-1", children: [_jsx(QueryEditorPanelTabsList, {}), _jsx("div", { className: "flex-1" }), _jsx(QueryEditorPanelActions, {})] }), isSelectedOpen ? (_jsx("div", { className: "bg-background h-full w-full py-1", children: _jsx("div", { className: "relative h-full flex-grow", children: _jsx("div", { className: "absolute inset-0", children: _jsx(QueryEditorPanelEditor, { queryId: selectedQueryId }) }) }) })) : (_jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-sm", children: "No open queries" }))] }));
|
|
15
14
|
};
|
|
16
15
|
//# sourceMappingURL=QueryEditorPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEditorPanel.js","sourceRoot":"","sources":["../../src/components/QueryEditorPanel.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"QueryEditorPanel.js","sourceRoot":"","sources":["../../src/components/QueryEditorPanel.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAChC,OAAO,EAAC,qBAAqB,EAAC,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAC,uBAAuB,EAAC,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAC,sBAAsB,EAAC,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAC,wBAAwB,EAAC,MAAM,4BAA4B,CAAC;AAOpE,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,SAAS,GACV,EAAE,EAAE;IACH,MAAM,eAAe,GAAG,qBAAqB,CAC3C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,eAAe,CAC1C,CAAC;IACF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE3E,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAE1D,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,sBAAsB;QACtB,4DAA4D;QAC5D,kBAAkB,EAClB,SAAS,CACV,aAED,eAAK,SAAS,EAAC,0DAA0D,aACvE,KAAC,wBAAwB,KAAG,EAC5B,cAAK,SAAS,EAAC,QAAQ,GAAG,EAC1B,KAAC,uBAAuB,KAAG,IACvB,EACL,cAAc,CAAC,CAAC,CAAC,CAChB,cAAK,SAAS,EAAC,kCAAkC,YAC/C,cAAK,SAAS,EAAC,2BAA2B,YACxC,cAAK,SAAS,EAAC,kBAAkB,YAC/B,KAAC,sBAAsB,IAAC,OAAO,EAAE,eAAe,GAAI,GAChD,GACF,GACF,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,uEAAuE,gCAEhF,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import React from 'react';\nimport {cn} from '@sqlrooms/ui';\nimport {useStoreWithSqlEditor} from '../SqlEditorSlice';\nimport {QueryEditorPanelActions} from './QueryEditorPanelActions';\nimport {QueryEditorPanelEditor} from './QueryEditorPanelEditor';\nimport {QueryEditorPanelTabsList} from './QueryEditorPanelTabsList';\n\nexport interface QueryEditorPanelProps {\n /** Custom class name for styling */\n className?: string;\n}\n\nexport const QueryEditorPanel: React.FC<QueryEditorPanelProps> = ({\n className,\n}) => {\n const selectedQueryId = useStoreWithSqlEditor(\n (s) => s.sqlEditor.config.selectedQueryId,\n );\n const openTabs = useStoreWithSqlEditor((s) => s.sqlEditor.config.openTabs);\n\n const isSelectedOpen = openTabs.includes(selectedQueryId);\n\n return (\n <div\n className={cn(\n 'flex h-full flex-col',\n // this is for Monaco's completion menu to not being cut off\n 'overflow-visible',\n className,\n )}\n >\n <div className=\"border-border flex items-center gap-4 border-b px-2 pt-1\">\n <QueryEditorPanelTabsList />\n <div className=\"flex-1\" />\n <QueryEditorPanelActions />\n </div>\n {isSelectedOpen ? (\n <div className=\"bg-background h-full w-full py-1\">\n <div className=\"relative h-full flex-grow\">\n <div className=\"absolute inset-0\">\n <QueryEditorPanelEditor queryId={selectedQueryId} />\n </div>\n </div>\n </div>\n ) : (\n <div className=\"text-muted-foreground flex h-full items-center justify-center text-sm\">\n No open queries\n </div>\n )}\n </div>\n );\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEditorPanelTabsList.d.ts","sourceRoot":"","sources":["../../src/components/QueryEditorPanelTabsList.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"QueryEditorPanelTabsList.d.ts","sourceRoot":"","sources":["../../src/components/QueryEditorPanelTabsList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,eAAO,MAAM,wBAAwB,EAAE,KAAK,CAAC,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAC,CAgInE,CAAC"}
|
|
@@ -1,58 +1,38 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { useCallback,
|
|
2
|
+
import { TabStrip } from '@sqlrooms/ui';
|
|
3
|
+
import { PencilIcon, TrashIcon } from 'lucide-react';
|
|
4
|
+
import { useCallback, useState } from 'react';
|
|
5
5
|
import { useStoreWithSqlEditor } from '../SqlEditorSlice';
|
|
6
6
|
import DeleteSqlQueryModal from './DeleteSqlQueryModal';
|
|
7
|
-
import { QueryTabMenuItem } from './QueryTabMenuItem';
|
|
8
7
|
import RenameSqlQueryModal from './RenameSqlQueryModal';
|
|
9
8
|
export const QueryEditorPanelTabsList = ({ className, }) => {
|
|
10
9
|
const queries = useStoreWithSqlEditor((s) => s.sqlEditor.config.queries);
|
|
11
|
-
const
|
|
10
|
+
const openTabs = useStoreWithSqlEditor((s) => s.sqlEditor.config.openTabs);
|
|
12
11
|
const selectedQueryId = useStoreWithSqlEditor((s) => s.sqlEditor.config.selectedQueryId);
|
|
13
|
-
const openedTabs = queries.filter((q) => !closedTabIds.includes(q.id));
|
|
14
|
-
const closedTabs = queries.filter((q) => closedTabIds.includes(q.id));
|
|
15
12
|
const renameQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.renameQueryTab);
|
|
16
|
-
// Local state for modals and editing
|
|
17
|
-
const [queryToDelete, setQueryToDelete] = useState(null);
|
|
18
|
-
const [editingQueryId, setEditingQueryId] = useState(null);
|
|
19
|
-
const [queryToRename, setQueryToRename] = useState(null);
|
|
20
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
21
|
-
// Ref for the scrollable container
|
|
22
|
-
const scrollContainerRef = useRef(null);
|
|
23
|
-
// Track previous selectedQueryId to only scroll when it actually changes
|
|
24
|
-
const prevSelectedQueryIdRef = useRef(null);
|
|
25
13
|
const closeQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.closeQueryTab);
|
|
26
|
-
const
|
|
14
|
+
const setSelectedQueryId = useStoreWithSqlEditor((s) => s.sqlEditor.setSelectedQueryId);
|
|
27
15
|
const createQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.createQueryTab);
|
|
28
16
|
const deleteQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.deleteQueryTab);
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
const setOpenTabs = useStoreWithSqlEditor((s) => s.sqlEditor.setOpenTabs);
|
|
18
|
+
const [queryToDelete, setQueryToDelete] = useState(null);
|
|
19
|
+
const [queryToRename, setQueryToRename] = useState(null);
|
|
20
|
+
const handleRenameRequest = useCallback((queryId) => {
|
|
21
|
+
const query = queries.find((q) => q.id === queryId);
|
|
22
|
+
if (query) {
|
|
23
|
+
setQueryToRename({ id: queryId, name: query.name });
|
|
24
|
+
}
|
|
25
|
+
}, [queries]);
|
|
33
26
|
const handleFinishRename = useCallback((newName) => {
|
|
34
27
|
if (queryToRename) {
|
|
35
28
|
renameQueryTab(queryToRename.id, newName);
|
|
36
29
|
}
|
|
37
30
|
setQueryToRename(null);
|
|
38
31
|
}, [queryToRename, renameQueryTab]);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (newName.trim() !== '') {
|
|
42
|
-
renameQueryTab(queryId, newName.trim());
|
|
43
|
-
}
|
|
44
|
-
setEditingQueryId(null);
|
|
45
|
-
}, [renameQueryTab]);
|
|
46
|
-
// Handle double click to start editing
|
|
47
|
-
const handleDoubleClick = useCallback((queryId) => {
|
|
48
|
-
setEditingQueryId(queryId);
|
|
49
|
-
}, []);
|
|
50
|
-
// Handle delete query
|
|
51
|
-
const handleDeleteQuery = useCallback((queryId) => {
|
|
52
|
-
// Find the query to check if it's empty
|
|
53
|
-
const queryToDelete = queries.find((q) => q.id === queryId);
|
|
32
|
+
const handleDelete = useCallback((queryId) => {
|
|
33
|
+
const query = queries.find((q) => q.id === queryId);
|
|
54
34
|
// If query is empty (no content), delete immediately without confirmation
|
|
55
|
-
if (
|
|
35
|
+
if (query && query.query.trim() === '') {
|
|
56
36
|
deleteQueryTab(queryId);
|
|
57
37
|
}
|
|
58
38
|
else {
|
|
@@ -60,89 +40,12 @@ export const QueryEditorPanelTabsList = ({ className, }) => {
|
|
|
60
40
|
setQueryToDelete(queryId);
|
|
61
41
|
}
|
|
62
42
|
}, [queries, deleteQueryTab]);
|
|
63
|
-
|
|
64
|
-
const handleNewQuery = useCallback(() => {
|
|
65
|
-
createQueryTab();
|
|
66
|
-
}, [createQueryTab]);
|
|
67
|
-
useEffect(() => {
|
|
68
|
-
if (!selectedQueryId)
|
|
69
|
-
return;
|
|
70
|
-
// Only react when the selected tab actually changes
|
|
71
|
-
if (prevSelectedQueryIdRef.current === selectedQueryId)
|
|
72
|
-
return;
|
|
73
|
-
prevSelectedQueryIdRef.current = selectedQueryId;
|
|
74
|
-
const container = scrollContainerRef.current;
|
|
75
|
-
if (!container)
|
|
76
|
-
return;
|
|
77
|
-
// Only scroll if the selected tab is still open
|
|
78
|
-
const isOpened = openedTabs.some((tab) => tab.id === selectedQueryId);
|
|
79
|
-
if (!isOpened)
|
|
80
|
-
return;
|
|
81
|
-
// After the DOM is painted
|
|
82
|
-
const frameId = requestAnimationFrame(() => {
|
|
83
|
-
const activeTab = container.querySelector('[data-state="active"]');
|
|
84
|
-
if (!activeTab)
|
|
85
|
-
return;
|
|
86
|
-
const visibleLeft = container.scrollLeft;
|
|
87
|
-
const visibleRight = visibleLeft + container.clientWidth;
|
|
88
|
-
const tabLeft = activeTab.offsetLeft;
|
|
89
|
-
const tabRight = tabLeft + activeTab.offsetWidth;
|
|
90
|
-
// If the tab is already fully visible, don't scroll
|
|
91
|
-
if (tabLeft >= visibleLeft && tabRight <= visibleRight)
|
|
92
|
-
return;
|
|
93
|
-
let newScrollLeft = visibleLeft;
|
|
94
|
-
if (tabLeft < visibleLeft) {
|
|
95
|
-
newScrollLeft = Math.max(0, tabLeft - 10);
|
|
96
|
-
}
|
|
97
|
-
else if (tabRight > visibleRight) {
|
|
98
|
-
newScrollLeft = Math.min(container.scrollWidth - container.clientWidth, tabRight - container.clientWidth + 10);
|
|
99
|
-
}
|
|
100
|
-
if (newScrollLeft !== visibleLeft) {
|
|
101
|
-
container.scrollTo({
|
|
102
|
-
left: newScrollLeft,
|
|
103
|
-
behavior: 'smooth',
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
return () => {
|
|
108
|
-
cancelAnimationFrame(frameId);
|
|
109
|
-
};
|
|
110
|
-
}, [selectedQueryId, openedTabs]);
|
|
111
|
-
const handleConfirmDeleteQuery = useCallback(() => {
|
|
43
|
+
const handleConfirmDelete = useCallback(() => {
|
|
112
44
|
if (queryToDelete) {
|
|
113
45
|
deleteQueryTab(queryToDelete);
|
|
114
46
|
setQueryToDelete(null);
|
|
115
47
|
}
|
|
116
48
|
}, [queryToDelete, deleteQueryTab]);
|
|
117
|
-
|
|
118
|
-
if (!searchQuery.trim())
|
|
119
|
-
return closedTabs;
|
|
120
|
-
const lowerQuery = searchQuery.toLowerCase();
|
|
121
|
-
return closedTabs.filter((tab) => tab.name.toLowerCase().includes(lowerQuery));
|
|
122
|
-
}, [closedTabs, searchQuery]);
|
|
123
|
-
const filteredOpenedTabs = useMemo(() => {
|
|
124
|
-
if (!searchQuery.trim())
|
|
125
|
-
return openedTabs;
|
|
126
|
-
const lowerQuery = searchQuery.toLowerCase();
|
|
127
|
-
return openedTabs.filter((tab) => tab.name.toLowerCase().includes(lowerQuery));
|
|
128
|
-
}, [openedTabs, searchQuery]);
|
|
129
|
-
const renderTabGroup = useCallback((tabs, emptyMessage) => {
|
|
130
|
-
if (tabs.length === 0) {
|
|
131
|
-
return (_jsx(DropdownMenuItem, { className: "items-center justify-center text-xs", disabled: true, children: emptyMessage }));
|
|
132
|
-
}
|
|
133
|
-
return tabs.map((tab) => (_jsx(QueryTabMenuItem, { tab: tab, onRestore: () => openQueryTab(tab.id), onRename: () => handleStartRename(tab.id, tab.name), onDelete: () => handleDeleteQuery(tab.id) }, tab.id)));
|
|
134
|
-
}, [openQueryTab, handleStartRename, handleDeleteQuery]);
|
|
135
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TabsList, { className: cn('flex min-w-0 justify-start gap-1 bg-transparent p-0 pt-1.5', className), children: [_jsx("div", { ref: scrollContainerRef, className: "flex h-full min-w-0 items-center gap-1 overflow-x-auto overflow-y-hidden pr-1 [&::-webkit-scrollbar]:hidden", children: openedTabs.map((q) => (_jsxs(TabsTrigger, { value: q.id, className: "data-[state=inactive]:hover:bg-primary/5 flex h-full min-w-[100px] max-w-[200px] flex-shrink-0 items-center justify-between gap-2 overflow-hidden rounded-b-none py-0 pl-4 pr-2 font-normal data-[state=active]:shadow-none", children: [_jsx("div", { className: "flex min-w-0 items-center", onDoubleClick: () => handleDoubleClick(q.id), children: editingQueryId !== q.id ? (_jsx("div", { className: "truncate text-sm", children: q.name })) : (_jsx(EditableText, { value: q.name, onChange: (newName) => handleRename(q.id, newName), className: "h-6 min-w-0 flex-1 truncate text-sm shadow-none", isEditing: editingQueryId === q.id, onEditingChange: (isEditing) => {
|
|
136
|
-
if (!isEditing) {
|
|
137
|
-
setEditingQueryId(null);
|
|
138
|
-
}
|
|
139
|
-
} })) }), _jsx(Button, { asChild: true, size: "xs", variant: "ghost", className: "hover:bg-primary/10 h-4 w-4 p-1", onMouseDown: (e) => {
|
|
140
|
-
e.stopPropagation();
|
|
141
|
-
e.preventDefault();
|
|
142
|
-
}, onClick: (e) => {
|
|
143
|
-
e.stopPropagation();
|
|
144
|
-
e.preventDefault();
|
|
145
|
-
closeQueryTab(q.id);
|
|
146
|
-
}, children: _jsx(XIcon, { className: "h-5 w-5" }) })] }, q.id))) }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { size: "icon", variant: "ghost", className: "ml-2 h-5 w-5 flex-shrink-0", children: _jsx(ListCollapseIcon, { className: "h-4 w-4" }) }) }), _jsxs(DropdownMenuContent, { onCloseAutoFocus: (e) => e.preventDefault(), className: "max-h-[400px] max-w-[240px] overflow-y-auto", children: [_jsxs("div", { className: "flex items-center gap-1 px-2", children: [_jsx(SearchIcon, { className: "text-muted-foreground", size: 14 }), _jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: (e) => e.stopPropagation(), onKeyUp: (e) => e.stopPropagation(), className: "border-none text-xs shadow-none focus-visible:ring-0", placeholder: "Search..." })] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { className: "text-muted-foreground text-xs font-normal", children: "Closed" }), renderTabGroup(filteredClosedTabs, 'No closed tabs')] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { className: "text-muted-foreground text-xs font-normal", children: "Opened" }), renderTabGroup(filteredOpenedTabs, 'No opened tabs')] })] })] }), _jsx(Button, { size: "icon", variant: "ghost", onClick: handleNewQuery, className: "h-5 w-5 flex-shrink-0", children: _jsx(PlusIcon, { className: "h-4 w-4" }) })] }), _jsx(DeleteSqlQueryModal, { isOpen: queryToDelete !== null, onClose: () => setQueryToDelete(null), onConfirm: handleConfirmDeleteQuery }), _jsx(RenameSqlQueryModal, { isOpen: queryToRename !== null, onClose: () => setQueryToRename(null), initialName: queryToRename?.name ?? '', onRename: handleFinishRename })] }));
|
|
49
|
+
return (_jsxs(_Fragment, { children: [_jsx(TabStrip, { className: className, tabs: queries, openTabs: openTabs, selectedTabId: selectedQueryId, onClose: closeQueryTab, onOpenTabsChange: setOpenTabs, onSelect: setSelectedQueryId, onCreate: createQueryTab, onRename: renameQueryTab, renderTabMenu: (tab) => (_jsxs(_Fragment, { children: [_jsxs(TabStrip.MenuItem, { onClick: () => handleRenameRequest(tab.id), children: [_jsx(PencilIcon, { className: "mr-2 h-4 w-4" }), "Rename"] }), _jsx(TabStrip.MenuSeparator, {}), _jsxs(TabStrip.MenuItem, { variant: "destructive", onClick: () => handleDelete(tab.id), children: [_jsx(TrashIcon, { className: "mr-2 h-4 w-4" }), "Delete"] })] })), renderSearchItemActions: (tab) => (_jsxs(_Fragment, { children: [_jsx(TabStrip.SearchItemAction, { icon: _jsx(PencilIcon, { className: "h-3 w-3", size: 5 }), "aria-label": `Rename ${tab.name}`, onClick: () => handleRenameRequest(tab.id) }), _jsx(TabStrip.SearchItemAction, { icon: _jsx(TrashIcon, { className: "h-3 w-3" }), "aria-label": `Delete ${tab.name}`, onClick: () => handleDelete(tab.id) })] })) }), _jsx(DeleteSqlQueryModal, { isOpen: queryToDelete !== null, onClose: () => setQueryToDelete(null), onConfirm: handleConfirmDelete }), _jsx(RenameSqlQueryModal, { isOpen: queryToRename !== null, onClose: () => setQueryToRename(null), initialName: queryToRename?.name ?? '', onRename: handleFinishRename })] }));
|
|
147
50
|
};
|
|
148
51
|
//# sourceMappingURL=QueryEditorPanelTabsList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryEditorPanelTabsList.js","sourceRoot":"","sources":["../../src/components/QueryEditorPanelTabsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Button,\n cn,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n EditableText,\n Input,\n TabsList,\n TabsTrigger,\n} from '@sqlrooms/ui';\nimport {ListCollapseIcon, PlusIcon, XIcon, SearchIcon} from 'lucide-react';\nimport React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {useStoreWithSqlEditor} from '../SqlEditorSlice';\nimport DeleteSqlQueryModal from './DeleteSqlQueryModal';\nimport {QueryTabMenuItem} from './QueryTabMenuItem';\nimport RenameSqlQueryModal from './RenameSqlQueryModal';\n\nexport const QueryEditorPanelTabsList: React.FC<{className?: string}> = ({\n className,\n}) => {\n const queries = useStoreWithSqlEditor((s) => s.sqlEditor.config.queries);\n const closedTabIds = useStoreWithSqlEditor(\n (s) => s.sqlEditor.config.closedTabIds,\n );\n const selectedQueryId = useStoreWithSqlEditor(\n (s) => s.sqlEditor.config.selectedQueryId,\n );\n const openedTabs = queries.filter((q) => !closedTabIds.includes(q.id));\n const closedTabs = queries.filter((q) => closedTabIds.includes(q.id));\n\n const renameQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.renameQueryTab,\n );\n\n // Local state for modals and editing\n const [queryToDelete, setQueryToDelete] = useState<string | null>(null);\n const [editingQueryId, setEditingQueryId] = useState<string | null>(null);\n const [queryToRename, setQueryToRename] = useState<{\n id: string;\n name: string;\n } | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n\n // Ref for the scrollable container\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n // Track previous selectedQueryId to only scroll when it actually changes\n const prevSelectedQueryIdRef = useRef<string | null>(null);\n\n const closeQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.closeQueryTab);\n const openQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.openQueryTab);\n const createQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.createQueryTab,\n );\n const deleteQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.deleteQueryTab,\n );\n\n // Handle rename query\n const handleStartRename = useCallback(\n (queryId: string, currentName: string) => {\n setQueryToRename({id: queryId, name: currentName});\n },\n [],\n );\n\n const handleFinishRename = useCallback(\n (newName: string) => {\n if (queryToRename) {\n renameQueryTab(queryToRename.id, newName);\n }\n setQueryToRename(null);\n },\n [queryToRename, renameQueryTab],\n );\n // Handle rename query\n const handleRename = useCallback(\n (queryId: string, newName: string) => {\n if (newName.trim() !== '') {\n renameQueryTab(queryId, newName.trim());\n }\n setEditingQueryId(null);\n },\n [renameQueryTab],\n );\n\n // Handle double click to start editing\n const handleDoubleClick = useCallback((queryId: string) => {\n setEditingQueryId(queryId);\n }, []);\n\n // Handle delete query\n const handleDeleteQuery = useCallback(\n (queryId: string) => {\n // Find the query to check if it's empty\n const queryToDelete = queries.find((q) => q.id === queryId);\n\n // If query is empty (no content), delete immediately without confirmation\n if (queryToDelete && queryToDelete.query.trim() === '') {\n deleteQueryTab(queryId);\n } else {\n // Otherwise, show confirmation modal\n setQueryToDelete(queryId);\n }\n },\n [queries, deleteQueryTab],\n );\n\n // Handle new query creation\n const handleNewQuery = useCallback(() => {\n createQueryTab();\n }, [createQueryTab]);\n\n useEffect(() => {\n if (!selectedQueryId) return;\n\n // Only react when the selected tab actually changes\n if (prevSelectedQueryIdRef.current === selectedQueryId) return;\n prevSelectedQueryIdRef.current = selectedQueryId;\n\n const container = scrollContainerRef.current;\n if (!container) return;\n\n // Only scroll if the selected tab is still open\n const isOpened = openedTabs.some((tab) => tab.id === selectedQueryId);\n if (!isOpened) return;\n\n // After the DOM is painted\n const frameId = requestAnimationFrame(() => {\n const activeTab = container.querySelector<HTMLElement>(\n '[data-state=\"active\"]',\n );\n if (!activeTab) return;\n\n const visibleLeft = container.scrollLeft;\n const visibleRight = visibleLeft + container.clientWidth;\n\n const tabLeft = activeTab.offsetLeft;\n const tabRight = tabLeft + activeTab.offsetWidth;\n\n // If the tab is already fully visible, don't scroll\n if (tabLeft >= visibleLeft && tabRight <= visibleRight) return;\n\n let newScrollLeft = visibleLeft;\n\n if (tabLeft < visibleLeft) {\n newScrollLeft = Math.max(0, tabLeft - 10);\n } else if (tabRight > visibleRight) {\n newScrollLeft = Math.min(\n container.scrollWidth - container.clientWidth,\n tabRight - container.clientWidth + 10,\n );\n }\n\n if (newScrollLeft !== visibleLeft) {\n container.scrollTo({\n left: newScrollLeft,\n behavior: 'smooth',\n });\n }\n });\n\n return () => {\n cancelAnimationFrame(frameId);\n };\n }, [selectedQueryId, openedTabs]);\n\n const handleConfirmDeleteQuery = useCallback(() => {\n if (queryToDelete) {\n deleteQueryTab(queryToDelete);\n setQueryToDelete(null);\n }\n }, [queryToDelete, deleteQueryTab]);\n\n const filteredClosedTabs = useMemo(() => {\n if (!searchQuery.trim()) return closedTabs;\n const lowerQuery = searchQuery.toLowerCase();\n return closedTabs.filter((tab) =>\n tab.name.toLowerCase().includes(lowerQuery),\n );\n }, [closedTabs, searchQuery]);\n\n const filteredOpenedTabs = useMemo(() => {\n if (!searchQuery.trim()) return openedTabs;\n const lowerQuery = searchQuery.toLowerCase();\n return openedTabs.filter((tab) =>\n tab.name.toLowerCase().includes(lowerQuery),\n );\n }, [openedTabs, searchQuery]);\n\n const renderTabGroup = useCallback(\n (tabs: typeof queries, emptyMessage: string) => {\n if (tabs.length === 0) {\n return (\n <DropdownMenuItem\n className=\"items-center justify-center text-xs\"\n disabled\n >\n {emptyMessage}\n </DropdownMenuItem>\n );\n }\n\n return tabs.map((tab) => (\n <QueryTabMenuItem\n key={tab.id}\n tab={tab}\n onRestore={() => openQueryTab(tab.id)}\n onRename={() => handleStartRename(tab.id, tab.name)}\n onDelete={() => handleDeleteQuery(tab.id)}\n />\n ));\n },\n [openQueryTab, handleStartRename, handleDeleteQuery],\n );\n\n return (\n <>\n <TabsList\n className={cn(\n 'flex min-w-0 justify-start gap-1 bg-transparent p-0 pt-1.5',\n className,\n )}\n >\n <div\n ref={scrollContainerRef}\n className=\"flex h-full min-w-0 items-center gap-1 overflow-x-auto overflow-y-hidden pr-1 [&::-webkit-scrollbar]:hidden\"\n >\n {openedTabs.map((q) => (\n <TabsTrigger\n key={q.id}\n value={q.id}\n className=\"data-[state=inactive]:hover:bg-primary/5 flex h-full min-w-[100px] max-w-[200px] flex-shrink-0 items-center justify-between gap-2 overflow-hidden rounded-b-none py-0 pl-4 pr-2 font-normal data-[state=active]:shadow-none\"\n >\n <div\n className=\"flex min-w-0 items-center\"\n onDoubleClick={() => handleDoubleClick(q.id)}\n >\n {editingQueryId !== q.id ? (\n <div className=\"truncate text-sm\">{q.name}</div>\n ) : (\n <EditableText\n value={q.name}\n onChange={(newName: string) => handleRename(q.id, newName)}\n className=\"h-6 min-w-0 flex-1 truncate text-sm shadow-none\"\n isEditing={editingQueryId === q.id}\n onEditingChange={(isEditing) => {\n if (!isEditing) {\n setEditingQueryId(null);\n }\n }}\n />\n )}\n </div>\n\n <Button\n asChild\n size=\"xs\"\n variant=\"ghost\"\n className=\"hover:bg-primary/10 h-4 w-4 p-1\"\n onMouseDown={(e) => {\n e.stopPropagation();\n e.preventDefault();\n }}\n onClick={(e) => {\n e.stopPropagation();\n e.preventDefault();\n closeQueryTab(q.id);\n }}\n >\n <XIcon className=\"h-5 w-5\" />\n </Button>\n </TabsTrigger>\n ))}\n </div>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n size=\"icon\"\n variant=\"ghost\"\n className=\"ml-2 h-5 w-5 flex-shrink-0\"\n >\n <ListCollapseIcon className=\"h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n onCloseAutoFocus={(e) => e.preventDefault()}\n className=\"max-h-[400px] max-w-[240px] overflow-y-auto\"\n >\n <div className=\"flex items-center gap-1 px-2\">\n <SearchIcon className=\"text-muted-foreground\" size={14} />\n <Input\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n onKeyDown={(e) => e.stopPropagation()}\n onKeyUp={(e) => e.stopPropagation()}\n className=\"border-none text-xs shadow-none focus-visible:ring-0\"\n placeholder=\"Search...\"\n />\n </div>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuLabel className=\"text-muted-foreground text-xs font-normal\">\n Closed\n </DropdownMenuLabel>\n {renderTabGroup(filteredClosedTabs, 'No closed tabs')}\n </DropdownMenuGroup>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuLabel className=\"text-muted-foreground text-xs font-normal\">\n Opened\n </DropdownMenuLabel>\n {renderTabGroup(filteredOpenedTabs, 'No opened tabs')}\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n\n <Button\n size=\"icon\"\n variant=\"ghost\"\n onClick={handleNewQuery}\n className=\"h-5 w-5 flex-shrink-0\"\n >\n <PlusIcon className=\"h-4 w-4\" />\n </Button>\n </TabsList>\n\n <DeleteSqlQueryModal\n isOpen={queryToDelete !== null}\n onClose={() => setQueryToDelete(null)}\n onConfirm={handleConfirmDeleteQuery}\n />\n <RenameSqlQueryModal\n isOpen={queryToRename !== null}\n onClose={() => setQueryToRename(null)}\n initialName={queryToRename?.name ?? ''}\n onRename={handleFinishRename}\n />\n </>\n );\n};\n"]}
|
|
1
|
+
{"version":3,"file":"QueryEditorPanelTabsList.js","sourceRoot":"","sources":["../../src/components/QueryEditorPanelTabsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TabStrip} from '@sqlrooms/ui';\nimport {PencilIcon, TrashIcon} from 'lucide-react';\nimport React, {useCallback, useState} from 'react';\nimport {useStoreWithSqlEditor} from '../SqlEditorSlice';\nimport DeleteSqlQueryModal from './DeleteSqlQueryModal';\nimport RenameSqlQueryModal from './RenameSqlQueryModal';\n\nexport const QueryEditorPanelTabsList: React.FC<{className?: string}> = ({\n className,\n}) => {\n const queries = useStoreWithSqlEditor((s) => s.sqlEditor.config.queries);\n const openTabs = useStoreWithSqlEditor((s) => s.sqlEditor.config.openTabs);\n const selectedQueryId = useStoreWithSqlEditor(\n (s) => s.sqlEditor.config.selectedQueryId,\n );\n\n const renameQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.renameQueryTab,\n );\n const closeQueryTab = useStoreWithSqlEditor((s) => s.sqlEditor.closeQueryTab);\n const setSelectedQueryId = useStoreWithSqlEditor(\n (s) => s.sqlEditor.setSelectedQueryId,\n );\n const createQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.createQueryTab,\n );\n const deleteQueryTab = useStoreWithSqlEditor(\n (s) => s.sqlEditor.deleteQueryTab,\n );\n const setOpenTabs = useStoreWithSqlEditor((s) => s.sqlEditor.setOpenTabs);\n\n const [queryToDelete, setQueryToDelete] = useState<string | null>(null);\n const [queryToRename, setQueryToRename] = useState<{\n id: string;\n name: string;\n } | null>(null);\n\n const handleRenameRequest = useCallback(\n (queryId: string) => {\n const query = queries.find((q) => q.id === queryId);\n if (query) {\n setQueryToRename({id: queryId, name: query.name});\n }\n },\n [queries],\n );\n\n const handleFinishRename = useCallback(\n (newName: string) => {\n if (queryToRename) {\n renameQueryTab(queryToRename.id, newName);\n }\n setQueryToRename(null);\n },\n [queryToRename, renameQueryTab],\n );\n\n const handleDelete = useCallback(\n (queryId: string) => {\n const query = queries.find((q) => q.id === queryId);\n // If query is empty (no content), delete immediately without confirmation\n if (query && query.query.trim() === '') {\n deleteQueryTab(queryId);\n } else {\n // Otherwise, show confirmation modal\n setQueryToDelete(queryId);\n }\n },\n [queries, deleteQueryTab],\n );\n\n const handleConfirmDelete = useCallback(() => {\n if (queryToDelete) {\n deleteQueryTab(queryToDelete);\n setQueryToDelete(null);\n }\n }, [queryToDelete, deleteQueryTab]);\n\n return (\n <>\n <TabStrip\n className={className}\n tabs={queries}\n openTabs={openTabs}\n selectedTabId={selectedQueryId}\n onClose={closeQueryTab}\n onOpenTabsChange={setOpenTabs}\n onSelect={setSelectedQueryId}\n onCreate={createQueryTab}\n onRename={renameQueryTab}\n renderTabMenu={(tab) => (\n <>\n <TabStrip.MenuItem onClick={() => handleRenameRequest(tab.id)}>\n <PencilIcon className=\"mr-2 h-4 w-4\" />\n Rename\n </TabStrip.MenuItem>\n <TabStrip.MenuSeparator />\n <TabStrip.MenuItem\n variant=\"destructive\"\n onClick={() => handleDelete(tab.id)}\n >\n <TrashIcon className=\"mr-2 h-4 w-4\" />\n Delete\n </TabStrip.MenuItem>\n </>\n )}\n renderSearchItemActions={(tab) => (\n <>\n <TabStrip.SearchItemAction\n icon={<PencilIcon className=\"h-3 w-3\" size={5} />}\n aria-label={`Rename ${tab.name}`}\n onClick={() => handleRenameRequest(tab.id)}\n />\n <TabStrip.SearchItemAction\n icon={<TrashIcon className=\"h-3 w-3\" />}\n aria-label={`Delete ${tab.name}`}\n onClick={() => handleDelete(tab.id)}\n />\n </>\n )}\n />\n\n <DeleteSqlQueryModal\n isOpen={queryToDelete !== null}\n onClose={() => setQueryToDelete(null)}\n onConfirm={handleConfirmDelete}\n />\n <RenameSqlQueryModal\n isOpen={queryToRename !== null}\n onClose={() => setQueryToRename(null)}\n initialName={queryToRename?.name ?? ''}\n onRename={handleFinishRename}\n />\n </>\n );\n};\n"]}
|