diff --git a/frontend/app/block/block.less b/frontend/app/block/block.less
index c4d75ee65..5550eda0a 100644
--- a/frontend/app/block/block.less
+++ b/frontend/app/block/block.less
@@ -84,8 +84,13 @@
gap: 8px;
color: var(--main-text-color);
- .block-frame-back-button {
+ .block-frame-preicon-button {
+ opacity: 0.7;
cursor: pointer;
+
+ &:hover {
+ opacity: 1;
+ }
}
.block-frame-view-icon {
@@ -117,6 +122,16 @@
display: flex;
align-items: center;
+ .block-frame-endicon-button {
+ opacity: 0.7;
+ cursor: pointer;
+ padding: 4px 6px;
+
+ &:hover {
+ opacity: 1;
+ }
+ }
+
.block-frame-settings {
display: flex;
width: 24px;
diff --git a/frontend/app/block/block.tsx b/frontend/app/block/block.tsx
index 46c1267b4..21962c555 100644
--- a/frontend/app/block/block.tsx
+++ b/frontend/app/block/block.tsx
@@ -230,8 +230,8 @@ const BlockFrame_Default_Component = ({
let isFocused = jotai.useAtomValue(isFocusedAtom);
const viewIcon = jotai.useAtomValue(viewModel.viewIcon);
const viewText = jotai.useAtomValue(viewModel.viewText);
- const hasBackButton = jotai.useAtomValue(viewModel.hasBackButton);
- const hasForwardButton = jotai.useAtomValue(viewModel.hasForwardButton);
+ const preIconButton = jotai.useAtomValue(viewModel.preIconButton);
+ const endIconButtons = jotai.useAtomValue(viewModel.endIconButtons);
if (preview) {
isFocused = true;
}
@@ -242,6 +242,43 @@ const BlockFrame_Default_Component = ({
if (isFocused && blockData?.meta?.["frame:bordercolor:focused"]) {
style.borderColor = blockData.meta["frame:bordercolor:focused"];
}
+ let preIconButtonElem: JSX.Element = null;
+ if (preIconButton) {
+ preIconButtonElem = (
+
+
+
+ );
+ }
+ let endIconsElem: JSX.Element[] = [];
+ if (endIconButtons && endIconButtons.length > 0) {
+ for (let idx = 0; idx < endIconButtons.length; idx++) {
+ const button = endIconButtons[idx];
+ endIconsElem.push(
+
+
+
+ );
+ }
+ }
+ endIconsElem.push(
+ handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)}
+ >
+
+
+ );
+ endIconsElem.push(
+
+
+
+ );
return (
handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)}
>
- {hasBackButton && !hasForwardButton && (
-
-
-
- )}
+ {preIconButtonElem}
{getBlockHeaderIcon(viewIcon, blockData)}
{blockViewToName(blockData?.view)}
{settingsConfig?.blockheader?.showblockids && (
@@ -275,19 +308,8 @@ const BlockFrame_Default_Component = ({
{util.isBlank(viewText) ? null :
{viewText}
}
-
-
handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)}
- >
-
-
-
-
-
-
+
{endIconsElem}
-
{preview ? : children}
);
@@ -412,8 +434,8 @@ function makeDefaultViewModel(blockId: string): ViewModel {
const blockData = get(blockDataAtom);
return blockData?.meta?.title;
}),
- hasBackButton: jotai.atom(false),
- hasForwardButton: jotai.atom(false),
+ preIconButton: jotai.atom(null),
+ endIconButtons: jotai.atom(null),
hasSearch: jotai.atom(false),
};
return viewModel;
diff --git a/frontend/app/view/directorypreview.tsx b/frontend/app/view/directorypreview.tsx
index dd3967c26..3d832aa1c 100644
--- a/frontend/app/view/directorypreview.tsx
+++ b/frontend/app/view/directorypreview.tsx
@@ -4,6 +4,7 @@
import * as services from "@/store/services";
import * as keyutil from "@/util/keyutil";
import * as util from "@/util/util";
+import type { PreviewModel } from "@/view/preview";
import {
Row,
Table,
@@ -30,7 +31,7 @@ interface DirectoryTableProps {
setFileName: (_: string) => void;
setSearch: (_: string) => void;
setSelectedPath: (_: string) => void;
- setRefresh: React.Dispatch>;
+ setRefreshVersion: React.Dispatch>;
}
const columnHelper = createColumnHelper();
@@ -143,7 +144,7 @@ function DirectoryTable({
setFileName,
setSearch,
setSelectedPath,
- setRefresh,
+ setRefreshVersion,
}: DirectoryTableProps) {
let settings = jotai.useAtomValue(atoms.settingsConfigAtom);
const getIconFromMimeType = React.useCallback(
@@ -299,7 +300,7 @@ function DirectoryTable({
setFocusIndex={setFocusIndex}
setSearch={setSearch}
setSelectedPath={setSelectedPath}
- setRefresh={setRefresh}
+ setRefreshVersion={setRefreshVersion}
/>
) : (
)}
@@ -327,7 +328,7 @@ interface TableBodyProps {
setFileName: (_: string) => void;
setSearch: (_: string) => void;
setSelectedPath: (_: string) => void;
- setRefresh: React.Dispatch>;
+ setRefreshVersion: React.Dispatch>;
}
function TableBody({
@@ -339,7 +340,7 @@ function TableBody({
setFileName,
setSearch,
setSelectedPath,
- setRefresh,
+ setRefreshVersion,
}: TableBodyProps) {
const dummyLineRef = React.useRef(null);
const parentRef = React.useRef(null);
@@ -391,7 +392,7 @@ function TableBody({
label: "Delete File",
click: async () => {
await services.FileService.DeleteFile(path).catch((e) => console.log(e)); //todo these errors need a popup
- setRefresh((current) => !current);
+ setRefreshVersion((current) => current + 1);
},
});
menu.push({
@@ -402,7 +403,7 @@ function TableBody({
});
ContextMenuModel.showContextMenu(menu, e);
},
- [setRefresh]
+ [setRefreshVersion]
);
const displayRow = React.useCallback(
@@ -463,17 +464,26 @@ const MemoizedTableBody = React.memo(
interface DirectoryPreviewProps {
fileNameAtom: jotai.WritableAtom;
+ model: PreviewModel;
}
-function DirectoryPreview({ fileNameAtom }: DirectoryPreviewProps) {
- console.log("DirectoryPreview render");
+function DirectoryPreview({ fileNameAtom, model }: DirectoryPreviewProps) {
const [searchText, setSearchText] = React.useState("");
const [focusIndex, setFocusIndex] = React.useState(0);
const [content, setContent] = React.useState([]);
const [fileName, setFileName] = jotai.useAtom(fileNameAtom);
- const [hideHiddenFiles, setHideHiddenFiles] = React.useState(true);
+ const [hideHiddenFiles, setHideHiddenFiles] = jotai.useAtom(model.showHiddenFiles);
const [selectedPath, setSelectedPath] = React.useState("");
- const [refresh, setRefresh] = React.useState(false);
+ const [refreshVersion, setRefreshVersion] = React.useState(0);
+
+ React.useEffect(() => {
+ model.refreshCallback = () => {
+ setRefreshVersion((refreshVersion) => refreshVersion + 1);
+ };
+ return () => {
+ model.refreshCallback = null;
+ };
+ }, [setRefreshVersion]);
React.useEffect(() => {
const getContent = async () => {
@@ -489,7 +499,7 @@ function DirectoryPreview({ fileNameAtom }: DirectoryPreviewProps) {
setContent(filtered);
};
getContent();
- }, [fileName, searchText, hideHiddenFiles, refresh]);
+ }, [fileName, searchText, hideHiddenFiles, refreshVersion]);
const handleKeyDown = React.useCallback(
(waveEvent: WaveKeyboardEvent): boolean => {
@@ -533,15 +543,6 @@ function DirectoryPreview({ fileNameAtom }: DirectoryPreviewProps) {
autoFocus={true}
value={searchText}
/>
- setHideHiddenFiles((current) => !current)} className="dir-table-button">
- {!hideHiddenFiles && }
- {hideHiddenFiles && }
- {}}>
-
- setRefresh((current) => !current)} className="dir-table-button">
-
- {}}>
-
);
diff --git a/frontend/app/view/preview.tsx b/frontend/app/view/preview.tsx
index c0f6b900a..476024b82 100644
--- a/frontend/app/view/preview.tsx
+++ b/frontend/app/view/preview.tsx
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { Markdown } from "@/element/markdown";
-import { getBackendHostPort, getObjectId, globalStore, useBlockAtom } from "@/store/global";
+import { getBackendHostPort, globalStore, useBlockAtom } from "@/store/global";
import * as services from "@/store/services";
import * as WOS from "@/store/wos";
import * as util from "@/util/util";
@@ -26,8 +26,8 @@ export class PreviewModel implements ViewModel {
viewIcon: jotai.Atom;
viewName: jotai.Atom;
viewText: jotai.Atom;
- hasBackButton: jotai.Atom;
- hasForwardButton: jotai.Atom;
+ preIconButton: jotai.Atom;
+ endIconButtons: jotai.Atom;
hasSearch: jotai.Atom;
fileName: jotai.WritableAtom;
@@ -37,12 +37,16 @@ export class PreviewModel implements ViewModel {
fileMimeTypeLoadable: jotai.Atom>;
fileContent: jotai.Atom>;
+ showHiddenFiles: jotai.PrimitiveAtom;
+ refreshCallback: () => void;
+
setPreviewFileName(fileName: string) {
services.ObjectService.UpdateObjectMeta(`block:${this.blockId}`, { file: fileName });
}
constructor(blockId: string) {
this.blockId = blockId;
+ this.showHiddenFiles = jotai.atom(true);
this.blockAtom = WOS.getWaveObjectAtom(`block:${blockId}`);
this.viewIcon = jotai.atom((get) => {
let blockData = get(this.blockAtom);
@@ -57,13 +61,34 @@ export class PreviewModel implements ViewModel {
this.viewText = jotai.atom((get) => {
return get(this.fileName);
});
- this.hasBackButton = jotai.atom(true);
- this.hasForwardButton = jotai.atom((get) => {
+ this.preIconButton = jotai.atom((get) => {
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
if (mimeType == "directory") {
- return true;
+ return null;
}
- return false;
+ return {
+ icon: "chevron-left",
+ click: this.onBack.bind(this),
+ };
+ });
+ this.endIconButtons = jotai.atom((get) => {
+ const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
+ if (mimeType == "directory") {
+ let showHiddenFiles = get(this.showHiddenFiles);
+ return [
+ {
+ icon: showHiddenFiles ? "eye" : "eye-slash",
+ click: () => {
+ globalStore.set(this.showHiddenFiles, (prev) => !prev);
+ },
+ },
+ {
+ icon: "arrows-rotate",
+ click: () => this.refreshCallback?.(),
+ },
+ ];
+ }
+ return null;
});
this.hasSearch = jotai.atom(false);
@@ -310,7 +335,6 @@ function iconForFile(mimeType: string, fileName: string): string {
}
function PreviewView({ blockId, model }: { blockId: string; model: PreviewModel }) {
- console.log("render previewview", getObjectId(model));
const ref = useRef(null);
const blockAtom = WOS.getWaveObjectAtom(`block:${blockId}`);
const fileNameAtom = model.fileName;
@@ -359,7 +383,7 @@ function PreviewView({ blockId, model }: { blockId: string; model: PreviewModel
) {
specializedView = ;
} else if (mimeType === "directory") {
- specializedView = ;
+ specializedView = ;
} else {
specializedView = (
diff --git a/frontend/types/custom.d.ts b/frontend/types/custom.d.ts
index 657328b05..5ef825cd8 100644
--- a/frontend/types/custom.d.ts
+++ b/frontend/types/custom.d.ts
@@ -116,12 +116,19 @@ declare global {
type SubjectWithRef = rxjs.Subject & { refCount: number; release: () => void };
+ type IconButtonDecl = {
+ icon: string;
+ title?: string;
+ click: () => void;
+ };
+
interface ViewModel {
viewIcon: jotai.Atom;
viewName: jotai.Atom;
viewText: jotai.Atom;
- hasBackButton: jotai.Atom;
- hasForwardButton: jotai.Atom;
+ preIconButton: jotai.Atom;
+ endIconButtons: jotai.Atom;
+
hasSearch: jotai.Atom;
onBack?: () => void;