improved save file experience

This commit is contained in:
Jan Prochazka
2021-01-30 18:23:05 +01:00
parent 059eabf2fa
commit 255c3e5ef4
15 changed files with 182 additions and 132 deletions

View File

@@ -45,29 +45,36 @@ function buildMenu() {
mainWindow.webContents.executeJavaScript(`dbgate_createNewConnection()`); mainWindow.webContents.executeJavaScript(`dbgate_createNewConnection()`);
}, },
}, },
{
label: 'New query',
click() {
mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
},
},
{ {
label: 'Open file', label: 'Open file',
click() { click() {
mainWindow.webContents.executeJavaScript(`dbgate_openFile()`); mainWindow.webContents.executeJavaScript(`dbgate_openFile()`);
}, },
}, },
{ type: 'separator' },
{ role: 'close' },
],
},
{
label: 'Window',
submenu: [
{
label: 'New query',
click() {
mainWindow.webContents.executeJavaScript(`dbgate_newQuery()`);
},
},
{ type: 'separator' },
{ {
label: 'Close all tabs', label: 'Close all tabs',
click() { click() {
mainWindow.webContents.executeJavaScript('dbgate_closeAll()'); mainWindow.webContents.executeJavaScript('dbgate_closeAll()');
}, },
}, },
{ type: 'separator' },
{ role: 'minimize' }, { role: 'minimize' },
{ role: 'close' },
], ],
}, },
// { // {
// label: 'Edit', // label: 'Edit',
// submenu: [ // submenu: [

View File

