Flexible LHS and RHS with flex

pull/3/head
Zef Hemel 2022-04-04 18:33:13 +02:00
parent a7cd3ea7e0
commit 1b0048cdcf
11 changed files with 193 additions and 149 deletions

View File

@ -32,14 +32,22 @@ export function flashNotification(message: string): Promise<void> {
return syscall("editor.flashNotification", message);
}
export function showRhs(html: string): Promise<void> {
return syscall("editor.showRhs", html);
export function showRhs(html: string, flex = 1): Promise<void> {
return syscall("editor.showRhs", html, flex);
}
export function hideRhs(): Promise<void> {
return syscall("editor.hideRhs");
}
export function showLhs(html: string, flex = 1): Promise<void> {
return syscall("editor.showLhs", html, flex);
}
export function hideLhs(): Promise<void> {
return syscall("editor.hideLhs");
}
export function insertAtPos(text: string, pos: number): Promise<void> {
return syscall("editor.insertAtPos", text, pos);
}

View File

@ -3,6 +3,8 @@ functions:
path: "./markdown.ts:togglePreview"
command:
name: "Toggle Markdown Preview"
key: Ctrl-p
mac: Cmd-p
preview:
path: "./markdown.ts:updateMarkdownPreview"
env: client

View File

@ -48,7 +48,7 @@ export async function updateMarkdownPreview() {
}
});
let html = md.render(renderMarkdown(mdTree));
await showRhs(`<html><body>${html}</body></html>`);
await showRhs(`<html><body>${html}</body></html>`, 1);
}
async function hideMarkdownPreview() {

View File

@ -1,9 +1,8 @@
import {useEffect, useRef} from "react";
// @ts-ignore
import iframeHtml from "bundle-text:./panel.html";
import {Simulate} from "react-dom/test-utils";
export function Panel({ html }: { html: string }) {
export function Panel({ html, flex }: { html: string; flex: number }) {
const iFrameRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
function loadContent() {
@ -25,7 +24,7 @@ export function Panel({ html }: { html: string }) {
};
}, [html]);
return (
<div className="panel">
<div className="panel" style={{ flex }}>
<iframe srcDoc={iframeHtml} ref={iFrameRef} />
</div>
);

View File

@ -1,6 +1,6 @@
import { Notification } from "../types";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFileLines } from "@fortawesome/free-solid-svg-icons";
import {Notification} from "../types";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faFileLines} from "@fortawesome/free-solid-svg-icons";
function prettyName(s: string | undefined): string {
if (!s) {
@ -14,27 +14,35 @@ export function TopBar({
unsavedChanges,
notifications,
onClick,
lhs,
rhs,
}: {
pageName?: string;
unsavedChanges: boolean;
notifications: Notification[];
onClick: () => void;
lhs?: React.ReactNode;
rhs?: React.ReactNode;
}) {
return (
<div id="top" onClick={onClick}>
<div className="inner">
<span className={`icon ${unsavedChanges ? "unsaved" : "saved"}`}>
<FontAwesomeIcon icon={faFileLines} />
</span>
<span className="current-page">{prettyName(pageName)}</span>
{notifications.length > 0 && (
<div className="status">
{notifications.map((notification) => (
<div key={notification.id}>{notification.message}</div>
))}
</div>
)}
{lhs}
<div className="main">
<div className="inner">
<span className={`icon ${unsavedChanges ? "unsaved" : "saved"}`}>
<FontAwesomeIcon icon={faFileLines} />
</span>
<span className="current-page">{prettyName(pageName)}</span>
{notifications.length > 0 && (
<div className="status">
{notifications.map((notification) => (
<div key={notification.id}>{notification.message}</div>
))}
</div>
)}
</div>
</div>
{rhs}
</div>
);
}

View File

@ -314,20 +314,19 @@ export class Editor implements AppEventDispatcher {
mac: "Cmd-i",
run: commands.insertMarker("_"),
},
{
key: "Ctrl-p",
mac: "Cmd-p",
run: (): boolean => {
window.open(location.href, "_blank")!.focus();
return true;
},
},
// {
// key: "Ctrl-p",
// mac: "Cmd-p",
// run: (): boolean => {
// window.open(location.href, "_blank")!.focus();
// return true;
// },
// },
{
key: "Ctrl-k",
mac: "Cmd-k",
run: (): boolean => {
this.viewDispatch({ type: "start-navigate" });
// asynchornously will dispatch pageListUpdate event
this.space.updatePageListAsync();
return true;
},
@ -463,7 +462,7 @@ export class Editor implements AppEventDispatcher {
}, [viewState.currentPage]);
return (
<div className={viewState.showRHS ? "rhs-open" : ""}>
<>
{viewState.showPageNavigator && (
<PageNavigator
allPages={viewState.allPages}
@ -493,7 +492,6 @@ export class Editor implements AppEventDispatcher {
commands={viewState.commands}
/>
)}
{viewState.showRHS && <Panel html={viewState.rhsHTML} />}
<TopBar
pageName={viewState.currentPage}
notifications={viewState.notifications}
@ -501,10 +499,28 @@ export class Editor implements AppEventDispatcher {
onClick={() => {
dispatch({ type: "start-navigate" });
}}
rhs={
!!viewState.showRHS && (
<div className="panel" style={{ flex: viewState.showRHS }} />
)
}
lhs={
!!viewState.showLHS && (
<div className="panel" style={{ flex: viewState.showLHS }} />
)
}
/>
<div id="editor" />
<div id="main">
{!!viewState.showLHS && (
<Panel html={viewState.lhsHTML} flex={viewState.showLHS} />
)}
<div id="editor" />
{!!viewState.showRHS && (
<Panel html={viewState.rhsHTML} flex={viewState.showRHS} />
)}
</div>
<StatusBar editorView={editor.editorView} />
</div>
</>
);
}

View File

@ -1,4 +1,4 @@
import { Action, AppViewState } from "./types";
import {Action, AppViewState} from "./types";
export default function reducer(
state: AppViewState,
@ -71,15 +71,27 @@ export default function reducer(
case "show-rhs":
return {
...state,
showRHS: true,
showRHS: action.flex,
rhsHTML: action.html,
};
case "hide-rhs":
return {
...state,
showRHS: false,
showRHS: 0,
rhsHTML: "",
};
case "show-lhs":
return {
...state,
showLHS: action.flex,
lhsHTML: action.html,
};
case "hide-lhs":
return {
...state,
showLHS: 0,
lhsHTML: "",
};
}
return state;
}

View File

@ -1,13 +1,12 @@
@import "constants.scss";
div.rhs-open #editor .cm-editor .cm-content {
max-width: 550px;
}
//div.rhs-open #editor .cm-editor .cm-content {
// max-width: 550px;
//}
.cm-editor {
width: 100%;
height: 100%;
font-size: var(--ident);
overflow-y: hidden;
.cm-content {
font-family: var(--editor-font);
@ -15,34 +14,8 @@ div.rhs-open #editor .cm-editor .cm-content {
max-width: $max-editor-width;
}
.other-cursor {
display: inline-block;
width: 2px;
margin-right: -2px;
height: 1em;
}
.cursor-label-container {
// display: none;
position: relative;
top: 2ch;
float: left;
width: 120px;
height: 2.2ch;
margin: 0;
padding: 0;
overflow: hidden;
font-family: Arial, Helvetica, sans-serif;
color: #fff;
border: gray 1px solid;
background-color: purple;
// font-size: 0.5em;
}
.cursor-label-container label {
margin: 0;
padding: 0;
font-size: 0.7em;
&.cm-focused {
outline: none !important;
}
.cm-selectionBackground {

View File

@ -14,105 +14,112 @@
html,
body {
height: 100%;
margin: 0;
height: 100%;
padding: 0;
width: 100%;
overflow: hidden;
}
.panel {
position: absolute;
top: $top-bar-height + 1px;
bottom: $bottom-bar-height;
right: 0;
width: 50%;
z-index: 20;
iframe {
border: 0;
width: 100%;
height: 100%;
padding: 0px;
margin: 0;
}
#root {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
#top {
display: flex;
flex-direction: row;
height: $top-bar-height;
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: rgb(213, 213, 213);
border-bottom: rgb(193, 193, 193) 1px solid;
color: rgb(55, 55, 55);
.inner {
padding-top: 12px;
max-width: $max-editor-width;
font-size: 28px;
margin: auto;
.main {
flex: 2;
.status {
float: right;
border: rgb(41, 41, 41) 1px solid;
border-radius: 5px;
padding: 3px;
font-size: 14px;
.inner {
max-width: $max-editor-width;
margin: auto;
padding-top: 12px;
font-size: 28px;
.status {
float: right;
border: rgb(41, 41, 41) 1px solid;
border-radius: 5px;
padding: 3px;
font-size: 14px;
}
.current-page {
font-family: var(--ui-font);
font-weight: bold;
}
.icon {
padding-left: 5px;
padding-right: 10px;
}
.icon.saved {
color: #015701;
}
.icon.unsaved {
color: #e19502;
}
}
}
.current-page {
font-family: var(--ui-font);
font-weight: bold;
}
.icon {
padding-left: 5px;
padding-right: 10px;
}
.panel {
flex: 1;
.icon.saved {
color: #015701;
}
}
}
.icon.unsaved {
color: #e19502;
#main {
display: flex;
flex-direction: row;
flex-grow: 1;
height: 0;
.panel {
flex: 1;
border-left: 1px solid #eee;
iframe {
border: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
}
}
#editor {
position: absolute;
top: $top-bar-height;
bottom: $bottom-bar-height;
left: 0;
right: 0;
overflow-y: hidden;
overflow-y: scroll;
flex: 2;
height: 100%;
}
div.rhs-open #editor {
right: 50%;
}
div.rhs-open #top .inner {
max-width: 550px;
padding-right: 50%;
}
@media only screen and (max-width: $max-editor-width) {
.cm-editor .cm-content {
margin: 0 10px !important;
}
}
//
//@media only screen and (max-width: $max-editor-width) {
// .cm-editor .cm-content {
// margin: 0 10px !important;
// }
//}
#bottom {
height: $bottom-bar-height;
background-color: #ccc;
position: fixed;
height: 25px;
text-align: right;
bottom: 0;
left: 0;
right: 0;
background-color: rgb(213, 213, 213);
border-top: rgb(193, 193, 193) 1px solid;

View File

@ -51,17 +51,30 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
"editor.flashNotification": (ctx, message: string) => {
editor.flashNotification(message);
},
"editor.showRhs": (ctx, html: string) => {
"editor.showRhs": (ctx, html: string, flex: number) => {
editor.viewDispatch({
type: "show-rhs",
html: html,
flex,
html,
});
},
"editor.hideRhs": (ctx, html: string) => {
"editor.hideRhs": (ctx) => {
editor.viewDispatch({
type: "hide-rhs",
});
},
"editor.showLhs": (ctx, html: string, flex: number) => {
editor.viewDispatch({
type: "show-lhs",
flex,
html,
});
},
"editor.hideLhs": (ctx) => {
editor.viewDispatch({
type: "hide-lhs",
});
},
"editor.insertAtPos": (ctx, text: string, pos: number) => {
editor.editorView!.dispatch({
changes: {

View File

@ -1,5 +1,5 @@
import { AppCommand } from "./hooks/command";
import { PageMeta } from "../common/types";
import {AppCommand} from "./hooks/command";
import {PageMeta} from "../common/types";
export const slashCommandRegexp = /\/[\w\-]*/;
@ -14,8 +14,10 @@ export type AppViewState = {
showPageNavigator: boolean;
showCommandPalette: boolean;
unsavedChanges: boolean;
showRHS: boolean;
showLHS: number; // 0 = hide, > 0 = flex
showRHS: number; // 0 = hide, > 0 = flex
rhsHTML: string;
lhsHTML: string;
allPages: Set<PageMeta>;
commands: Map<string, AppCommand>;
notifications: Notification[];
@ -25,8 +27,10 @@ export const initialViewState: AppViewState = {
showPageNavigator: false,
showCommandPalette: false,
unsavedChanges: false,
showRHS: false,
rhsHTML: "<h1>Loading...</h1>",
showLHS: 0,
showRHS: 0,
rhsHTML: "",
lhsHTML: "",
allPages: new Set(),
commands: new Map(),
notifications: [],
@ -44,5 +48,7 @@ export type Action =
| { type: "hide-palette" }
| { type: "show-notification"; notification: Notification }
| { type: "dismiss-notification"; id: number }
| { type: "show-rhs"; html: string }
| { type: "hide-rhs" };
| { type: "show-rhs"; html: string; flex: number }
| { type: "hide-rhs" }
| { type: "show-lhs"; html: string; flex: number }
| { type: "hide-lhs" };