1
0
silverbullet/web/components/top_bar.tsx

162 lines
4.8 KiB
TypeScript
Raw Normal View History

import {
CompletionContext,
CompletionResult,
useEffect,
useRef,
} from "../deps.ts";
import type { ComponentChildren, FunctionalComponent } from "../deps.ts";
import { Notification } from "../types.ts";
import { FeatherProps } from "https://esm.sh/v99/preact-feather@4.2.1/dist/types";
import { MiniEditor } from "./mini_editor.tsx";
2023-06-14 18:58:08 +00:00
import process from "https://deno.land/std@0.177.1/node/process.ts";
export type ActionButton = {
icon: FunctionalComponent<FeatherProps>;
description: string;
callback: () => void;
href?: string;
};
export function TopBar({
pageName,
unsavedChanges,
synced,
isLoading,
notifications,
onRename,
actionButtons,
darkMode,
vimMode,
2023-06-14 18:58:08 +00:00
progressPerc,
completer,
2022-04-04 16:33:13 +00:00
lhs,
rhs,
}: {
pageName?: string;
unsavedChanges: boolean;
synced: boolean;
isLoading: boolean;
notifications: Notification[];
darkMode: boolean;
vimMode: boolean;
2023-06-14 18:58:08 +00:00
progressPerc?: number;
onRename: (newName?: string) => Promise<void>;
completer: (context: CompletionContext) => Promise<CompletionResult | null>;
actionButtons: ActionButton[];
lhs?: ComponentChildren;
rhs?: ComponentChildren;
}) {
const inputRef = useRef<HTMLInputElement>(null);
2022-09-24 17:01:16 +00:00
// Another one of my less proud moments:
// Somehow I cannot seem to proerply limit the width of the page name, so I'm doing
// it this way. If you have a better way to do this, please let me know!
useEffect(() => {
function resizeHandler() {
const editorWidth = parseInt(
getComputedStyle(document.getElementById("sb-root")!).getPropertyValue(
"--editor-width",
),
);
const currentPageElement = document.getElementById("sb-current-page");
if (currentPageElement) {
// Temporarily make it very narrow to give the parent space
currentPageElement.style.width = "10px";
const innerDiv = currentPageElement.parentElement!.parentElement!;
// Then calculate a new width
currentPageElement.style.width = `${
Math.min(editorWidth - 150, innerDiv.clientWidth - 150)
}px`;
}
}
globalThis.addEventListener("resize", resizeHandler);
// Stop listening on unmount
return () => {
globalThis.removeEventListener("resize", resizeHandler);
};
}, []);
return (
<div id="sb-top" className={synced ? undefined : "sb-sync-error"}>
2022-04-04 16:33:13 +00:00
{lhs}
<div className="main">
<div className="inner">
2022-12-16 10:39:46 +00:00
<div className="wrapper">
<span
id="sb-current-page"
className={isLoading
? "sb-loading"
: unsavedChanges
? "sb-unsaved"
: "sb-saved"}
2022-12-16 10:39:46 +00:00
>
<MiniEditor
text={pageName ?? ""}
vimMode={vimMode}
darkMode={darkMode}
onBlur={(newName) => {
if (newName !== pageName) {
return onRename(newName);
} else {
return onRename();
2022-12-16 10:39:46 +00:00
}
}}
completer={completer}
onEnter={(newName) => {
onRename(newName);
2022-12-16 10:39:46 +00:00
}}
/>
</span>
{notifications.length > 0 && (
<div className="sb-notifications">
{notifications.map((notification) => (
<div
key={notification.id}
className={`sb-notification-${notification.type}`}
>
{notification.message}
</div>
))}
</div>
)}
<div className="sb-actions">
2023-06-14 18:58:08 +00:00
{progressPerc !== undefined &&
(
<div className="progress-wrapper" title={`${progressPerc}%`}>
<div
className="progress-bar"
style={`background: radial-gradient(closest-side, white 79%, transparent 80% 100%), conic-gradient(#282828 ${progressPerc}%, #adadad 0);`}
>
2023-06-14 18:58:08 +00:00
{progressPerc}%
</div>
</div>
)}
{actionButtons.map((actionButton) => {
const button = (
<button
onClick={(e) => {
e.preventDefault();
actionButton.callback();
e.stopPropagation();
}}
title={actionButton.description}
>
<actionButton.icon size={18} />
</button>
);
2023-06-14 18:58:08 +00:00
return actionButton.href !== undefined
? <a href={actionButton.href}>{button}</a>
: button;
})}
2022-04-04 16:33:13 +00:00
</div>
2022-05-06 16:55:04 +00:00
</div>
2022-04-04 16:33:13 +00:00
</div>
</div>
2022-04-04 16:33:13 +00:00
{rhs}
</div>
);
}