pull/229/head
Zef Hemel 2022-12-21 16:08:51 +01:00
parent 3545d00d46
commit 52f0c14c78
9 changed files with 312 additions and 76 deletions

View File

@ -0,0 +1,123 @@
import type { CompletionContext, CompletionResult } from "../deps.ts";
import { useRef, useState } from "../deps.ts";
import { MiniEditor } from "./mini_editor.tsx";
export function Prompt({
message,
defaultValue,
vimMode,
darkMode,
completer,
callback,
}: {
message: string;
defaultValue?: string;
vimMode: boolean;
darkMode: boolean;
completer: (context: CompletionContext) => Promise<CompletionResult | null>;
callback: (value?: string) => void;
}) {
const [text, setText] = useState(defaultValue || "");
const returnEl = (
<div className="sb-modal-wrapper">
<div className="sb-modal-box">
<div className="sb-prompt">
<label>{message}</label>
<MiniEditor
text={defaultValue || ""}
vimMode={vimMode}
vimStartInInsertMode={true}
focus={true}
darkMode={darkMode}
completer={completer}
onEnter={(text) => {
callback(text);
return true;
}}
onEscape={() => {
callback();
}}
onChange={(text) => {
setText(text);
}}
/>
<button
onClick={() => {
callback(text);
}}
>
Ok
</button>
<button
onClick={() => {
callback();
}}
>
Cancel
</button>
</div>
</div>
</div>
);
return returnEl;
}
export function Confirm({
message,
callback,
}: {
message: string;
callback: (value: boolean) => void;
}) {
const okButtonRef = useRef<HTMLButtonElement>(null);
setTimeout(() => {
okButtonRef.current?.focus();
});
const returnEl = (
<div className="sb-modal-wrapper">
<div className="sb-modal-box">
<div
className="sb-prompt"
onKeyDown={(e) => {
e.stopPropagation();
e.preventDefault();
switch (e.key) {
case "Enter":
callback(true);
break;
case "Escape":
callback(false);
break;
}
}}
>
<label>{message}</label>
<div>
<button
ref={okButtonRef}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
callback(true);
}}
>
Ok
</button>
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
callback(false);
}}
>
Cancel
</button>
</div>
</div>
</div>
</div>
);
return returnEl;
}

View File

