Compare commits
No commits in common. "4cbdc7c142ddf14f7ee18f71655e61e4766b746e" and "0a830d5189dc88101deceb3bbb966d5de64e88d5" have entirely different histories.
4cbdc7c142
...
0a830d5189
|
@ -1,2 +0,0 @@
|
||||||
node_modules
|
|
||||||
src-tauri
|
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
useTabs: false,
|
|
||||||
tabWidth: 2,
|
|
||||||
singleQuote: false,
|
|
||||||
trailingComma: "all",
|
|
||||||
printWidth: 100,
|
|
||||||
}
|
|
4481
package-lock.json
generated
4481
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,13 +10,7 @@
|
||||||
"tauri": "tauri"
|
"tauri": "tauri"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/react": "^2.7.0",
|
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@tauri-apps/api": "^1.3.0",
|
"@tauri-apps/api": "^1.3.0",
|
||||||
"classnames": "^2.3.2",
|
|
||||||
"flowbite": "^1.6.5",
|
|
||||||
"framer-motion": "^10.12.16",
|
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router": "^6.11.2",
|
"react-router": "^6.11.2",
|
||||||
|
@ -29,10 +23,7 @@
|
||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@vitejs/plugin-react": "^3.0.0",
|
"@vitejs/plugin-react": "^3.0.0",
|
||||||
"autoprefixer": "^10.4.14",
|
|
||||||
"postcss": "^8.4.24",
|
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.62.1",
|
||||||
"tailwindcss": "^3.3.2",
|
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.2.1"
|
"vite": "^4.2.1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
export default {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
};
|
|
36
src-tauri/Cargo.lock
generated
36
src-tauri/Cargo.lock
generated
|
@ -1656,46 +1656,12 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libappindicator"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8"
|
|
||||||
dependencies = [
|
|
||||||
"glib",
|
|
||||||
"gtk",
|
|
||||||
"gtk-sys",
|
|
||||||
"libappindicator-sys",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libappindicator-sys"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa"
|
|
||||||
dependencies = [
|
|
||||||
"gtk-sys",
|
|
||||||
"libloading",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.145"
|
version = "0.2.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81"
|
checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libloading"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.24.2"
|
version = "0.24.2"
|
||||||
|
@ -3082,7 +3048,6 @@ dependencies = [
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"dirs-next",
|
|
||||||
"dispatch",
|
"dispatch",
|
||||||
"gdk",
|
"gdk",
|
||||||
"gdk-pixbuf",
|
"gdk-pixbuf",
|
||||||
|
@ -3097,7 +3062,6 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
"jni",
|
"jni",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libappindicator",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk",
|
"ndk",
|
||||||
|
|
|
@ -14,7 +14,7 @@ members = ["database-maker"]
|
||||||
tauri-build = { version = "1.3", features = [] }
|
tauri-build = { version = "1.3", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1.3", features = ["shell-open", "system-tray"] }
|
tauri = { version = "1.3", features = ["shell-open"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use sqlx::{sqlite::SqliteRow, Row, SqlitePool};
|
use sqlx::{Row, SqlitePool};
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
pub struct KanjiDb(pub SqlitePool);
|
pub struct KanjiDb(pub SqlitePool);
|
||||||
|
@ -6,86 +6,42 @@ pub struct KanjiDb(pub SqlitePool);
|
||||||
#[derive(Debug, Derivative, Serialize, Deserialize)]
|
#[derive(Debug, Derivative, Serialize, Deserialize)]
|
||||||
#[derivative(Default)]
|
#[derivative(Default)]
|
||||||
pub struct GetKanjiOptions {
|
pub struct GetKanjiOptions {
|
||||||
#[serde(default)]
|
|
||||||
#[derivative(Default(value = "10"))]
|
#[derivative(Default(value = "10"))]
|
||||||
how_many: u32,
|
how_many: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Kanji {
|
|
||||||
character: String,
|
|
||||||
meaning: String,
|
|
||||||
most_used_rank: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct GetKanjiResult {
|
pub struct GetKanjiResult {
|
||||||
count: u32,
|
count: u32,
|
||||||
kanji: Vec<Kanji>,
|
kanji: Vec<String>,
|
||||||
}
|
|
||||||
|
|
||||||
fn build_kanji(row: SqliteRow) -> Kanji {
|
|
||||||
let character = row.get("Character");
|
|
||||||
let meaning = row.get("Meaning");
|
|
||||||
let most_used_rank = row.get("MostUsedRank");
|
|
||||||
Kanji {
|
|
||||||
character,
|
|
||||||
meaning,
|
|
||||||
most_used_rank,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_kanji(
|
pub async fn get_kanji(
|
||||||
db: State<'_, KanjiDb>,
|
state: State<'_, KanjiDb>,
|
||||||
options: Option<GetKanjiOptions>,
|
options: Option<GetKanjiOptions>,
|
||||||
) -> Result<GetKanjiResult, ()> {
|
) -> Result<GetKanjiResult, ()> {
|
||||||
let opts = options.unwrap_or_default();
|
let opts = options.unwrap_or_default();
|
||||||
|
|
||||||
let result = sqlx::query(
|
let result = sqlx::query(
|
||||||
r#"
|
r#"SELECT * FROM KanjiSet
|
||||||
SELECT * FROM KanjiSet
|
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
||||||
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
WHERE MostUsedRank IS NOT NULL
|
||||||
GROUP BY KanjiSet.ID
|
ORDER BY MostUsedRank
|
||||||
HAVING MostUsedRank IS NOT NULL
|
LIMIT ?"#,
|
||||||
ORDER BY MostUsedRank
|
|
||||||
LIMIT ?
|
|
||||||
"#,
|
|
||||||
)
|
)
|
||||||
.bind(opts.how_many)
|
.bind(opts.how_many)
|
||||||
.fetch_all(&db.0)
|
.fetch_all(&state.0)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())?;
|
.map_err(|_| ())?;
|
||||||
|
|
||||||
let kanji = result.into_iter().map(build_kanji).collect();
|
let kanji = result.into_iter().map(|row| row.get("Character")).collect();
|
||||||
|
|
||||||
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
|
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
|
||||||
.fetch_one(&db.0)
|
.fetch_one(&state.0)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ())?;
|
.map_err(|_| ())?;
|
||||||
let count = count.try_get(0).map_err(|_| ())?;
|
let count = count.try_get(0).map_err(|_| ())?;
|
||||||
|
|
||||||
Ok(GetKanjiResult { kanji, count })
|
Ok(GetKanjiResult { kanji, count })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_single_kanji(
|
|
||||||
db: State<'_, KanjiDb>,
|
|
||||||
character: String,
|
|
||||||
) -> Result<Option<Kanji>, ()> {
|
|
||||||
let result = sqlx::query(
|
|
||||||
r#"
|
|
||||||
SELECT * FROM KanjiSet
|
|
||||||
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
|
|
||||||
GROUP BY KanjiSet.ID
|
|
||||||
HAVING MostUsedRank IS NOT NULL
|
|
||||||
AND Kanji.Character = ?
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(character)
|
|
||||||
.fetch_one(&db.0)
|
|
||||||
.await
|
|
||||||
.map_err(|_| ())?;
|
|
||||||
|
|
||||||
Ok(Some(build_kanji(result)))
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ use sqlx::{
|
||||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
||||||
SqlitePool,
|
SqlitePool,
|
||||||
};
|
};
|
||||||
use tauri::SystemTray;
|
|
||||||
|
|
||||||
use crate::kanji::KanjiDb;
|
use crate::kanji::KanjiDb;
|
||||||
|
|
||||||
|
@ -27,25 +26,15 @@ fn greet(name: &str) -> String {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// Open kanji db
|
|
||||||
let kanji_db_options =
|
let kanji_db_options =
|
||||||
SqliteConnectOptions::from_str("./KanjiDatabase.sqlite")?.read_only(true);
|
SqliteConnectOptions::from_str("./KanjiDatabase.sqlite")?.read_only(true);
|
||||||
let kanji_pool = SqlitePoolOptions::new()
|
let kanji_pool = SqlitePoolOptions::new()
|
||||||
.connect_with(kanji_db_options)
|
.connect_with(kanji_db_options)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// System tray
|
|
||||||
let tray = SystemTray::new();
|
|
||||||
|
|
||||||
// Build tauri
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.manage(KanjiDb(kanji_pool))
|
.manage(KanjiDb(kanji_pool))
|
||||||
.system_tray(tray)
|
.invoke_handler(tauri::generate_handler![greet, kanji::get_kanji])
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
greet,
|
|
||||||
kanji::get_kanji,
|
|
||||||
kanji::get_single_kanji
|
|
||||||
])
|
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.context("error while running tauri application")?;
|
.context("error while running tauri application")?;
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,6 @@
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null
|
||||||
},
|
},
|
||||||
"systemTray": {
|
|
||||||
"iconPath": "icons/icon.ico",
|
|
||||||
"iconAsTemplate": true
|
|
||||||
},
|
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
|
@ -47,4 +43,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
main.main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
li {
|
||||||
flex-grow: 1;
|
padding-left: 0;
|
||||||
display: inline-block;
|
}
|
||||||
padding: 12px;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.link-active {
|
|
||||||
background-color: skyblue;
|
|
||||||
}
|
|
61
src/App.tsx
61
src/App.tsx
|
@ -1,39 +1,23 @@
|
||||||
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
|
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
|
||||||
|
import styles from "./App.module.scss";
|
||||||
import KanjiPane from "./panes/KanjiPane";
|
import KanjiPane from "./panes/KanjiPane";
|
||||||
import classNames from "classnames";
|
|
||||||
import { ChakraProvider } from "@chakra-ui/react";
|
|
||||||
import HomePane from "./panes/HomePane";
|
import HomePane from "./panes/HomePane";
|
||||||
import { createBrowserRouter } from "react-router-dom";
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
import { Outlet, Route, createRoutesFromElements, matchPath, useLocation } from "react-router";
|
import { Outlet, Route, createRoutesFromElements } from "react-router";
|
||||||
import SrsPane from "./panes/SrsPane";
|
|
||||||
import VocabPane from "./panes/VocabPane";
|
|
||||||
import SettingsPane from "./panes/SettingsPane";
|
|
||||||
import { StrictMode } from "react";
|
|
||||||
|
|
||||||
import styles from "./App.module.scss";
|
|
||||||
|
|
||||||
function Layout() {
|
function Layout() {
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={styles.main}>
|
<>
|
||||||
<ul className={styles.header}>
|
<ul className={styles.header}>
|
||||||
{routes.map((route) => {
|
{routes.map((route) => (
|
||||||
if (!route.title) return undefined;
|
<li key={route.path}>
|
||||||
const active = matchPath({ path: route.path }, location.pathname);
|
<Link to={route.path}>{route.title}</Link>
|
||||||
const className = classNames(styles.link, active && styles["link-active"]);
|
</li>
|
||||||
return (
|
))}
|
||||||
<li key={route.path}>
|
|
||||||
<Link to={route.path} className={className}>
|
|
||||||
{route.title}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,26 +26,21 @@ export default function App() {
|
||||||
createRoutesFromElements(
|
createRoutesFromElements(
|
||||||
<Route path="/" element={<Layout />}>
|
<Route path="/" element={<Layout />}>
|
||||||
{routes.map((route, idx) => (
|
{routes.map((route, idx) => (
|
||||||
<Route key={route.path} index={idx === 0} path={route.path} element={route.element} />
|
<Route
|
||||||
|
key={route.path}
|
||||||
|
index={idx === 0}
|
||||||
|
path={route.path}
|
||||||
|
element={route.element}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Route>,
|
</Route>
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return <RouterProvider router={router} />;
|
||||||
<StrictMode>
|
|
||||||
<ChakraProvider>
|
|
||||||
<RouterProvider router={router} />
|
|
||||||
</ChakraProvider>
|
|
||||||
</StrictMode>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ key: "home", path: "/", title: "Home", element: <HomePane /> },
|
{ path: "/", title: "Home", element: <HomePane /> },
|
||||||
{ key: "srs", path: "/srs", title: "SRS", element: <SrsPane /> },
|
{ path: "/kanji", title: "Kanji", element: <KanjiPane /> },
|
||||||
{ key: "kanji", path: "/kanji", title: "Kanji", element: <KanjiPane /> },
|
|
||||||
{ key: "kanjiSelected", path: "/kanji/:selectedKanji", element: <KanjiPane /> },
|
|
||||||
{ key: "vocab", path: "/vocab", title: "Vocab", element: <VocabPane /> },
|
|
||||||
{ key: "settings", path: "/settings", title: "Settings", element: <SettingsPane /> },
|
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
$kanjiDisplaySize: 80px;
|
|
||||||
|
|
||||||
.display {
|
|
||||||
border: 1px solid rgb(87, 87, 210);
|
|
||||||
width: $kanjiDisplaySize;
|
|
||||||
height: $kanjiDisplaySize;
|
|
||||||
font-size: $kanjiDisplaySize * 0.8;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { invoke } from "@tauri-apps/api/tauri";
|
|
||||||
import { GetKanjiResult } from "../panes/KanjiPane";
|
|
||||||
import { Kanji } from "../types/Kanji";
|
|
||||||
import styles from "./KanjiDisplay.module.scss";
|
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
interface KanjiDisplayProps {
|
|
||||||
kanjiCharacter: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function KanjiDisplay({ kanjiCharacter }: KanjiDisplayProps) {
|
|
||||||
const { data, error, isLoading } = useSWR(["get_single_kanji", kanjiCharacter], ([command, character]) => invoke<GetKanjiResult>(command, { character }));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className={styles.display}>{kanjiCharacter}</div>
|
|
||||||
|
|
||||||
{JSON.stringify(isLoading)}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
data:
|
|
||||||
{JSON.stringify(data)}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
error:
|
|
||||||
{JSON.stringify(error)}
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
3
src/components/KanjiSearch.tsx
Normal file
3
src/components/KanjiSearch.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function KanjiSearch() {
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
export default function HomePane() {
|
export default function HomePane() {
|
||||||
return <>hellosu</>;
|
return <>
|
||||||
}
|
hellosu
|
||||||
|
</>
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
.kanji-list {
|
$kanjiDisplaySize: 80px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kanji-link {
|
.kanjiDisplay {
|
||||||
padding: 4px 8px;
|
border: 1px solid rgb(87, 87, 210);
|
||||||
border: 1px solid lightgray;
|
width: $kanjiDisplaySize;
|
||||||
}
|
height: $kanjiDisplaySize;
|
||||||
|
font-size: $kanjiDisplaySize * 0.8;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
|
@ -1,63 +1,36 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Kanji } from "../types/Kanji";
|
||||||
import { invoke } from "@tauri-apps/api/tauri";
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { Box, Grid, GridItem, LinkBox, Stack } from "@chakra-ui/layout";
|
|
||||||
|
|
||||||
import styles from "./KanjiPane.module.scss";
|
import styles from "./KanjiPane.module.scss";
|
||||||
import { Link, useParams } from "react-router-dom";
|
|
||||||
import KanjiDisplay from "../components/KanjiDisplay";
|
|
||||||
import { Kanji } from "../types/Kanji";
|
|
||||||
|
|
||||||
export interface GetKanjiResult {
|
interface GetKanjiResult {
|
||||||
count: number;
|
count: number;
|
||||||
kanji: Kanji[];
|
kanji: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface KanjiListProps {
|
function KanjiList({ data }: { data : GetKanjiResult}) {
|
||||||
data: GetKanjiResult;
|
return <>
|
||||||
selectedCharacter?: string;
|
Displaying {data.kanji.length} of {data.count} results.
|
||||||
}
|
|
||||||
|
|
||||||
function KanjiList({ data, selectedCharacter }: KanjiListProps) {
|
<ul>
|
||||||
return (
|
{data.kanji.map(kanji => <li key={kanji}>
|
||||||
<>
|
{kanji}
|
||||||
Displaying {data.kanji.length} of {data.count} results.
|
</li>)}
|
||||||
{data.kanji.map((kanji) => (
|
</ul>
|
||||||
<Link
|
</>
|
||||||
key={kanji.character}
|
|
||||||
className={styles["kanji-link"]}
|
|
||||||
to={`/kanji/${kanji.character}`}
|
|
||||||
>
|
|
||||||
<Grid templateRows="repeat(2, 1fr)" templateColumns="1fr 3fr">
|
|
||||||
<GridItem rowSpan={2} style={{ fontSize: "24px", textAlign: "center" }}>
|
|
||||||
{kanji.character}
|
|
||||||
</GridItem>
|
|
||||||
<GridItem>{kanji.meaning}</GridItem>
|
|
||||||
<GridItem>#{kanji.most_used_rank} most used</GridItem>
|
|
||||||
</Grid>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function KanjiPane() {
|
export default function KanjiPane() {
|
||||||
const { selectedKanji } = useParams();
|
|
||||||
const { data, error, isLoading } = useSWR("get_kanji", invoke<GetKanjiResult>);
|
const { data, error, isLoading } = useSWR("get_kanji", invoke<GetKanjiResult>);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{JSON.stringify(error)}
|
{JSON.stringify(error)}
|
||||||
{JSON.stringify(selectedKanji)}
|
|
||||||
|
|
||||||
<Stack spacing={7} direction="row">
|
<div className={styles.kanjiDisplay}></div>
|
||||||
<Box p={2} className={styles["kanji-list"]}>
|
|
||||||
{data && <KanjiList data={data} selectedCharacter={selectedKanji} />}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box p={5}>
|
{data && <KanjiList data={data} />}
|
||||||
{selectedKanji ? <KanjiDisplay kanjiCharacter={selectedKanji} /> : "nothing selected"}
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default function SettingsPane() {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
export default function SrsPane() {
|
export default function SrsPane() {
|
||||||
return <></>;
|
|
||||||
}
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
export default function VocabPane() {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
112
src/styles.css
112
src/styles.css
|
@ -1,3 +1,109 @@
|
||||||
@tailwind base;
|
:root {
|
||||||
@tailwind components;
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||||
@tailwind utilities;
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color: #0f0f0f;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 10vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 6em;
|
||||||
|
padding: 1.5em;
|
||||||
|
will-change: filter;
|
||||||
|
transition: 0.75s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo.tauri:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #24c8db);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
color: #0f0f0f;
|
||||||
|
background-color: #ffffff;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
border-color: #396cd8;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
border-color: #396cd8;
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#greet-input {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
color: #f6f6f6;
|
||||||
|
background-color: #2f2f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #24c8db;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #0f0f0f98;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
background-color: #0f0f0f69;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export interface Kanji {
|
export interface Kanji {
|
||||||
character: string;
|
character: string;
|
||||||
meaning: string;
|
translation: string;
|
||||||
most_used_rank: number;
|
}
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
export default {
|
|
||||||
content: ["./src/**/*.{html,js,jsx,ts,tsx}", "./node_modules/flowbite/**/*.js"],
|
|
||||||
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: [require("flowbite/plugin")],
|
|
||||||
};
|
|
Loading…
Reference in a new issue