Skip to content

Commit

Permalink
feat: add tooltips, fix minor issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Ionov committed Dec 22, 2023
1 parent 384a39c commit 03be41d
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const QueryTab = () => {
});

return (
<div class="flex flex-col h-full overflow-hidden">
<div class="flex flex-col h-full">
<div id="query" class="flex flex-col">
<QueryTextArea />
</div>
Expand Down
100 changes: 37 additions & 63 deletions src/components/Screens/Console/Content/QueryTab/QueryTextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import {
createCodeMirror,
createEditorControlledValue,
createEditorFocus,
} from 'solid-codemirror';
import { createEffect, createSignal, Show } from 'solid-js';
import {
EditorView,
drawSelection,
highlightWhitespace,
highlightActiveLine,
} from '@codemirror/view';
import { createCodeMirror, createEditorControlledValue, createEditorFocus } from 'solid-codemirror';
import { createEffect, createSignal } from 'solid-js';
import { EditorView, drawSelection, highlightWhitespace, highlightActiveLine } from '@codemirror/view';
import { MySQL, sql, SQLite, PostgreSQL } from '@codemirror/lang-sql';
import { dracula } from '@uiw/codemirror-theme-dracula';
import { vim } from '@replit/codemirror-vim';
Expand All @@ -19,12 +10,12 @@ import { Copy, EditIcon, FireIcon, VimIcon } from 'components/UI/Icons';
import { useAppSelector } from 'services/Context';
import { QueryTaskEnqueueResult, Dialect } from 'interfaces';
import { t } from 'utils/i18n';
import { Alert } from 'components/UI';
import { basicSetup } from 'codemirror';
import { createShortcut } from '@solid-primitives/keyboard';
import { search } from '@codemirror/search';
import { createStore } from 'solid-js/store';
import { ActionRowButton } from './components/ActionRowButton';
import { debounce } from 'utils/utils';

const SQLDialects = {
[Dialect.Mysql]: MySQL,
Expand All @@ -37,7 +28,6 @@ export const QueryTextArea = () => {
connections: {
updateContentTab,
getConnection,
getContent,
getContentData,
getSchemaTables,
contentStore: { idx: tabIdx },
Expand All @@ -49,12 +39,21 @@ export const QueryTextArea = () => {
const [loading, setLoading] = createSignal(false);
const [autoLimit, setAutoLimit] = createSignal(true);

const updateQueryText = async (query: string) => {
updateContentTab('data', { query });
const updateCursor = debounce(() => {
updateContentTab('data', { query: code() });
}, 300);

const updateQueryText = (query: string) => {
setCode(query);
if (focused()) {
updateContentTab('data', { cursor: editorView().state.selection.ranges[0].from });
}
updateCursor();
};

const { ref, editorView, createExtension } = createCodeMirror({
onValueChange: setCode,
value: code(),
onValueChange: updateQueryText,
});
createEditorControlledValue(editorView, code);
createExtension(drawSelection);
Expand All @@ -64,10 +63,8 @@ export const QueryTextArea = () => {
createExtension(search);
createExtension(() => basicSetup);
createExtension(() => (vimModeOn() ? vim() : []));
createExtension(() =>
sql({ dialect: SQLDialects[getConnection().connection.dialect], schema })
);
const { setFocused } = createEditorFocus(editorView);
createExtension(() => sql({ dialect: SQLDialects[getConnection().connection.dialect], schema }));
const { setFocused, focused } = createEditorFocus(editorView);
// TODO: add option to scroll inside autocompletion list with c-l and c-k
// defaultKeymap.push({ keys: "gq", type: "operator", operator: "hardWrap" });
// Vim.defineOperator(
Expand Down Expand Up @@ -100,17 +97,15 @@ export const QueryTextArea = () => {
updateContentTab('error', undefined);
const activeConnection = getConnection();
try {
const { result_sets } = await invoke<QueryTaskEnqueueResult>(
'enqueue_query',
{
connId: activeConnection.id,
sql: selectedText || code(),
autoLimit: autoLimit(),
tabIdx,
}
);
const { result_sets } = await invoke<QueryTaskEnqueueResult>('enqueue_query', {
connId: activeConnection.id,
sql: selectedText || code(),
autoLimit: autoLimit(),
tabIdx,
});
updateContentTab('data', {
query: code(),
cursor: editorView().state.selection.ranges[0].from,
result_sets: result_sets.map((id) => ({
id,
})),
Expand Down Expand Up @@ -143,46 +138,31 @@ export const QueryTextArea = () => {
createShortcut(['Control', 'l'], () => setFocused(true));
createShortcut(['Control', 'Shift', 'F'], onFormat);

const onTestClick = () => {
console.log(getContentData('Query'));
}
// const onTestClick = () => {
// console.log(editorView());
// };

return (
<div class="flex-1 flex flex-col">
<div class="flex-1 flex flex-col h-full">
<div class="w-full px-2 py-1 bg-base-100 border-b-2 border-accent flex justify-between items-center">
<div class="flex items-center">
<ActionRowButton
dataTip={'Testing'}
onClick={onTestClick}
icon={<EditIcon />}
/>
<ActionRowButton
dataTip={t('console.actions.format')}
onClick={onFormat}
icon={<EditIcon />}
/>
{/*
<ActionRowButton dataTip={'Testing'} onClick={onTestClick} icon={<EditIcon />} />
*/}
<ActionRowButton dataTip={t('console.actions.format')} onClick={onFormat} icon={<EditIcon />} />
<ActionRowButton
dataTip={t('console.actions.execute')}
onClick={onExecute}
loading={loading()}
icon={<FireIcon />}
/>

<ActionRowButton
dataTip={t('console.actions.copy_query')}
onClick={copyQueryToClipboard}
icon={<Copy />}
/>
<ActionRowButton dataTip={t('console.actions.copy_query')} onClick={copyQueryToClipboard} icon={<Copy />} />

<div
class="tooltip tooltip-primary tooltip-bottom"
data-tip={t('console.actions.auto_limit')}
>
<div class="tooltip tooltip-primary tooltip-bottom" data-tip={t('console.actions.auto_limit')}>
<div class="form-control">
<label class="cursor-pointer label">
<span class="label-text font-semibold mr-2 text-primary mt-1">
{t('console.actions.limit')}
</span>
<span class="label-text font-semibold mr-2 text-primary mt-1">{t('console.actions.limit')}</span>
<input
type="checkbox"
checked={autoLimit()}
Expand All @@ -193,10 +173,7 @@ export const QueryTextArea = () => {
</div>
</div>

<div
class="tooltip tooltip-primary tooltip-bottom"
data-tip={t('console.actions.vim_mode_on')}
>
<div class="tooltip tooltip-primary tooltip-bottom" data-tip={t('console.actions.vim_mode_on')}>
<div class="flex items-center mx-2">
<span class="mr-2">
<VimIcon />
Expand All @@ -213,9 +190,6 @@ export const QueryTextArea = () => {
</div>
</div>
</div>
<Show when={getContent().error}>
<Alert color="error">{getContent().error}</Alert>
</Show>
</div>
<div class="overflow-hidden w-full h-full">
<div ref={ref} class="w-full h-full" />
Expand Down
24 changes: 14 additions & 10 deletions src/components/Screens/Console/Content/QueryTab/ResultesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,21 @@ export const ResultsTable = () => {

const [rows, setRows] = createSignal<Row[]>([]);
const [page, setPage] = createSignal(0);
const [loading, setLoading] = createSignal(false);

const updateRows = async () => {
const result_set = getContentData('Query').result_sets[queryIdx()];
// console.log(getContentData('Query').result_sets)
// console.log({ result_set });
if (result_set?.status !== 'Completed') {
setRows([]);
return;
try {
setLoading(true);
const result_set = getContentData('Query').result_sets[queryIdx()];
if (result_set?.status !== 'Completed') {
setRows([]);
return;
}
const _rows = await getQueryResults(result_set.path!, page());
setRows(_rows);
} finally {
setLoading(false);
}
const _rows = await getQueryResults(result_set.path!, page());
setRows(_rows);
};

createEffect(
Expand Down Expand Up @@ -107,7 +111,7 @@ export const ResultsTable = () => {
},
},
{
label: 'Copy row to clipboard',
label: 'Copy row to clipboard as json',
action: function(_e, row) {
// @ts-ignore
navigator.clipboard.writeText(JSON.stringify(row._row.data));
Expand Down Expand Up @@ -154,7 +158,7 @@ export const ResultsTable = () => {
<button>close</button>
</form>
</dialog>
<Pagination {...{ page, onNextPage, onPrevPage }} />
<Pagination {...{ page, onNextPage, onPrevPage, loading, setPage }} />
<div id="results-table"></div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ import { useAppSelector } from 'services/Context';
import { createShortcut } from '@solid-primitives/keyboard';
import { ChevronLeft, ChevronRight } from 'components/UI/Icons';
import { t } from 'utils/i18n';
import { Accessor, createEffect, Show } from 'solid-js';
import { Accessor, createEffect, For, Match, Show, Switch } from 'solid-js';
import { createStore } from 'solid-js/store';
import { ResultSet } from 'interfaces';

type PaginationProps = {
page: Accessor<number>;
loading: Accessor<boolean>;
setPage: (n: number) => void;
onPrevPage: () => void;
onNextPage: () => void;
};

const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];

export const Pagination = (props: PaginationProps) => {
const {
connections: { selectNextQuery, selectPrevQuery, queryIdx, getContentData },
backend: { pageSize },
backend: { pageSize, setPageSize },
} = useAppSelector();

createShortcut(['Control', 'Shift', 'N'], selectNextQuery);
Expand All @@ -35,55 +39,74 @@ export const Pagination = (props: PaginationProps) => {
<div class="container flex justify-between items-top p-1 my-1 gap-2 bg-base-200">
<div class="flex gap-2">
<div class="join">
<button class="join-item btn btn-sm" onClick={selectPrevQuery}>
<ChevronLeft />
</button>
<div class="tooltip tooltip-primary tooltip-right" data-tip={t('console.actions.previous_result_set')}>
<button class="join-item btn btn-sm" onClick={selectPrevQuery}>
<ChevronLeft />
</button>
</div>
<button class="join-item btn btn-sm btn-disabled !text-info">
<span class="mt-1">
{t('console.result_set')} {queryIdx() + 1}
</span>
</button>
<button class="join-item btn btn-sm" onClick={selectNextQuery}>
<ChevronRight />
</button>
<div class="tooltip tooltip-primary tooltip-left" data-tip={t('console.actions.next_result_set')}>
<button class="join-item btn btn-sm" onClick={selectNextQuery}>
<ChevronRight />
</button>
</div>
</div>
<div class="flex-1">
<Show when={resultSet?.status === 'Completed' && resultSet.info}>
<Alert color="info">
{resultSet?.status === 'Completed' && resultSet?.info}
</Alert>
<Alert color="info">{resultSet?.status === 'Completed' && resultSet?.info}</Alert>
</Show>
<Show when={resultSet?.status === 'Error'}>
<Alert color="error">
{resultSet?.status === 'Error' && resultSet?.error}
</Alert>
<Alert color="error">{resultSet?.status === 'Error' && resultSet?.error}</Alert>
</Show>
</div>
</div>
<Show when={resultSet.status === 'Completed' && !resultSet.info}>
<div class="join">
<button
class="join-item btn btn-sm"
disabled={!props.page()}
onClick={props.onPrevPage}
>
<ChevronLeft />
</button>
<button
disabled
class="join-item btn btn-sm btn-disabled !text-base-content"
>
{props.page() + 1}
</button>
<button
class="join-item btn btn-sm"
disabled={
props.page() * pageSize() + pageSize() >= (resultSet?.count ?? 0)
}
onClick={props.onNextPage}
>
<ChevronRight />
<div class="join flex items-center">
<div class="px-3">
<select
value={pageSize()}
onChange={(e) => {
setPageSize(+e.currentTarget.value);
props.setPage(0);
}}
class="select select-accent select-bordered select-xs w-full">
<For each={PAGE_SIZE_OPTIONS}>
{(n) => (
<option class="py-1" value={n}>
{n}
</option>
)}
</For>
</select>
</div>
<div class="tooltip tooltip-primary tooltip-left" data-tip={t('console.actions.previous_page')}>
<button class="join-item btn btn-sm" disabled={props.loading() || !props.page()} onClick={props.onPrevPage}>
<ChevronLeft />
</button>
</div>
<button disabled class="join-item btn btn-sm btn-disabled !text-base-content w-[50px]">
<Switch>
<Match when={props.loading()}>
<span class="loading text-primary loading-bars loading-xs"></span>
</Match>
<Match when={!props.loading()}>
<span>{props.page() + 1}</span>
</Match>
</Switch>
</button>

<div class="tooltip tooltip-primary tooltip-left" data-tip={t('console.actions.next_page')}>
<button
class="join-item btn btn-sm"
disabled={props.loading() || props.page() * pageSize() + pageSize() >= (resultSet?.count ?? 0)}
onClick={props.onNextPage}>
<ChevronRight />
</button>
</div>
</div>
</Show>
</div>
Expand Down
Loading

0 comments on commit 03be41d

Please sign in to comment.