@ -127,8 +127,8 @@ export function FilterList({
}, []);
const returnEl = (
<div className="sb-filter-wrapper">
<div className="sb-filter-box">
<div className="sb-modal-wrapper">
<div className="sb-modal-box">
<div className="sb-header">
<label>{label}</label>
<MiniEditor

View File

@ -1,15 +1,4 @@
// Third party web dependencies
import {
BookIcon,
HomeIcon,
preactRender,
TerminalIcon,
useEffect,
useReducer,
yUndoManagerKeymap,
} from "./deps.ts";
// Third-party dependencies
import {
autocompletion,
closeBrackets,
@ -32,6 +21,7 @@ import {
keymap,
LanguageDescription,
LanguageSupport,
markdown,
runScopeHandlers,
searchKeymap,
standardKeymap,
@ -43,9 +33,7 @@ import {
ViewUpdate,
yamlLanguage,
} from "../common/deps.ts";
import { SilverBulletHooks } from "../common/manifest.ts";
import { markdown } from "../common/deps.ts";
import {
loadMarkdownExtensions,
MDExt,
@ -55,50 +43,14 @@ import { Space } from "../common/spaces/space.ts";
import { markdownSyscalls } from "../common/syscalls/markdown.ts";
import { FilterOption, PageMeta } from "../common/types.ts";
import { isMacLike, safeRun, throttle } from "../common/util.ts";
import { PathPageNavigator } from "./navigator.ts";
import reducer from "./reducer.ts";
// PlugOS Dependencies
import { createSandbox } from "../plugos/environments/webworker_sandbox.ts";
import { EventHook } from "../plugos/hooks/event.ts";
import assetSyscalls from "../plugos/syscalls/asset.ts";
import { eventSyscalls } from "../plugos/syscalls/event.ts";
import sandboxSyscalls from "../plugos/syscalls/sandbox.ts";
import { System } from "../plugos/system.ts";
import { CommandHook } from "./hooks/command.ts";
import { SlashCommandHook } from "./hooks/slash_command.ts";
// Syscalls
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
import { editorSyscalls } from "./syscalls/editor.ts";
import { fulltextSyscalls } from "./syscalls/fulltext.ts";
import { indexerSyscalls } from "./syscalls/index.ts";
import { spaceSyscalls } from "./syscalls/space.ts";
import { storeSyscalls } from "./syscalls/store.ts";
import { systemSyscalls } from "./syscalls/system.ts";
import assetSyscalls from "../plugos/syscalls/asset.ts";
// State and state transitions
import {
Action,
AppViewState,
BuiltinSettings,
initialViewState,
} from "./types.ts";
import type {
AppEvent,
ClickEvent,
CompleteEvent,
} from "../plug-api/app_event.ts";
// UI Components
import { CommandPalette } from "./components/command_palette.tsx";
import { FilterList } from "./components/filter.tsx";
import { PageNavigator } from "./components/page_navigator.tsx";
import { Panel } from "./components/panel.tsx";
import { TopBar } from "./components/top_bar.tsx";
// CodeMirror plugins
import { cleanModePlugins } from "./cm_plugins/clean.ts";
import { CollabState } from "./cm_plugins/collab.ts";
import {
attachmentExtension,
pasteLinkExtension,
@ -106,14 +58,50 @@ import {
import { inlineImagesPlugin } from "./cm_plugins/inline_image.ts";
import { lineWrapper } from "./cm_plugins/line_wrapper.ts";
import { smartQuoteKeymap } from "./cm_plugins/smart_quotes.ts";
import { cleanModePlugins } from "./cm_plugins/clean.ts";
import { Confirm, Prompt } from "./components/basic_modals.tsx";
import { CommandPalette } from "./components/command_palette.tsx";
import { FilterList } from "./components/filter.tsx";
import { PageNavigator } from "./components/page_navigator.tsx";
import { Panel } from "./components/panel.tsx";
import { TopBar } from "./components/top_bar.tsx";
import {
BookIcon,
HomeIcon,
preactRender,
TerminalIcon,
useEffect,
useReducer,
vim,
yUndoManagerKeymap,
} from "./deps.ts";
import { CommandHook } from "./hooks/command.ts";
import { SlashCommandHook } from "./hooks/slash_command.ts";
import { PathPageNavigator } from "./navigator.ts";
import reducer from "./reducer.ts";
import customMarkdownStyle from "./style.ts";
// Real-time collaboration
import { CollabState } from "./cm_plugins/collab.ts";
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
import { collabSyscalls } from "./syscalls/collab.ts";
import { Vim, vim, vimGetCm } from "./deps.ts";
import { editorSyscalls } from "./syscalls/editor.ts";
import { fulltextSyscalls } from "./syscalls/fulltext.ts";
import { indexerSyscalls } from "./syscalls/index.ts";
import { spaceSyscalls } from "./syscalls/space.ts";
import { storeSyscalls } from "./syscalls/store.ts";
import { systemSyscalls } from "./syscalls/system.ts";
import { AppViewState, BuiltinSettings, initialViewState } from "./types.ts";
// Third-party dependencies
// PlugOS Dependencies
// Syscalls
// State and state transitions
import type {
AppEvent,
ClickEvent,
CompleteEvent,
} from "../plug-api/app_event.ts";
// UI Components
// CodeMirror plugins
// Real-time collaboration
const frontMatterRegex = /^---\n(.*?)---\n/ms;
class PageState {
@ -423,6 +411,40 @@ export class Editor {
});
}
prompt(
message: string,
defaultValue = "",
): Promise<string | undefined> {
return new Promise((resolve) => {
this.viewDispatch({
type: "show-prompt",
message,
defaultValue,
callback: (value: string | undefined) => {
this.viewDispatch({ type: "hide-prompt" });
this.focus();
resolve(value);
},
});
});
}
confirm(
message: string,
): Promise<boolean> {
return new Promise((resolve) => {
this.viewDispatch({
type: "show-confirm",
message,
callback: (value: boolean) => {
this.viewDispatch({ type: "hide-confirm" });
this.focus();
resolve(value);
},
});
});
}
dispatchAppEvent(name: AppEvent, data?: any): Promise<any[]> {
return this.eventHook.dispatchEvent(name, data);
}
@ -924,6 +946,28 @@ export class Editor {
onSelect={viewState.filterBoxOnSelect}
/>
)}
{viewState.showPrompt && (
<Prompt
message={viewState.promptMessage!}
defaultValue={viewState.promptDefaultValue}
vimMode={viewState.uiOptions.vimMode}
darkMode={viewState.uiOptions.darkMode}
completer={this.miniEditorComplete.bind(this)}
callback={(value) => {
dispatch({ type: "hide-prompt" });
viewState.promptCallback!(value);
}}
/>
)}
{viewState.showConfirm && (
<Confirm
message={viewState.confirmMessage!}
callback={(value) => {
dispatch({ type: "hide-confirm" });
viewState.confirmCallback!(value);
}}
/>
)}
<TopBar
pageName={viewState.currentPage}
notifications={viewState.notifications}

View File

@ -139,6 +139,36 @@ export default function reducer(
filterBoxOptions: [],
filterBoxHelpText: "",
};
case "show-prompt":
return {
...state,
showPrompt: true,
promptDefaultValue: action.defaultValue,
promptMessage: action.message,
promptCallback: action.callback,
};
case "hide-prompt":
return {
...state,
showPrompt: false,
promptDefaultValue: undefined,
promptMessage: undefined,
promptCallback: undefined,
};
case "show-confirm":
return {
...state,
showConfirm: true,
confirmMessage: action.message,
confirmCallback: action.callback,
};
case "hide-confirm":
return {
...state,
showConfirm: false,
confirmMessage: undefined,
confirmCallback: undefined,
};
case "set-ui-option":
return {
...state,

View File

@ -1,5 +1,5 @@
@use "editor";
@use "filter_box";
@use "modals";
@use "theme";
@font-face {

View File

@ -1,4 +1,4 @@
.sb-filter-wrapper {
.sb-modal-wrapper {
position: absolute;
margin: auto;
max-width: 500px;
@ -11,7 +11,7 @@
z-index: 100;
}
.sb-filter-box {
.sb-modal-box {
border-radius: 8px;
overflow: hidden;
margin: 10px;
@ -27,6 +27,19 @@
}
.sb-prompt {
padding: 13px 10px 10px 10px;
label {
font-weight: bold;
}
.sb-mini-editor {
border: 1px solid #333;
margin: 10px 0;
}
}
.sb-help-text {
padding: 5px;
}
@ -46,9 +59,7 @@
.sb-selected-option {
padding: 8px;
cursor: pointer;
// height: 20px;
line-height: 20px;
// white-space: nowrap;
}
.sb-selected-option {

View File

@ -69,7 +69,7 @@
}
/* Filter boxes */
.sb-filter-box {
.sb-modal-box {
background-color: #fff;
border: rgb(103, 103, 103) 1px solid;
box-shadow: rgba(0, 0, 0, 0.35) 0px 20px 20px;
@ -87,29 +87,29 @@
}
}
.sb-filter-box .sb-header {
.sb-modal-box .sb-header {
border-bottom: 1px rgb(108, 108, 108) solid;
}
.sb-filter-box .sb-header .sb-mini-editor {
.sb-modal-box .sb-header .sb-mini-editor {
font-family: var(--ui-font);
width: 100%;
border: 0;
outline: none;
}
.sb-filter-box .sb-help-text {
.sb-modal-box .sb-help-text {
background-color: #eee;
border-bottom: 1px rgb(108, 108, 108) solid;
color: #555;
}
.sb-filter-box .sb-selected-option {
.sb-modal-box .sb-selected-option {
color: #eee;
}
.sb-filter-box .sb-option .sb-hint,
.sb-filter-box .sb-selected-option .sb-hint {
.sb-modal-box .sb-option .sb-hint,
.sb-modal-box .sb-selected-option .sb-hint {
color: #eee;
background-color: #212476;
}
@ -570,7 +570,7 @@ html[data-theme="dark"] {
.sb-filter-box,
.sb-modal-box,
/* duplicating the class name to increase specificity */
.sb-help-text.sb-help-text {
color: #ccc;

View File

@ -139,14 +139,14 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
_ctx,
message: string,
defaultValue = "",
): string | null => {
return prompt(message, defaultValue);
): Promise<string | undefined> => {
return editor.prompt(message, defaultValue);
},
"editor.confirm": (
_ctx,
message: string,
): boolean => {
return confirm(message);
): Promise<boolean> => {
return editor.confirm(message);
},
"editor.getUiOption": (_ctx, key: string): any => {
return (editor.viewState.uiOptions as any)[key];

View File

@ -42,12 +42,24 @@ export type AppViewState = {
forcedROMode: boolean;
};
// Filter box
showFilterBox: boolean;
filterBoxLabel: string;
filterBoxPlaceHolder: string;
filterBoxOptions: FilterOption[];
filterBoxHelpText: string;
filterBoxOnSelect: (option: FilterOption | undefined) => void;
// Prompt
showPrompt: boolean;
promptMessage?: string;
promptDefaultValue?: string;
promptCallback?: (value: string | undefined) => void;
// Confirm
showConfirm: boolean;
confirmMessage?: string;
confirmCallback?: (value: boolean) => void;
};
export const initialViewState: AppViewState = {
@ -78,6 +90,9 @@ export const initialViewState: AppViewState = {
filterBoxOnSelect: () => {},
filterBoxOptions: [],
filterBoxPlaceHolder: "",
showPrompt: false,
showConfirm: false,
};
export type Action =
@ -112,4 +127,17 @@ export type Action =
onSelect: (option: FilterOption | undefined) => void;
}
| { type: "hide-filterbox" }
| {
type: "show-prompt";
message: string;
defaultValue: string;
callback: (value: string | undefined) => void;
}
| { type: "hide-prompt" }
| {
type: "show-confirm";
message: string;
callback: (value: boolean) => void;
}
| { type: "hide-confirm" }
| { type: "set-ui-option"; key: string; value: any };