custom markdown pages - basic concept

This commit is contained in:
Jan Prochazka
2020-12-10 15:12:19 +01:00
parent 7a0883ea03
commit 79ddbd439f
12 changed files with 201 additions and 11 deletions

View File

@@ -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
fn.extractKey = (data) => data.file;
});

View 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>
)}
</>
);
}

View File

@@ -3,7 +3,7 @@ import ToolbarButton from '../widgets/ToolbarButton';
import styled from 'styled-components';
import { TabPage, TabControl } from '../widgets/TabControl';
import dimensions from '../theme/dimensions';
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
import GenericEditor from '../sqleditor/GenericEditor';
import MacroParameters from './MacroParameters';
import { WidgetTitle } from '../widgets/WidgetStyles';
import { FormButton } from '../utility/forms';
@@ -113,7 +113,7 @@ export default function MacroDetail({ selectedMacro, setSelectedMacro, onChangeV
</MacroDetailTabWrapper>
</TabPage>
<TabPage label="JavaScript" key="javascript">
<JavaScriptEditor readOnly value={selectedMacro.code} />
<GenericEditor readOnly value={selectedMacro.code} mode="javascript" />
</TabPage>
</TabControl>
</MacroDetailContainer>

View File

@@ -39,6 +39,7 @@ const iconNames = {
'icon theme': 'mdi mdi-brightness-6',
'icon error': 'mdi mdi-close-circle',
'icon ok': 'mdi mdi-check-circle',
'icon markdown': 'mdi mdi-application',
'icon run': 'mdi mdi-play',
'icon chevron-down': 'mdi mdi-chevron-down',
@@ -60,6 +61,7 @@ const iconNames = {
'img sql-file': 'mdi mdi-file',
'img shell': 'mdi mdi-flash color-blue-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 macro': 'mdi mdi-hammer-wrench',

View File

@@ -10,6 +10,7 @@ import 'ace-builds/src-noconflict/mode-mysql';
import 'ace-builds/src-noconflict/mode-pgsql';
import 'ace-builds/src-noconflict/mode-sqlserver';
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-twilight';
import 'ace-builds/src-noconflict/ext-searchbox';

View File

@@ -12,7 +12,8 @@ const Wrapper = styled.div`
bottom: 0;
`;
export default function JavaScriptEditor({
export default function GenericEditor({
mode,
value = undefined,
readOnly = false,
onChange = undefined,
@@ -52,7 +53,7 @@ export default function JavaScriptEditor({
<Wrapper ref={containerRef}>
<AceEditor
ref={currentEditorRef}
mode="javascript"
mode={mode}
theme={theme.aceEditorTheme}
onChange={onChange}
name="UNIQUE_ID_OF_DIV"

View 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}
/>
</>
);
}

View 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>
);
}

View File

@@ -7,7 +7,7 @@ import { VerticalSplitter } from '../widgets/Splitter';
import keycodes from '../utility/keycodes';
import { changeTab } from '../utility/common';
import useSocket from '../utility/SocketProvider';
import JavaScriptEditor from '../sqleditor/JavaScriptEditor';
import GenericEditor from '../sqleditor/GenericEditor';
import ShellToolbar from '../query/ShellToolbar';
import RunnerOutputPane from '../query/RunnerOutputPane';
import useShowModal from '../modals/showModal';
@@ -104,12 +104,13 @@ export default function ShellTab({ tabid, tabVisible, toolbarPortalRef, ...other
return (
<>
<VerticalSplitter>
<JavaScriptEditor
<GenericEditor
value={editorData || ''}
onChange={setEditorData}
tabVisible={tabVisible}
onKeyDown={handleKeyDown}
editorRef={editorRef}
mode="javascript"
/>
<RunnerOutputPane runnerId={runnerId} executeNumber={executeNumber} />
</VerticalSplitter>

View File

@@ -8,6 +8,8 @@ import ArchiveFileTab from './ArchiveFileTab';
import FreeTableTab from './FreeTableTab';
import PluginTab from './PluginTab';
import ChartTab from './ChartTab';
import MarkdownEditorTab from './MarkdownEditorTab';
import MarkdownViewTab from './MarkdownViewTab';
export default {
TableDataTab,
@@ -20,4 +22,6 @@ export default {
FreeTableTab,
PluginTab,
ChartTab,
MarkdownEditorTab,
MarkdownViewTab,
};

View File

@@ -5,7 +5,12 @@ import { AppObjectList } from '../appobj/AppObjectList';
import { useOpenedTabs } from '../utility/globalState';
import ClosedTabAppObject from '../appobj/ClosedTabAppObject';
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 { useFiles } from '../utility/metadataLoaders';
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() {
const hasPermission = useHasPermission();
return (
@@ -72,20 +89,25 @@ export default function FilesWidget() {
<ClosedTabsList />
</WidgetColumnBarItem>
{hasPermission('files/sql/read') && (
<WidgetColumnBarItem title="Saved SQL files" name="sqlFiles" height="20%">
<WidgetColumnBarItem title="Saved SQL files" name="sqlFiles" height="15%">
<SavedSqlFilesList />
</WidgetColumnBarItem>
)}
{hasPermission('files/shell/read') && (
<WidgetColumnBarItem title="Saved shell files" name="shellFiles" height="20%">
<WidgetColumnBarItem title="Saved shell files" name="shellFiles" height="15%">
<SavedShellFilesList />
</WidgetColumnBarItem>
)}
{hasPermission('files/charts/read') && (
<WidgetColumnBarItem title="Saved charts" name="charts" height="20%">
<WidgetColumnBarItem title="Saved charts" name="charts" height="15%">
<SavedChartFilesList />
</WidgetColumnBarItem>
)}
{hasPermission('files/markdown/read') && (
<WidgetColumnBarItem title="Saved markdown pages" name="markdown" height="15%">
<SavedMarkdownFilesList />
</WidgetColumnBarItem>
)}
</WidgetColumnBar>
);
}

View File

@@ -66,6 +66,14 @@ export default function ToolBar({ toolbarPortalRef }) {
else setCurrentTheme('light');
};
const newMarkdown = () => {
openNewTab(setOpenedTabs, {
title: 'Page',
tabComponent: 'MarkdownEditorTab',
icon: 'img markdown',
});
};
function openTabFromButton(button) {
if (openedTabs.find((x) => x.tabComponent == 'InfoPageTab' && x.props && x.props.page == button.page)) {
setOpenedTabs((tabs) =>
@@ -118,6 +126,9 @@ export default function ToolBar({ toolbarPortalRef }) {
<ToolbarButton onClick={newFreeTable} icon="icon table">
Free table editor
</ToolbarButton>
<ToolbarButton onClick={newMarkdown} icon="icon markdown">
New markdown
</ToolbarButton>
<ToolbarButton onClick={showImport} icon="icon import">
Import data
</ToolbarButton>