@@ -1,17 +1,9 @@
import React from 'react'; import React from 'react';
import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton'; import ToolbarButton from '../widgets/ToolbarButton';
export default function ChartToolbar({ save, modelState, dispatchModel }) { export default function ChartToolbar({ modelState, dispatchModel }) {
const hasPermission = useHasPermission();
return ( return (
<> <>
{hasPermission('files/charts/write') && (
<ToolbarButton onClick={save} icon="icon save">
Save
</ToolbarButton>
)}
<ToolbarButton disabled={!modelState.canUndo} onClick={() => dispatchModel({ type: 'undo' })} icon="icon undo"> <ToolbarButton disabled={!modelState.canUndo} onClick={() => dispatchModel({ type: 'undo' })} icon="icon undo">
Undo Undo
</ToolbarButton> </ToolbarButton>

View File

@@ -1,18 +1,15 @@
import React from 'react'; import React from 'react';
import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton'; import ToolbarButton from '../widgets/ToolbarButton';
export default function QueryDesignToolbar({ export default function QueryDesignToolbar({
execute, execute,
isDatabaseDefined, isDatabaseDefined,
busy, busy,
save,
modelState, modelState,
dispatchModel, dispatchModel,
isConnected, isConnected,
kill, kill,
}) { }) {
const hasPermission = useHasPermission();
return ( return (
<> <>
<ToolbarButton disabled={!isDatabaseDefined || busy} onClick={execute} icon="icon run"> <ToolbarButton disabled={!isDatabaseDefined || busy} onClick={execute} icon="icon run">
@@ -21,11 +18,6 @@ export default function QueryDesignToolbar({
<ToolbarButton disabled={!isConnected} onClick={kill} icon="icon close"> <ToolbarButton disabled={!isConnected} onClick={kill} icon="icon close">
Kill Kill
</ToolbarButton> </ToolbarButton>
{hasPermission('files/query/write') && (
<ToolbarButton onClick={save} icon="icon save">
Save
</ToolbarButton>
)}
<ToolbarButton disabled={!modelState.canUndo} onClick={() => dispatchModel({ type: 'undo' })} icon="icon undo"> <ToolbarButton disabled={!modelState.canUndo} onClick={() => dispatchModel({ type: 'undo' })} icon="icon undo">
Undo Undo
</ToolbarButton> </ToolbarButton>

View File

@@ -1,17 +1,9 @@
import React from 'react'; import React from 'react';
import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton'; import ToolbarButton from '../widgets/ToolbarButton';
export default function MarkdownToolbar({ save, showPreview }) { export default function MarkdownToolbar({ showPreview }) {
const hasPermission = useHasPermission();
return ( return (
<> <>
{hasPermission('files/markdown/write') && (
<ToolbarButton onClick={save} icon="icon save">
Save
</ToolbarButton>
)}
<ToolbarButton onClick={showPreview} icon="icon preview"> <ToolbarButton onClick={showPreview} icon="icon preview">
Preview Preview
</ToolbarButton> </ToolbarButton>

View File

@@ -34,11 +34,10 @@ export default function SaveFileModal({
} }
}; };
const handleSaveAs = async filePath => { const handleSaveToDisk = async filePath => {
const path = window.require('path'); const path = window.require('path');
const parsed = path.parse(filePath); const parsed = path.parse(filePath);
if (!parsed.ext) filePath += `.${fileExtension}`; // if (!parsed.ext) filePath += `.${fileExtension}`;
await axios.post('files/save-as', { filePath, data, format }); await axios.post('files/save-as', { filePath, data, format });
modalState.close(); modalState.close();
@@ -67,11 +66,15 @@ export default function SaveFileModal({
value="Save to disk" value="Save to disk"
onClick={() => { onClick={() => {
const file = electron.remote.dialog.showSaveDialogSync(electron.remote.getCurrentWindow(), { const file = electron.remote.dialog.showSaveDialogSync(electron.remote.getCurrentWindow(), {
filters: { name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] }, filters: [
defaultPath: filePath, { name: `${fileExtension.toUpperCase()} files`, extensions: [fileExtension] },
{ name: `All files`, extensions: ['*'] },
],
defaultPath: filePath || `${name}.${fileExtension}`,
properties: ['showOverwriteConfirmation'],
}); });
if (file) { if (file) {
handleSaveAs(file); handleSaveToDisk(file);
} }
}} }}
/> />

View File

@@ -1,12 +1,28 @@
import React from 'react'; import React from 'react';
import axios from '../utility/axios';
import { changeTab } from '../utility/common'; import { changeTab } from '../utility/common';
import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState'; import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
import keycodes from '../utility/keycodes'; import keycodes from '../utility/keycodes';
import SaveFileToolbarButton from '../utility/SaveFileToolbarButton';
import ToolbarPortal from '../utility/ToolbarPortal';
import useHasPermission from '../utility/useHasPermission';
import SaveFileModal from './SaveFileModal'; import SaveFileModal from './SaveFileModal';
import useModalState from './useModalState';
export default function SaveTabModal({ data, folder, format, modalState, tabid, tabVisible, fileExtension }) { export default function SaveTabModal({
data,
folder,
format,
tabid,
tabVisible,
fileExtension,
toolbarPortalRef = undefined,
}) {
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
const openedTabs = useOpenedTabs(); const openedTabs = useOpenedTabs();
const saveFileModalState = useModalState();
const hasPermission = useHasPermission();
const canSave = hasPermission(`files/${folder}/write`);
const { savedFile, savedFilePath } = openedTabs.find(x => x.tabid == tabid).props || {}; const { savedFile, savedFilePath } = openedTabs.find(x => x.tabid == tabid).props || {};
const onSave = (title, newProps) => { const onSave = (title, newProps) => {
@@ -21,35 +37,63 @@ export default function SaveTabModal({ data, folder, format, modalState, tabid,
})); }));
}; };
const handleSave = async () => {
if (savedFile) {
await axios.post('files/save', { folder, file: savedFile, data, format });
}
if (savedFilePath) {
await axios.post('files/save-as', { filePath: savedFilePath, data, format });
}
};
const handleSaveRef = React.useRef(handleSave);
handleSaveRef.current = handleSave;
const handleKeyboard = React.useCallback( const handleKeyboard = React.useCallback(
e => { e => {
if (e.keyCode == keycodes.s && e.ctrlKey) { if (e.keyCode == keycodes.s && e.ctrlKey) {
e.preventDefault(); e.preventDefault();
modalState.open(); if (e.shiftKey) {
saveFileModalState.open();
} else {
if (savedFile || savedFilePath) handleSaveRef.current();
else saveFileModalState.open();
}
} }
}, },
[modalState] [saveFileModalState]
); );
React.useEffect(() => { React.useEffect(() => {
if (tabVisible) { if (tabVisible && canSave) {
document.addEventListener('keydown', handleKeyboard); document.addEventListener('keydown', handleKeyboard);
return () => { return () => {
document.removeEventListener('keydown', handleKeyboard); document.removeEventListener('keydown', handleKeyboard);
}; };
} }
}, [tabVisible, handleKeyboard]); }, [tabVisible, handleKeyboard, canSave]);
return ( return (
<SaveFileModal <>
data={data} <SaveFileModal
folder={folder} data={data}
format={format} folder={folder}
modalState={modalState} format={format}
name={savedFile || 'newFile'} modalState={saveFileModalState}
filePath={savedFilePath} name={savedFile || 'newFile'}
fileExtension={fileExtension} filePath={savedFilePath}
onSave={onSave} fileExtension={fileExtension}
/> onSave={onSave}
/>
{canSave && (
<ToolbarPortal tabVisible={tabVisible} toolbarPortalRef={toolbarPortalRef}>
<SaveFileToolbarButton
saveAs={saveFileModalState.open}
save={savedFile || savedFilePath ? handleSave : null}
tabid={tabid}
/>
</ToolbarPortal>
)}
</>
); );
} }

View File

@@ -2,7 +2,7 @@ import React from 'react';
import useHasPermission from '../utility/useHasPermission'; import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton'; import ToolbarButton from '../widgets/ToolbarButton';
export default function QueryToolbar({ execute, isDatabaseDefined, busy, save, format, isConnected, kill }) { export default function QueryToolbar({ execute, isDatabaseDefined, busy, format, isConnected, kill }) {
const hasPermission = useHasPermission(); const hasPermission = useHasPermission();
return ( return (
<> <>
@@ -15,11 +15,11 @@ export default function QueryToolbar({ execute, isDatabaseDefined, busy, save, f
<ToolbarButton disabled={!isConnected} onClick={kill} icon="icon close"> <ToolbarButton disabled={!isConnected} onClick={kill} icon="icon close">
Kill Kill
</ToolbarButton> </ToolbarButton>
{hasPermission('files/sql/write') && ( {/* {hasPermission('files/sql/write') && (
<ToolbarButton onClick={save} icon="icon save"> <ToolbarButton onClick={save} icon="icon save">
Save Save
</ToolbarButton> </ToolbarButton>
)} )} */}
<ToolbarButton onClick={format} icon="icon format-code"> <ToolbarButton onClick={format} icon="icon format-code">
Format Format
</ToolbarButton> </ToolbarButton>

View File

@@ -1,9 +1,7 @@
import React from 'react'; import React from 'react';
import useHasPermission from '../utility/useHasPermission';
import ToolbarButton from '../widgets/ToolbarButton'; import ToolbarButton from '../widgets/ToolbarButton';
export default function ShellToolbar({ execute, cancel, busy, edit, save, editAvailable }) { export default function ShellToolbar({ execute, cancel, busy, edit, editAvailable }) {
const hasPermission = useHasPermission();
return ( return (
<> <>
<ToolbarButton disabled={busy} onClick={execute} icon="icon run"> <ToolbarButton disabled={busy} onClick={execute} icon="icon run">
@@ -15,11 +13,6 @@ export default function ShellToolbar({ execute, cancel, busy, edit, save, editAv
<ToolbarButton disabled={!editAvailable} onClick={edit} icon="icon show-wizard"> <ToolbarButton disabled={!editAvailable} onClick={edit} icon="icon show-wizard">
Show wizard Show wizard
</ToolbarButton> </ToolbarButton>
{hasPermission('files/shell/write') && (
<ToolbarButton onClick={save} icon="icon save">
Save
</ToolbarButton>
)}
</> </>
); );
} }

View File

@@ -4,17 +4,16 @@ import { createFreeTableModel } from 'dbgate-datalib';
import useUndoReducer from '../utility/useUndoReducer'; import useUndoReducer from '../utility/useUndoReducer';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { useUpdateDatabaseForTab } from '../utility/globalState'; import { useUpdateDatabaseForTab } from '../utility/globalState';
import useModalState from '../modals/useModalState';
import LoadingInfo from '../widgets/LoadingInfo'; import LoadingInfo from '../widgets/LoadingInfo';
import ErrorInfo from '../widgets/ErrorInfo'; import ErrorInfo from '../widgets/ErrorInfo';
import useEditorData from '../utility/useEditorData'; import useEditorData from '../utility/useEditorData';
import SaveTabModal from '../modals/SaveTabModal'; import SaveTabModal from '../modals/SaveTabModal';
import ChartEditor from '../charts/ChartEditor'; import ChartEditor from '../charts/ChartEditor';
import ChartToolbar from '../charts/ChartToolbar'; import ChartToolbar from '../charts/ChartToolbar';
import ToolbarPortal from '../utility/ToolbarPortal';
export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database, tabid }) { export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database, tabid }) {
const [modelState, dispatchModel] = useUndoReducer(createFreeTableModel()); const [modelState, dispatchModel] = useUndoReducer(createFreeTableModel());
const saveFileModalState = useModalState();
const { initialData, setEditorData, errorMessage, isLoading } = useEditorData({ const { initialData, setEditorData, errorMessage, isLoading } = useEditorData({
tabid, tabid,
}); });
@@ -57,21 +56,17 @@ export default function ChartTab({ tabVisible, toolbarPortalRef, conid, database
database={database} database={database}
/> />
<SaveTabModal <SaveTabModal
modalState={saveFileModalState}
tabVisible={tabVisible} tabVisible={tabVisible}
toolbarPortalRef={toolbarPortalRef}
data={modelState.value} data={modelState.value}
format="json" format="json"
folder="charts" folder="charts"
tabid={tabid} tabid={tabid}
fileExtension='chart' fileExtension="chart"
/> />
{toolbarPortalRef && <ToolbarPortal toolbarPortalRef={toolbarPortalRef} tabVisible={tabVisible}>
toolbarPortalRef.current && <ChartToolbar modelState={modelState} dispatchModel={dispatchModel} />
tabVisible && </ToolbarPortal>
ReactDOM.createPortal(
<ChartToolbar save={saveFileModalState.open} modelState={modelState} dispatchModel={dispatchModel} />,
toolbarPortalRef.current
)}
</> </>
); );
} }

View File

@@ -11,10 +11,10 @@ import LoadingInfo from '../widgets/LoadingInfo';
import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState'; import { useOpenedTabs, useSetOpenedTabs } from '../utility/globalState';
import useOpenNewTab from '../utility/useOpenNewTab'; import useOpenNewTab from '../utility/useOpenNewTab';
import { setSelectedTabFunc } from '../utility/common'; import { setSelectedTabFunc } from '../utility/common';
import ToolbarPortal from '../utility/ToolbarPortal';
export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef, ...other }) { export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef, ...other }) {
const { editorData, setEditorData, isLoading, saveToStorage } = useEditorData({ tabid }); const { editorData, setEditorData, isLoading, saveToStorage } = useEditorData({ tabid });
const saveFileModalState = useModalState();
const openedTabs = useOpenedTabs(); const openedTabs = useOpenedTabs();
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
const openNewTab = useOpenNewTab(); const openNewTab = useOpenNewTab();
@@ -61,21 +61,17 @@ export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef,
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
mode="markdown" mode="markdown"
/> />
{toolbarPortalRef && <ToolbarPortal toolbarPortalRef={toolbarPortalRef} tabVisible={tabVisible}>
toolbarPortalRef.current && <MarkdownToolbar showPreview={showPreview} />
tabVisible && </ToolbarPortal>
ReactDOM.createPortal(
<MarkdownToolbar save={saveFileModalState.open} showPreview={showPreview} />,
toolbarPortalRef.current
)}
<SaveTabModal <SaveTabModal
modalState={saveFileModalState}
tabVisible={tabVisible} tabVisible={tabVisible}
toolbarPortalRef={toolbarPortalRef}
data={editorData} data={editorData}
format="text" format="text"
folder="markdown" folder="markdown"
tabid={tabid} tabid={tabid}
fileExtension='md' fileExtension="md"
/> />
</> </>
); );

View File

@@ -15,7 +15,6 @@ import keycodes from '../utility/keycodes';
import { changeTab } from '../utility/common'; import { changeTab } from '../utility/common';
import useSocket from '../utility/SocketProvider'; import useSocket from '../utility/SocketProvider';
import SaveTabModal from '../modals/SaveTabModal'; import SaveTabModal from '../modals/SaveTabModal';
import useModalState from '../modals/useModalState';
import sqlFormatter from 'sql-formatter'; import sqlFormatter from 'sql-formatter';
import useEditorData from '../utility/useEditorData'; import useEditorData from '../utility/useEditorData';
import LoadingInfo from '../widgets/LoadingInfo'; import LoadingInfo from '../widgets/LoadingInfo';
@@ -27,6 +26,7 @@ import { generateDesignedQuery } from '../designer/designerTools';
import useUndoReducer from '../utility/useUndoReducer'; import useUndoReducer from '../utility/useUndoReducer';
import { StatusBarItem } from '../widgets/StatusBar'; import { StatusBarItem } from '../widgets/StatusBar';
import useTimerLabel from '../utility/useTimerLabel'; import useTimerLabel from '../utility/useTimerLabel';
import ToolbarPortal from '../utility/ToolbarPortal';
export default function QueryDesignTab({ export default function QueryDesignTab({
tabid, tabid,
@@ -43,7 +43,6 @@ export default function QueryDesignTab({
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
const socket = useSocket(); const socket = useSocket();
const [busy, setBusy] = React.useState(false); const [busy, setBusy] = React.useState(false);
const saveFileModalState = useModalState();
const extensions = useExtensions(); const extensions = useExtensions();
const connection = useConnectionInfo({ conid }); const connection = useConnectionInfo({ conid });
const engine = findEngineDriver(connection, extensions); const engine = findEngineDriver(connection, extensions);
@@ -196,36 +195,32 @@ export default function QueryDesignTab({
)} )}
</ResultTabs> </ResultTabs>
</VerticalSplitter> </VerticalSplitter>
{toolbarPortalRef && <ToolbarPortal toolbarPortalRef={toolbarPortalRef} tabVisible={tabVisible}>
toolbarPortalRef.current && <QueryDesignToolbar
tabVisible && modelState={modelState}
ReactDOM.createPortal( dispatchModel={dispatchModel}
<QueryDesignToolbar isDatabaseDefined={conid && database}
modelState={modelState} execute={handleExecute}
dispatchModel={dispatchModel} busy={busy}
isDatabaseDefined={conid && database} // cancel={handleCancel}
execute={handleExecute} // format={handleFormatCode}
busy={busy} isConnected={!!sessionId}
// cancel={handleCancel} kill={handleKill}
// format={handleFormatCode} />
save={saveFileModalState.open} </ToolbarPortal>
isConnected={!!sessionId}
kill={handleKill}
/>,
toolbarPortalRef.current
)}
{statusbarPortalRef && {statusbarPortalRef &&
statusbarPortalRef.current && statusbarPortalRef.current &&
tabVisible && tabVisible &&
ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)} ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)}
<SaveTabModal <SaveTabModal
modalState={saveFileModalState} // modalState={saveFileModalState}
tabVisible={tabVisible} tabVisible={tabVisible}
toolbarPortalRef={toolbarPortalRef}
data={modelState.value} data={modelState.value}
format="json" format="json"
folder="query" folder="query"
tabid={tabid} tabid={tabid}
fileExtension='qdesign' fileExtension="qdesign"
/> />
</> </>
); );

View File

@@ -23,6 +23,7 @@ import LoadingInfo from '../widgets/LoadingInfo';
import useExtensions from '../utility/useExtensions'; import useExtensions from '../utility/useExtensions';
import useTimerLabel from '../utility/useTimerLabel'; import useTimerLabel from '../utility/useTimerLabel';
import { StatusBarItem } from '../widgets/StatusBar'; import { StatusBarItem } from '../widgets/StatusBar';
import ToolbarPortal from '../utility/ToolbarPortal';
function createSqlPreview(sql) { function createSqlPreview(sql) {
if (!sql) return undefined; if (!sql) return undefined;
@@ -58,7 +59,6 @@ export default function QueryTab({
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
const socket = useSocket(); const socket = useSocket();
const [busy, setBusy] = React.useState(false); const [busy, setBusy] = React.useState(false);
const saveFileModalState = useModalState();
const extensions = useExtensions(); const extensions = useExtensions();
const timerLabel = useTimerLabel(); const timerLabel = useTimerLabel();
const { editorData, setEditorData, isLoading } = useEditorData({ const { editorData, setEditorData, isLoading } = useEditorData({
@@ -201,7 +201,7 @@ export default function QueryTab({
</ResultTabs> </ResultTabs>
)} )}
</VerticalSplitter> </VerticalSplitter>
{toolbarPortalRef && {/* {toolbarPortalRef &&
toolbarPortalRef.current && toolbarPortalRef.current &&
tabVisible && tabVisible &&
ReactDOM.createPortal( ReactDOM.createPortal(
@@ -216,19 +216,31 @@ export default function QueryTab({
kill={handleKill} kill={handleKill}
/>, />,
toolbarPortalRef.current toolbarPortalRef.current
)} )} */}
{statusbarPortalRef && {statusbarPortalRef &&
statusbarPortalRef.current && statusbarPortalRef.current &&
tabVisible && tabVisible &&
ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)} ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)}
<ToolbarPortal toolbarPortalRef={toolbarPortalRef} tabVisible={tabVisible}>
<QueryToolbar
isDatabaseDefined={conid && database}
execute={handleExecute}
busy={busy}
// cancel={handleCancel}
format={handleFormatCode}
// save={saveFileModalState.open}
isConnected={!!sessionId}
kill={handleKill}
/>
</ToolbarPortal>
<SaveTabModal <SaveTabModal
modalState={saveFileModalState} toolbarPortalRef={toolbarPortalRef}
tabVisible={tabVisible} tabVisible={tabVisible}
data={editorData} data={editorData}
format="text" format="text"
folder="sql" folder="sql"
tabid={tabid} tabid={tabid}
fileExtension='sql' fileExtension="sql"
/> />
</> </>
); );

View File

@@ -14,10 +14,10 @@ import useShowModal from '../modals/showModal';
import ImportExportModal from '../modals/ImportExportModal'; import ImportExportModal from '../modals/ImportExportModal';
import useEditorData from '../utility/useEditorData'; import useEditorData from '../utility/useEditorData';
import SaveTabModal from '../modals/SaveTabModal'; import SaveTabModal from '../modals/SaveTabModal';
import useModalState from '../modals/useModalState';
import LoadingInfo from '../widgets/LoadingInfo'; import LoadingInfo from '../widgets/LoadingInfo';
import useTimerLabel from '../utility/useTimerLabel'; import useTimerLabel from '../utility/useTimerLabel';
import { StatusBarItem } from '../widgets/StatusBar'; import { StatusBarItem } from '../widgets/StatusBar';
import ToolbarPortal from '../utility/ToolbarPortal';
const configRegex = /\s*\/\/\s*@ImportExportConfigurator\s*\n\s*\/\/\s*(\{[^\n]+\})\n/; const configRegex = /\s*\/\/\s*@ImportExportConfigurator\s*\n\s*\/\/\s*(\{[^\n]+\})\n/;
const requireRegex = /\s*(\/\/\s*@require\s+[^\n]+)\n/g; const requireRegex = /\s*(\/\/\s*@require\s+[^\n]+)\n/g;
@@ -27,7 +27,6 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, statusba
const [busy, setBusy] = React.useState(false); const [busy, setBusy] = React.useState(false);
const showModal = useShowModal(); const showModal = useShowModal();
const { editorData, setEditorData, isLoading } = useEditorData({ tabid }); const { editorData, setEditorData, isLoading } = useEditorData({ tabid });
const saveFileModalState = useModalState();
const timerLabel = useTimerLabel(); const timerLabel = useTimerLabel();
const setOpenedTabs = useSetOpenedTabs(); const setOpenedTabs = useSetOpenedTabs();
@@ -120,32 +119,27 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, statusba
/> />
<RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber} /> <RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber} />
</VerticalSplitter> </VerticalSplitter>
{toolbarPortalRef && <ToolbarPortal toolbarPortalRef={toolbarPortalRef} tabVisible={tabVisible}>
toolbarPortalRef.current && <ShellToolbar
tabVisible && execute={handleExecute}
ReactDOM.createPortal( busy={busy}
<ShellToolbar cancel={handleCancel}
execute={handleExecute} edit={handleEdit}
busy={busy} editAvailable={configRegex.test(editorData || '')}
cancel={handleCancel} />
edit={handleEdit} </ToolbarPortal>
editAvailable={configRegex.test(editorData || '')}
save={saveFileModalState.open}
/>,
toolbarPortalRef.current
)}
{statusbarPortalRef && {statusbarPortalRef &&
statusbarPortalRef.current && statusbarPortalRef.current &&
tabVisible && tabVisible &&
ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)} ReactDOM.createPortal(<StatusBarItem>{timerLabel.text}</StatusBarItem>, statusbarPortalRef.current)}
<SaveTabModal <SaveTabModal
modalState={saveFileModalState} toolbarPortalRef={toolbarPortalRef}
tabVisible={tabVisible} tabVisible={tabVisible}
data={editorData} data={editorData}
format="text" format="text"
folder="shell" folder="shell"
tabid={tabid} tabid={tabid}
fileExtension='js' fileExtension="js"
/> />
</> </>
); );

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { DropDownMenuItem } from '../modals/DropDownMenu';
import ToolbarButton, { ToolbarDropDownButton } from '../widgets/ToolbarButton';
export default function SaveFileToolbarButton({ tabid, save, saveAs }) {
if (!saveAs) return null;
if (save) {
return (
<ToolbarDropDownButton icon="icon save" text="Save">
<DropDownMenuItem onClick={save}>Save</DropDownMenuItem>
<DropDownMenuItem onClick={saveAs}>Save As</DropDownMenuItem>
</ToolbarDropDownButton>
);
}
return (
<ToolbarButton onClick={saveAs} icon="icon save">
Save As
</ToolbarButton>
);
}

View File

@@ -0,0 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
export default function ToolbarPortal({ toolbarPortalRef, tabVisible, children }) {
return (
(toolbarPortalRef &&
toolbarPortalRef.current &&
tabVisible &&
children &&
ReactDOM.createPortal(children, toolbarPortalRef.current)) ||
null
);
}