feat(layout):完成基本界面布局以及文件夹扫描功能。
This commit is contained in:
parent
ebc7c26522
commit
848c8c01e7
101
src-tauri/Cargo.lock
generated
101
src-tauri/Cargo.lock
generated
|
@ -1626,6 +1626,17 @@ dependencies = [
|
||||||
"objc_exception",
|
"objc_exception",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-foundation"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"objc",
|
||||||
|
"objc_id",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc_exception"
|
name = "objc_exception"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -2088,6 +2099,30 @@ version = "0.6.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfd"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"dispatch",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"gtk-sys",
|
||||||
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"objc_id",
|
||||||
|
"raw-window-handle",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"windows 0.37.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2536,6 +2571,7 @@ dependencies = [
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"regex",
|
"regex",
|
||||||
|
"rfd",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -3081,6 +3117,18 @@ dependencies = [
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-futures"
|
||||||
|
version = "0.4.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.84"
|
version = "0.2.84"
|
||||||
|
@ -3110,6 +3158,16 @@ version = "0.2.84"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webkit2gtk"
|
name = "webkit2gtk"
|
||||||
version = "0.18.2"
|
version = "0.18.2"
|
||||||
|
@ -3226,6 +3284,19 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc 0.37.0",
|
||||||
|
"windows_i686_gnu 0.37.0",
|
||||||
|
"windows_i686_msvc 0.37.0",
|
||||||
|
"windows_x86_64_gnu 0.37.0",
|
||||||
|
"windows_x86_64_msvc 0.37.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
@ -3326,6 +3397,12 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
@ -3338,6 +3415,12 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
@ -3350,6 +3433,12 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
@ -3362,6 +3451,12 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
@ -3380,6 +3475,12 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|
|
@ -14,7 +14,7 @@ tauri-build = { version = "1.2", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
once_cell = "1.17.0"
|
once_cell = "1.17.0"
|
||||||
tauri = { version = "1.2", features = ["shell-open"] }
|
tauri = { version = "1.2", features = ["dialog-open", "shell-open"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
chrono = { version = "0.4.23", features = ["serde"] }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
|
|
36
src-tauri/src/commands/files.rs
Normal file
36
src-tauri/src/commands/files.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use serde::Serialize;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct FileItem {
|
||||||
|
pub filename: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn scan_directory(target: String) -> Result<Vec<FileItem>, String> {
|
||||||
|
WalkDir::new(target)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|f| f.ok())
|
||||||
|
.filter(|f| f.path().is_file())
|
||||||
|
.map(|f| {
|
||||||
|
Ok(FileItem {
|
||||||
|
filename: f
|
||||||
|
.path()
|
||||||
|
.file_name()
|
||||||
|
.ok_or(anyhow!("不能获取到文件名。"))?
|
||||||
|
.to_owned()
|
||||||
|
.into_string()
|
||||||
|
.unwrap(),
|
||||||
|
path: f
|
||||||
|
.path()
|
||||||
|
.clone()
|
||||||
|
.to_str()
|
||||||
|
.ok_or(anyhow!("不能获取到文件路径。"))?
|
||||||
|
.to_string(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<FileItem>, anyhow::Error>>()
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
use tauri::{App, AppHandle, Runtime, Window};
|
use tauri::{App, AppHandle, Runtime, Window};
|
||||||
|
|
||||||
|
mod files;
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::files::*;
|
||||||
|
}
|
||||||
|
|
||||||
/// 用于持有应用实例,可存放不同的应用实例。
|
/// 用于持有应用实例,可存放不同的应用实例。
|
||||||
pub enum AppHold<'a, R: Runtime> {
|
pub enum AppHold<'a, R: Runtime> {
|
||||||
Instance(&'a App<R>),
|
Instance(&'a App<R>),
|
||||||
|
|
|
@ -8,7 +8,7 @@ use tauri::Manager;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![])
|
.invoke_handler(tauri::generate_handler![commands::prelude::scan_directory])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let main_window = app.get_window("main").unwrap();
|
let main_window = app.get_window("main").unwrap();
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
|
|
@ -16,6 +16,14 @@
|
||||||
"shell": {
|
"shell": {
|
||||||
"all": false,
|
"all": false,
|
||||||
"open": true
|
"open": true
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"all": false,
|
||||||
|
"ask": false,
|
||||||
|
"confirm": false,
|
||||||
|
"message": false,
|
||||||
|
"open": true,
|
||||||
|
"save": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { Box, Group } from "@mantine/core";
|
import { Group, Stack } from '@mantine/core';
|
||||||
import { FC } from "react";
|
import { FC } from 'react';
|
||||||
import { Outlet } from "react-router-dom";
|
import { ComicView } from './components/ComicView';
|
||||||
import { NavMenu } from "./NavMenu";
|
import { PicToolbar } from './components/PicToolbar';
|
||||||
|
import { NavMenu } from './NavMenu';
|
||||||
|
|
||||||
export const MainLayout: FC = () => {
|
export const MainLayout: FC = () => {
|
||||||
return (
|
return (
|
||||||
<Group grow noWrap spacing={0} h="100%" w="100%">
|
<Group grow noWrap spacing={0} h="100%" w="100%">
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
<Box h="inherit" w="inherit" maw="100%" sx={{ flexGrow: 5 }}>
|
<Stack h="inherit" w="inherit" maw="100%" sx={{ flexGrow: 5 }} spacing={0}>
|
||||||
<Outlet />
|
<PicToolbar />
|
||||||
</Box>
|
<ComicView />
|
||||||
|
</Stack>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,32 +1,38 @@
|
||||||
import { Stack, useMantineTheme } from "@mantine/core";
|
import { Stack, useMantineTheme } from '@mantine/core';
|
||||||
import { ifElse, path, propEq } from "ramda";
|
import { ifElse, path, propEq } from 'ramda';
|
||||||
import { FC, useMemo } from "react";
|
import { FC, useMemo } from 'react';
|
||||||
|
import { FileList } from './components/FileList';
|
||||||
|
import { FileToolbar } from './components/FileTools';
|
||||||
|
|
||||||
const bgSelectFn = ifElse(
|
const bgSelectFn = ifElse(
|
||||||
propEq("colorScheme", "dark"),
|
propEq('colorScheme', 'dark'),
|
||||||
path(["colors", "cbg", 2]),
|
path(['colors', 'cbg', 2]),
|
||||||
path(["colors", "cbg", 7])
|
path(['colors', 'cbg', 7])
|
||||||
);
|
);
|
||||||
|
|
||||||
export const NavMenu: FC = () => {
|
export const NavMenu: FC = () => {
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const normalColor = useMemo(() => path(["violet", 7])(theme.colors), [theme.colors]);
|
const normalColor = useMemo(() => path(['violet', 7])(theme.colors), [theme.colors]);
|
||||||
const activatedColor = useMemo(() => path<string>(["violet", 3])(theme.colors), [theme.colors]);
|
const activatedColor = useMemo(() => path<string>(['violet', 3])(theme.colors), [theme.colors]);
|
||||||
const disabledColor = useMemo(() => path<string>(["gray", 7])(theme.colors), [theme.colors]);
|
const disabledColor = useMemo(() => path<string>(['gray', 7])(theme.colors), [theme.colors]);
|
||||||
const navMenuBg = useMemo(() => bgSelectFn(theme), [theme, theme]);
|
const navMenuBg = useMemo(() => bgSelectFn(theme), [theme, theme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
spacing={24}
|
spacing={8}
|
||||||
maw={64}
|
miw={200}
|
||||||
h="inherit"
|
h="inherit"
|
||||||
sx={(theme) => ({
|
sx={theme => ({
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
backgroundColor: navMenuBg,
|
backgroundColor: navMenuBg,
|
||||||
|
overflow: 'hidden'
|
||||||
})}
|
})}
|
||||||
|
px={4}
|
||||||
|
py={4}
|
||||||
align="center"
|
align="center"
|
||||||
px={16}
|
>
|
||||||
py={16}
|
<FileToolbar />
|
||||||
></Stack>
|
<FileList />
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
6
src/components/ComicView.tsx
Normal file
6
src/components/ComicView.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { Box } from '@mantine/core';
|
||||||
|
import { FC } from 'react';
|
||||||
|
|
||||||
|
export const ComicView: FC = () => {
|
||||||
|
return <Box w="100%" h="100%" sx={{ overflow: 'hidden' }}></Box>;
|
||||||
|
};
|
23
src/components/FileList.tsx
Normal file
23
src/components/FileList.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Box, Center, Text } from '@mantine/core';
|
||||||
|
import { isEmpty, map, pipe, sort } from 'ramda';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { useFileListStore } from '../states/files';
|
||||||
|
|
||||||
|
export const FileList: FC = () => {
|
||||||
|
const files = useFileListStore.use.files();
|
||||||
|
console.log('[debug]files from store: ', files);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box w="100%" h="100%" pl={4} sx={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }}>
|
||||||
|
{pipe(
|
||||||
|
sort((fa, fb) => fa.sort - fb.sort),
|
||||||
|
map(item => <div key={item.filename}>{item.filename}</div>)
|
||||||
|
)(files)}
|
||||||
|
{isEmpty(files) && (
|
||||||
|
<Center h="100%">
|
||||||
|
<Text size="xs">请先打开一个文件夹。</Text>
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
42
src/components/FileTools.tsx
Normal file
42
src/components/FileTools.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { Button, Group, Tooltip } from '@mantine/core';
|
||||||
|
import { notifications } from '@mantine/notifications';
|
||||||
|
import { IconFolder } from '@tabler/icons-react';
|
||||||
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
import { open } from '@tauri-apps/api/dialog';
|
||||||
|
import { FC, useCallback } from 'react';
|
||||||
|
import { useFileListStore } from '../states/files';
|
||||||
|
|
||||||
|
export const FileToolbar: FC = () => {
|
||||||
|
const storeFiles = useFileListStore.use.updateFiles();
|
||||||
|
const handleOpenAction = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const directory = await open({
|
||||||
|
title: '打开要浏览的漫画所在的文件夹',
|
||||||
|
directory: true,
|
||||||
|
multiple: false
|
||||||
|
});
|
||||||
|
const files = await invoke('scan_directory', { target: directory });
|
||||||
|
console.log('[debug]file list: ', files);
|
||||||
|
storeFiles(files);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[error]打开文件夹', e);
|
||||||
|
notifications.show({ title: '未能成功打开指定文件夹,请重试。', color: 'red' });
|
||||||
|
}
|
||||||
|
}, [storeFiles]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group align="start" w="100%" spacing={4}>
|
||||||
|
<Tooltip label="打开漫画所在文件夹">
|
||||||
|
<Button
|
||||||
|
size="xs"
|
||||||
|
variant="subtle"
|
||||||
|
fullWidth
|
||||||
|
leftIcon={<IconFolder stroke={1.5} size={14} />}
|
||||||
|
onClick={handleOpenAction}
|
||||||
|
>
|
||||||
|
打开文件夹
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
61
src/components/PicToolbar.tsx
Normal file
61
src/components/PicToolbar.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { ActionIcon, Group, NumberInput, rem, SegmentedControl, Tooltip } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
IconArrowAutofitWidth,
|
||||||
|
IconLock,
|
||||||
|
IconPercentage,
|
||||||
|
IconZoomIn,
|
||||||
|
IconZoomOut
|
||||||
|
} from '@tabler/icons-react';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { useZoomState } from '../states/zoom';
|
||||||
|
|
||||||
|
export const PicToolbar: FC = () => {
|
||||||
|
const { lock, autoFit, currentZoom, viewMode } = useZoomState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group w="100%" position="right" spacing={8} px={4} py={4}>
|
||||||
|
<Tooltip label="锁定缩放">
|
||||||
|
<ActionIcon variant={lock ? 'filled' : 'subtle'} color="grape">
|
||||||
|
<IconLock stroke={1.5} size={24} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="适应窗口宽度">
|
||||||
|
<ActionIcon variant={autoFit ? 'filled' : 'subtle'} color="grape">
|
||||||
|
<IconArrowAutofitWidth stroke={1.5} size={24} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="缩小">
|
||||||
|
<ActionIcon variant="subtle" color="grape">
|
||||||
|
<IconZoomOut stroke={1.5} size={24} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<NumberInput
|
||||||
|
hideControls
|
||||||
|
size="xs"
|
||||||
|
min={20}
|
||||||
|
max={100}
|
||||||
|
step={5}
|
||||||
|
value={currentZoom}
|
||||||
|
styles={{ input: { width: rem(58), textAlign: 'center' } }}
|
||||||
|
rightSection={<IconPercentage stroke={1.5} size={16} />}
|
||||||
|
/>
|
||||||
|
<Tooltip label="放大">
|
||||||
|
<ActionIcon variant="subtle" color="grape">
|
||||||
|
<IconZoomIn stroke={1.5} size={24} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="翻页模式">
|
||||||
|
<SegmentedControl
|
||||||
|
size="xs"
|
||||||
|
value={viewMode}
|
||||||
|
color="grape"
|
||||||
|
data={[
|
||||||
|
{ label: '单页', value: 'single' },
|
||||||
|
{ label: '双页', value: 'double' },
|
||||||
|
{ label: '连续', value: 'continuation' }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
5
src/models.ts
Normal file
5
src/models.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export type FileItem = {
|
||||||
|
sort: number;
|
||||||
|
filename: string;
|
||||||
|
path: string;
|
||||||
|
};
|
28
src/states/files.ts
Normal file
28
src/states/files.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { addIndex, map, mergeRight } from 'ramda';
|
||||||
|
import { FileItem } from '../models';
|
||||||
|
import { SyncObjectCallback } from '../types';
|
||||||
|
import { createStoreHook } from '../utils/store_creator';
|
||||||
|
|
||||||
|
interface FileListState {
|
||||||
|
files: FileItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileListActions = {
|
||||||
|
updateFiles: SyncObjectCallback<Omit<FileItem, 'sort'>[]>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: FileListState = {
|
||||||
|
files: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFileListStore = createStoreHook<FileListState & FileListActions>(set => ({
|
||||||
|
...initialState,
|
||||||
|
updateFiles(files) {
|
||||||
|
set(df => {
|
||||||
|
df.files = addIndex<Omit<FileItem, 'sort'>, FileItem>(map)(
|
||||||
|
(item, index) => mergeRight({ sort: index * 10 }, item),
|
||||||
|
files
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
19
src/states/zoom.ts
Normal file
19
src/states/zoom.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { createStoreHook } from '../utils/store_creator';
|
||||||
|
|
||||||
|
interface ZoomState {
|
||||||
|
lock: boolean;
|
||||||
|
autoFit: boolean;
|
||||||
|
currentZoom: number;
|
||||||
|
viewMode: 'single' | 'double' | 'continuation';
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: ZoomState = {
|
||||||
|
lock: true,
|
||||||
|
autoFit: false,
|
||||||
|
currentZoom: 100,
|
||||||
|
viewMode: 'continuation'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useZoomState = createStoreHook<ZoomState>(set => ({
|
||||||
|
...initialState
|
||||||
|
}));
|
Loading…
Reference in New Issue
Block a user