refactor: commands from menu

This commit is contained in:
2023-08-04 13:48:12 +02:00
parent b95cfcc5b4
commit f9638c35fc
12 changed files with 317 additions and 114 deletions

View File

@ -37,6 +37,7 @@ export const KMenu = (
const loadingText = useSignal("");
const activeIndex = useSignal(-1);
const containerRef = useRef<HTMLDivElement>(null);
const input = useRef<HTMLInputElement>(null);
const commandInput = useSignal("");
@ -83,6 +84,23 @@ export const KMenu = (
}, context);
}
const container = containerRef.current;
if (container) {
const selectedItem = container.children[activeIndex.value];
if (selectedItem) {
const itemPosition = selectedItem.getBoundingClientRect();
const containerPosition = container.getBoundingClientRect();
// Check if the selected item is above the visible area
if (itemPosition.top < containerPosition.top) {
container.scrollTop -= containerPosition.top - itemPosition.top;
} // Check if the selected item is below the visible area
else if (itemPosition.bottom > containerPosition.bottom) {
container.scrollTop += itemPosition.bottom - containerPosition.bottom;
}
}
}
useEventListener("keydown", (ev: KeyboardEvent) => {
if (ev.key === "/" && ev.ctrlKey) {
visible.value = !visible.value;
@ -140,7 +158,7 @@ export const KMenu = (
} border-gray-500 `}
style={{
gridTemplateColumns: activeState.value !== "loading"
? "4em 1fr"
? "auto 1fr"
: "1fr",
}}
>
@ -148,7 +166,7 @@ export const KMenu = (
(
<>
<div class="grid place-items-center border-r border-gray-500">
<span class="text-white">
<span class="text-white mx-4">
{activeMenu.title}
</span>
</div>
@ -172,7 +190,11 @@ export const KMenu = (
</div>
{activeState.value === "normal" &&
(
<div class="" style={{ maxHeight: "12rem", overflowY: "auto" }}>
<div
class=""
style={{ maxHeight: "12rem", overflowY: "auto" }}
ref={containerRef}
>
{entries?.length === 0 && (
<div class="text-gray-400 px-4 py-2">
No Entries

View File

@ -1,56 +1,12 @@
import { Menu } from "@islands/KMenu/types.ts";
import { Movie } from "@lib/resource/movies.ts";
import { TMDBMovie } from "@lib/types.ts";
import { fetchStream, isValidUrl } from "@lib/helpers.ts";
import { addMovieInfos } from "@islands/KMenu/commands/add_movie_infos.ts";
import { createNewMovie } from "@islands/KMenu/commands/create_movie.ts";
import { createNewArticle } from "@islands/KMenu/commands/create_article.ts";
export const menus: Record<string, Menu> = {
main: {
title: "Run",
entries: [
{
title: "Close menu",
meta: "",
cb: (state) => {
state.visible.value = false;
},
visible: () => false,
},
{
title: "Create new article",
meta: "",
icon: "IconSquareRoundedPlus",
cb: (state) => {
state.menus["input_link"] = {
title: "Link:",
entries: [],
};
state.activeMenu.value = "input_link";
state.activeState.value = "input";
const unsub = state.commandInput.subscribe((value) => {
if (isValidUrl(value)) {
unsub();
state.activeState.value = "loading";
fetchStream("/api/articles/create?url=" + value, (chunk) => {
console.log({ chunk: chunk.split("\n") });
if (chunk.startsWith("id:")) {
state.loadingText.value = "Finished";
setTimeout(() => {
window.location.href = "/articles/" +
chunk.replace("id:", "").trim();
}, 500);
} else {
state.loadingText.value = chunk;
}
});
}
});
},
visible: () => true,
},
{
title: "Clear Cache",
icon: "IconRefresh",
@ -63,58 +19,9 @@ export const menus: Record<string, Menu> = {
state.visible.value = false;
},
},
{
title: "Add Movie infos",
meta: "",
icon: "IconReportSearch",
cb: async (state, context) => {
state.activeState.value = "loading";
const movie = context as Movie;
const query = movie.name;
const response = await fetch(
`/api/tmdb/query?q=${encodeURIComponent(query)}`,
);
const json = await response.json() as TMDBMovie[];
const menuID = `result/${movie.name}`;
state.menus[menuID] = {
title: "Select",
entries: json.map((m) => ({
title: `${m.title} released ${m.release_date}`,
cb: async () => {
state.activeState.value = "loading";
await fetch(`/api/movies/enhance/${movie.name}/`, {
method: "POST",
body: JSON.stringify({ tmdbId: m.id }),
});
state.visible.value = false;
state.activeState.value = "normal";
window.location.reload();
},
})),
};
state.activeMenu.value = menuID;
state.activeState.value = "normal";
},
visible: () => {
const loc = globalThis["location"];
return loc?.pathname?.includes("movie");
},
},
{
title: "Reload Page",
meta: "",
cb: () => {
window.location.reload();
},
visible: () => false,
},
createNewArticle,
createNewMovie,
addMovieInfos,
],
},
};

View File

@ -0,0 +1,48 @@
import { MenuEntry } from "@islands/KMenu/types.ts";
import { Movie } from "@lib/resource/movies.ts";
import { TMDBMovie } from "@lib/types.ts";
export const addMovieInfos: MenuEntry = {
title: "Add Movie infos",
meta: "",
icon: "IconReportSearch",
cb: async (state, context) => {
state.activeState.value = "loading";
const movie = context as Movie;
const query = movie.name;
const response = await fetch(
`/api/tmdb/query?q=${encodeURIComponent(query)}`,
);
const json = await response.json() as TMDBMovie[];
const menuID = `result/${movie.name}`;
state.menus[menuID] = {
title: "Select",
entries: json.map((m) => ({
title: `${m.title} released ${m.release_date}`,
cb: async () => {
state.activeState.value = "loading";
await fetch(`/api/movies/enhance/${movie.name}/`, {
method: "POST",
body: JSON.stringify({ tmdbId: m.id }),
});
state.visible.value = false;
state.activeState.value = "normal";
window.location.reload();
},
})),
};
state.activeMenu.value = menuID;
state.activeState.value = "normal";
},
visible: () => {
const loc = globalThis["location"];
return loc?.pathname?.includes("movie");
},
};

View File

@ -0,0 +1,39 @@
import { MenuEntry } from "@islands/KMenu/types.ts";
import { fetchStream, isValidUrl } from "@lib/helpers.ts";
export const createNewArticle: MenuEntry = {
title: "Create new article",
meta: "",
icon: "IconSquareRoundedPlus",
cb: (state) => {
state.menus["input_link"] = {
title: "Link:",
entries: [],
};
state.activeMenu.value = "input_link";
state.activeState.value = "input";
const unsub = state.commandInput.subscribe((value) => {
if (isValidUrl(value)) {
unsub();
state.activeState.value = "loading";
fetchStream("/api/articles/create?url=" + value, (chunk) => {
console.log({ chunk: chunk.split("\n") });
if (chunk.startsWith("id:")) {
state.loadingText.value = "Finished";
setTimeout(() => {
window.location.href = "/articles/" +
chunk.replace("id:", "").trim();
}, 500);
} else {
state.loadingText.value = chunk;
}
});
}
});
},
visible: () => true,
};

View File

@ -0,0 +1,72 @@
import { MenuEntry } from "@islands/KMenu/types.ts";
import { TMDBMovie } from "@lib/types.ts";
import { debounce } from "@lib/helpers.ts";
import { Movie } from "@lib/resource/movies.ts";
export const createNewMovie: MenuEntry = {
title: "Create new movie",
meta: "",
icon: "IconSquareRoundedPlus",
cb: (state) => {
state.menus["input_link"] = {
title: "Search",
entries: [],
};
state.menus["loading"] = {
title: "Search",
entries: [
{
title: "Loading",
icon: "IconLoader2",
cb() {
},
},
],
};
state.activeMenu.value = "input_link";
state.activeState.value = "normal";
let currentQuery: string;
const search = debounce(async function search(query: string) {
currentQuery = query;
console.log({ query });
if (query.length < 2) {
return;
}
const response = await fetch("/api/tmdb/query?q=" + query);
const movies = await response.json() as TMDBMovie[];
console.log({ query, currentQuery, movies });
if (query !== currentQuery) return;
state.menus["input_link"] = {
title: "Search",
entries: movies.map((r) => {
return {
title: `${r.title} - ${r.release_date}`,
cb: async () => {
state.activeState.value = "loading";
const response = await fetch("/api/movies/" + r.id, {
method: "POST",
});
const movie = await response.json() as Movie;
window.location.href = "/movies/" + movie.name;
},
};
}),
};
state.activeMenu.value = "input_link";
}, 500);
const unsub = state.commandInput.subscribe((value) => {
state.activeMenu.value = "loading";
search(value);
});
},
visible: () => true,
};