mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-24 21:05:59 +00:00
custom markdown pages - basic concept
This commit is contained in:
@@ -177,7 +177,43 @@ export function SavedChartFileAppObject({ data, commonProps }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SavedSqlFileAppObject, SavedShellFileAppObject, SavedChartFileAppObject].forEach((fn) => {
|
export function SavedMarkdownFileAppObject({ data, commonProps }) {
|
||||||
|
const { file, folder } = data;
|
||||||
|
const setOpenedTabs = useSetOpenedTabs();
|
||||||
|
|
||||||
|
const showPage = () => {
|
||||||
|
openNewTab(setOpenedTabs, {
|
||||||
|
title: file,
|
||||||
|
icon: 'img markdown',
|
||||||
|
tabComponent: 'MarkdownViewTab',
|
||||||
|
props: {
|
||||||
|
file,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<SavedFileAppObjectBase
|
||||||
|
data={data}
|
||||||
|
commonProps={commonProps}
|
||||||
|
format="text"
|
||||||
|
icon="img markdown"
|
||||||
|
onLoad={(data) => {
|
||||||
|
openNewTab(
|
||||||
|
setOpenedTabs,
|
||||||
|
{
|
||||||
|
title: file,
|
||||||
|
icon: 'img markdown',
|
||||||
|
tabComponent: 'MarkdownEditorTab',
|
||||||
|
},
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
menuExt={<DropDownMenuItem onClick={showPage}>Show page</DropDownMenuItem>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SavedSqlFileAppObject, SavedShellFileAppObject, SavedChartFileAppObject, SavedMarkdownFileAppObject].forEach((fn) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
fn.extractKey = (data) => data.file;
|
fn.extractKey = (data) => data.file;
|
||||||
});
|
});
|
||||||
|
|||||||
17
packages/web/src/charts/MarkdownToolbar.js
Normal file
17
packages/web/src/charts/MarkdownToolbar.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import useHasPermission from '../utility/useHasPermission';
|
||||||
|
import ToolbarButton from '../widgets/ToolbarButton';
|
||||||
|
|
||||||
|
export default function MarkdownToolbar({ save }) {
|
||||||
|
const hasPermission = useHasPermission();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{hasPermission('files/markdown/write') && (
|
||||||
|
<ToolbarButton onClick={save} icon="icon save">
|
||||||
|
Save
|
||||||
|
</ToolbarButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import ToolbarButton from '../widgets/ToolbarButton';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { TabPage, TabControl } from '../widgets/TabControl';
|
import { TabPage, TabControl } from '../widgets/TabControl';
|
||||||
import dimensions from '../theme/dimensions';
|
import dimensions from '../theme/dimensions';
|
||||||
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
|
import GenericEditor from '../sqleditor/GenericEditor';
|
||||||
import MacroParameters from './MacroParameters';
|
import MacroParameters from './MacroParameters';
|
||||||
import { WidgetTitle } from '../widgets/WidgetStyles';
|
import { WidgetTitle } from '../widgets/WidgetStyles';
|
||||||
import { FormButton } from '../utility/forms';
|
import { FormButton } from '../utility/forms';
|
||||||
@@ -113,7 +113,7 @@ export default function MacroDetail({ selectedMacro, setSelectedMacro, onChangeV
|
|||||||
</MacroDetailTabWrapper>
|
</MacroDetailTabWrapper>
|
||||||
</TabPage>
|
</TabPage>
|
||||||
<TabPage label="JavaScript" key="javascript">
|
<TabPage label="JavaScript" key="javascript">
|
||||||
<JavaScriptEditor readOnly value={selectedMacro.code} />
|
<GenericEditor readOnly value={selectedMacro.code} mode="javascript" />
|
||||||
</TabPage>
|
</TabPage>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</MacroDetailContainer>
|
</MacroDetailContainer>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const iconNames = {
|
|||||||
'icon theme': 'mdi mdi-brightness-6',
|
'icon theme': 'mdi mdi-brightness-6',
|
||||||
'icon error': 'mdi mdi-close-circle',
|
'icon error': 'mdi mdi-close-circle',
|
||||||
'icon ok': 'mdi mdi-check-circle',
|
'icon ok': 'mdi mdi-check-circle',
|
||||||
|
'icon markdown': 'mdi mdi-application',
|
||||||
|
|
||||||
'icon run': 'mdi mdi-play',
|
'icon run': 'mdi mdi-play',
|
||||||
'icon chevron-down': 'mdi mdi-chevron-down',
|
'icon chevron-down': 'mdi mdi-chevron-down',
|
||||||
@@ -60,6 +61,7 @@ const iconNames = {
|
|||||||
'img sql-file': 'mdi mdi-file',
|
'img sql-file': 'mdi mdi-file',
|
||||||
'img shell': 'mdi mdi-flash color-blue-7',
|
'img shell': 'mdi mdi-flash color-blue-7',
|
||||||
'img chart': 'mdi mdi-chart-bar color-magenta-7',
|
'img chart': 'mdi mdi-chart-bar color-magenta-7',
|
||||||
|
'img markdown': 'mdi mdi-application color-red-7',
|
||||||
|
|
||||||
'img free-table': 'mdi mdi-table color-green-7',
|
'img free-table': 'mdi mdi-table color-green-7',
|
||||||
'img macro': 'mdi mdi-hammer-wrench',
|
'img macro': 'mdi mdi-hammer-wrench',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'ace-builds/src-noconflict/mode-mysql';
|
|||||||
import 'ace-builds/src-noconflict/mode-pgsql';
|
import 'ace-builds/src-noconflict/mode-pgsql';
|
||||||
import 'ace-builds/src-noconflict/mode-sqlserver';
|
import 'ace-builds/src-noconflict/mode-sqlserver';
|
||||||
import 'ace-builds/src-noconflict/mode-javascript';
|
import 'ace-builds/src-noconflict/mode-javascript';
|
||||||
|
import 'ace-builds/src-noconflict/mode-markdown';
|
||||||
import 'ace-builds/src-noconflict/theme-github';
|
import 'ace-builds/src-noconflict/theme-github';
|
||||||
import 'ace-builds/src-noconflict/theme-twilight';
|
import 'ace-builds/src-noconflict/theme-twilight';
|
||||||
import 'ace-builds/src-noconflict/ext-searchbox';
|
import 'ace-builds/src-noconflict/ext-searchbox';
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ const Wrapper = styled.div`
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function JavaScriptEditor({
|
export default function GenericEditor({
|
||||||
|
mode,
|
||||||
value = undefined,
|
value = undefined,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
onChange = undefined,
|
onChange = undefined,
|
||||||
@@ -52,7 +53,7 @@ export default function JavaScriptEditor({
|
|||||||
<Wrapper ref={containerRef}>
|
<Wrapper ref={containerRef}>
|
||||||
<AceEditor
|
<AceEditor
|
||||||
ref={currentEditorRef}
|
ref={currentEditorRef}
|
||||||
mode="javascript"
|
mode={mode}
|
||||||
theme={theme.aceEditorTheme}
|
theme={theme.aceEditorTheme}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
name="UNIQUE_ID_OF_DIV"
|
name="UNIQUE_ID_OF_DIV"
|
||||||
54
packages/web/src/tabs/MarkdownEditorTab.js
Normal file
54
packages/web/src/tabs/MarkdownEditorTab.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import keycodes from '../utility/keycodes';
|
||||||
|
import GenericEditor from '../sqleditor/GenericEditor';
|
||||||
|
import MarkdownToolbar from '../charts/MarkdownToolbar';
|
||||||
|
import useEditorData from '../utility/useEditorData';
|
||||||
|
import SaveTabModal from '../modals/SaveTabModal';
|
||||||
|
import useModalState from '../modals/useModalState';
|
||||||
|
import LoadingInfo from '../widgets/LoadingInfo';
|
||||||
|
|
||||||
|
export default function MarkdownEditorTab({ tabid, tabVisible, toolbarPortalRef, ...other }) {
|
||||||
|
const { editorData, setEditorData, isLoading } = useEditorData({ tabid });
|
||||||
|
const saveFileModalState = useModalState();
|
||||||
|
|
||||||
|
const handleKeyDown = (data, hash, keyString, keyCode, event) => {
|
||||||
|
if (keyCode == keycodes.f5) {
|
||||||
|
event.preventDefault();
|
||||||
|
// handlePreview();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LoadingInfo message="Loading markdown page" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GenericEditor
|
||||||
|
value={editorData || ''}
|
||||||
|
onChange={setEditorData}
|
||||||
|
tabVisible={tabVisible}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
mode="markdown"
|
||||||
|
/>
|
||||||
|
{toolbarPortalRef &&
|
||||||
|
toolbarPortalRef.current &&
|
||||||
|
tabVisible &&
|
||||||
|
ReactDOM.createPortal(<MarkdownToolbar save={saveFileModalState.open} />, toolbarPortalRef.current)}
|
||||||
|
<SaveTabModal
|
||||||
|
modalState={saveFileModalState}
|
||||||
|
tabVisible={tabVisible}
|
||||||
|
data={editorData}
|
||||||
|
format="text"
|
||||||
|
folder="markdown"
|
||||||
|
tabid={tabid}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
packages/web/src/tabs/MarkdownViewTab.js
Normal file
41
packages/web/src/tabs/MarkdownViewTab.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import axios from '../utility/axios';
|
||||||
|
import LoadingInfo from '../widgets/LoadingInfo';
|
||||||
|
import Markdown from 'markdown-to-jsx';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function MarkdownViewTab({ file }) {
|
||||||
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
const [text, setText] = React.useState(null);
|
||||||
|
|
||||||
|
const handleLoad = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const resp = await axios.post('files/load', { folder: 'markdown', file, format: 'text' });
|
||||||
|
setText(resp.data);
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
handleLoad();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LoadingInfo message="Loading markdown page" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Markdown>{text || ''}</Markdown>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import { VerticalSplitter } from '../widgets/Splitter';
|
|||||||
import keycodes from '../utility/keycodes';
|
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 JavaScriptEditor from '../sqleditor/JavaScriptEditor';
|
import GenericEditor from '../sqleditor/GenericEditor';
|
||||||
import ShellToolbar from '../query/ShellToolbar';
|
import ShellToolbar from '../query/ShellToolbar';
|
||||||
import RunnerOutputPane from '../query/RunnerOutputPane';
|
import RunnerOutputPane from '../query/RunnerOutputPane';
|
||||||
import useShowModal from '../modals/showModal';
|
import useShowModal from '../modals/showModal';
|
||||||
@@ -104,12 +104,13 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, ...other
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<VerticalSplitter>
|
<VerticalSplitter>
|
||||||
<JavaScriptEditor
|
<GenericEditor
|
||||||
value={editorData || ''}
|
value={editorData || ''}
|
||||||
onChange={setEditorData}
|
onChange={setEditorData}
|
||||||
tabVisible={tabVisible}
|
tabVisible={tabVisible}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
editorRef={editorRef}
|
editorRef={editorRef}
|
||||||
|
mode="javascript"
|
||||||
/>
|
/>
|
||||||
<RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber} />
|
<RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber} />
|
||||||
</VerticalSplitter>
|
</VerticalSplitter>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import ArchiveFileTab from './ArchiveFileTab';
|
|||||||
import FreeTableTab from './FreeTableTab';
|
import FreeTableTab from './FreeTableTab';
|
||||||
import PluginTab from './PluginTab';
|
import PluginTab from './PluginTab';
|
||||||
import ChartTab from './ChartTab';
|
import ChartTab from './ChartTab';
|
||||||
|
import MarkdownEditorTab from './MarkdownEditorTab';
|
||||||
|
import MarkdownViewTab from './MarkdownViewTab';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
TableDataTab,
|
TableDataTab,
|
||||||
@@ -20,4 +22,6 @@ export default {
|
|||||||
FreeTableTab,
|
FreeTableTab,
|
||||||
PluginTab,
|
PluginTab,
|
||||||
ChartTab,
|
ChartTab,
|
||||||
|
MarkdownEditorTab,
|
||||||
|
MarkdownViewTab,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import { AppObjectList } from '../appobj/AppObjectList';
|
|||||||
import { useOpenedTabs } from '../utility/globalState';
|
import { useOpenedTabs } from '../utility/globalState';
|
||||||
import ClosedTabAppObject from '../appobj/ClosedTabAppObject';
|
import ClosedTabAppObject from '../appobj/ClosedTabAppObject';
|
||||||
import { WidgetsInnerContainer } from './WidgetStyles';
|
import { WidgetsInnerContainer } from './WidgetStyles';
|
||||||
import { SavedSqlFileAppObject, SavedShellFileAppObject, SavedChartFileAppObject } from '../appobj/SavedFileAppObject';
|
import {
|
||||||
|
SavedSqlFileAppObject,
|
||||||
|
SavedShellFileAppObject,
|
||||||
|
SavedChartFileAppObject,
|
||||||
|
SavedMarkdownFileAppObject,
|
||||||
|
} from '../appobj/SavedFileAppObject';
|
||||||
import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar';
|
import WidgetColumnBar, { WidgetColumnBarItem } from './WidgetColumnBar';
|
||||||
import { useFiles } from '../utility/metadataLoaders';
|
import { useFiles } from '../utility/metadataLoaders';
|
||||||
import useHasPermission from '../utility/useHasPermission';
|
import useHasPermission from '../utility/useHasPermission';
|
||||||
@@ -64,6 +69,18 @@ function SavedChartFilesList() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SavedMarkdownFilesList() {
|
||||||
|
const files = useFiles({ folder: 'markdown' });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<WidgetsInnerContainer>
|
||||||
|
<AppObjectList list={files} AppObjectComponent={SavedMarkdownFileAppObject} />
|
||||||
|
</WidgetsInnerContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function FilesWidget() {
|
export default function FilesWidget() {
|
||||||
const hasPermission = useHasPermission();
|
const hasPermission = useHasPermission();
|
||||||
return (
|
return (
|
||||||
@@ -72,20 +89,25 @@ export default function FilesWidget() {
|
|||||||
<ClosedTabsList />
|
<ClosedTabsList />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
{hasPermission('files/sql/read') && (
|
{hasPermission('files/sql/read') && (
|
||||||
<WidgetColumnBarItem title="Saved SQL files" name="sqlFiles" height="20%">
|
<WidgetColumnBarItem title="Saved SQL files" name="sqlFiles" height="15%">
|
||||||
<SavedSqlFilesList />
|
<SavedSqlFilesList />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
)}
|
)}
|
||||||
{hasPermission('files/shell/read') && (
|
{hasPermission('files/shell/read') && (
|
||||||
<WidgetColumnBarItem title="Saved shell files" name="shellFiles" height="20%">
|
<WidgetColumnBarItem title="Saved shell files" name="shellFiles" height="15%">
|
||||||
<SavedShellFilesList />
|
<SavedShellFilesList />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
)}
|
)}
|
||||||
{hasPermission('files/charts/read') && (
|
{hasPermission('files/charts/read') && (
|
||||||
<WidgetColumnBarItem title="Saved charts" name="charts" height="20%">
|
<WidgetColumnBarItem title="Saved charts" name="charts" height="15%">
|
||||||
<SavedChartFilesList />
|
<SavedChartFilesList />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
)}
|
)}
|
||||||
|
{hasPermission('files/markdown/read') && (
|
||||||
|
<WidgetColumnBarItem title="Saved markdown pages" name="markdown" height="15%">
|
||||||
|
<SavedMarkdownFilesList />
|
||||||
|
</WidgetColumnBarItem>
|
||||||
|
)}
|
||||||
</WidgetColumnBar>
|
</WidgetColumnBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ export default function ToolBar({ toolbarPortalRef }) {
|
|||||||
else setCurrentTheme('light');
|
else setCurrentTheme('light');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newMarkdown = () => {
|
||||||
|
openNewTab(setOpenedTabs, {
|
||||||
|
title: 'Page',
|
||||||
|
tabComponent: 'MarkdownEditorTab',
|
||||||
|
icon: 'img markdown',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
function openTabFromButton(button) {
|
function openTabFromButton(button) {
|
||||||
if (openedTabs.find((x) => x.tabComponent == 'InfoPageTab' && x.props && x.props.page == button.page)) {
|
if (openedTabs.find((x) => x.tabComponent == 'InfoPageTab' && x.props && x.props.page == button.page)) {
|
||||||
setOpenedTabs((tabs) =>
|
setOpenedTabs((tabs) =>
|
||||||
@@ -118,6 +126,9 @@ export default function ToolBar({ toolbarPortalRef }) {
|
|||||||
<ToolbarButton onClick={newFreeTable} icon="icon table">
|
<ToolbarButton onClick={newFreeTable} icon="icon table">
|
||||||
Free table editor
|
Free table editor
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
|
<ToolbarButton onClick={newMarkdown} icon="icon markdown">
|
||||||
|
New markdown
|
||||||
|
</ToolbarButton>
|
||||||
<ToolbarButton onClick={showImport} icon="icon import">
|
<ToolbarButton onClick={showImport} icon="icon import">
|
||||||
Import data
|
Import data
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user