feat: Add search bar for formats
This commit is contained in:
parent
7d2af46b0b
commit
53fff594fc
4 changed files with 290 additions and 43 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
214
src/index.tsx
214
src/index.tsx
|
|
@ -1,12 +1,12 @@
|
||||||
import { Database } from "bun:sqlite";
|
|
||||||
import { randomUUID } from "node:crypto";
|
|
||||||
import { rmSync } from "node:fs";
|
|
||||||
import { mkdir, unlink } from "node:fs/promises";
|
|
||||||
import cookie from "@elysiajs/cookie";
|
import cookie from "@elysiajs/cookie";
|
||||||
import { html } from "@elysiajs/html";
|
import { html } from "@elysiajs/html";
|
||||||
import { jwt } from "@elysiajs/jwt";
|
import { jwt } from "@elysiajs/jwt";
|
||||||
import { staticPlugin } from "@elysiajs/static";
|
import { staticPlugin } from "@elysiajs/static";
|
||||||
|
import { Database } from "bun:sqlite";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { rmSync } from "node:fs";
|
||||||
|
import { mkdir, unlink } from "node:fs/promises";
|
||||||
import { BaseHtml } from "./components/base";
|
import { BaseHtml } from "./components/base";
|
||||||
import { Header } from "./components/header";
|
import { Header } from "./components/header";
|
||||||
import {
|
import {
|
||||||
|
|
@ -473,27 +473,101 @@ const app = new Elysia({
|
||||||
))}
|
))}
|
||||||
</select> */}
|
</select> */}
|
||||||
</article>
|
</article>
|
||||||
<form method="post" action="/convert">
|
<form
|
||||||
|
method="post"
|
||||||
|
action="/convert"
|
||||||
|
style={{ position: "relative" }}>
|
||||||
<input type="hidden" name="file_names" id="file_names" />
|
<input type="hidden" name="file_names" id="file_names" />
|
||||||
<article>
|
<article>
|
||||||
<select name="convert_to" aria-label="Convert to" required>
|
<input
|
||||||
<option selected disabled value="">
|
type="search"
|
||||||
Convert to
|
name="convert_to_search"
|
||||||
</option>
|
placeholder="Search for conversions"
|
||||||
{Object.entries(getAllTargets()).map(
|
autocomplete="off"
|
||||||
([converter, targets]) => (
|
/>
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
|
||||||
<optgroup label={converter}>
|
<div class="select_container">
|
||||||
{targets.map((target) => (
|
<article
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
class="convert_to_popup"
|
||||||
<option value={`${target},${converter}`} safe>
|
hidden
|
||||||
{target}
|
style={{
|
||||||
</option>
|
flexDirection: "column",
|
||||||
))}
|
display: "flex",
|
||||||
</optgroup>
|
zIndex: 2,
|
||||||
),
|
position: "absolute",
|
||||||
)}
|
maxHeight: "50vh",
|
||||||
</select>
|
width: "90vw",
|
||||||
|
overflowY: "scroll",
|
||||||
|
margin: "0px",
|
||||||
|
overflowX: "hidden",
|
||||||
|
}}>
|
||||||
|
{Object.entries(getAllTargets()).map(
|
||||||
|
([converter, targets]) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<article
|
||||||
|
class="convert_to_group"
|
||||||
|
data-converter={converter}
|
||||||
|
style={{
|
||||||
|
borderColor: "gray",
|
||||||
|
padding: "2px",
|
||||||
|
}}>
|
||||||
|
<header
|
||||||
|
style={{ fontSize: "20px", fontWeight: "bold" }}>
|
||||||
|
{converter}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="convert_to_target"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: "5px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
}}>
|
||||||
|
{targets.map((target) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<button
|
||||||
|
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
||||||
|
tabindex={0}
|
||||||
|
class="target"
|
||||||
|
data-value={`${target},${converter}`}
|
||||||
|
data-target={target}
|
||||||
|
data-converter={converter}
|
||||||
|
style={{ fontSize: "15px", padding: "5px" }}
|
||||||
|
type="button">
|
||||||
|
{target}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{/* Hidden element which determines the format to convert the file too and the converter to use */}
|
||||||
|
<select
|
||||||
|
name="convert_to"
|
||||||
|
aria-label="Convert to"
|
||||||
|
required
|
||||||
|
hidden>
|
||||||
|
<option selected disabled value="">
|
||||||
|
Convert to
|
||||||
|
</option>
|
||||||
|
{Object.entries(getAllTargets()).map(
|
||||||
|
([converter, targets]) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<optgroup label={converter}>
|
||||||
|
{targets.map((target) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<option value={`${target},${converter}`} safe>
|
||||||
|
{target}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<input type="submit" value="Convert" />
|
<input type="submit" value="Convert" />
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -507,24 +581,82 @@ const app = new Elysia({
|
||||||
"/conversions",
|
"/conversions",
|
||||||
({ body }) => {
|
({ body }) => {
|
||||||
return (
|
return (
|
||||||
<select name="convert_to" aria-label="Convert to" required>
|
<>
|
||||||
<option selected disabled value="">
|
<article
|
||||||
Convert to
|
class="convert_to_popup"
|
||||||
</option>
|
hidden
|
||||||
{Object.entries(getPossibleTargets(body.fileType)).map(
|
style={{
|
||||||
([converter, targets]) => (
|
flexDirection: "column",
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
display: "flex",
|
||||||
<optgroup label={converter}>
|
zIndex: 2,
|
||||||
{targets.map((target) => (
|
position: "absolute",
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
maxHeight: "50vh",
|
||||||
<option value={`${target},${converter}`} safe>
|
width: "90vw",
|
||||||
{target}
|
overflowY: "scroll",
|
||||||
</option>
|
margin: "0px",
|
||||||
))}
|
overflowX: "hidden",
|
||||||
</optgroup>
|
}}>
|
||||||
),
|
{Object.entries(getPossibleTargets(body.fileType)).map(
|
||||||
)}
|
([converter, targets]) => (
|
||||||
</select>
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<article
|
||||||
|
class="convert_to_group"
|
||||||
|
data-converter={converter}
|
||||||
|
style={{
|
||||||
|
borderColor: "gray",
|
||||||
|
padding: "2px",
|
||||||
|
}}>
|
||||||
|
<header style={{ fontSize: "20px", fontWeight: "bold" }}>
|
||||||
|
{converter}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="convert_to_target"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: "5px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
}}>
|
||||||
|
{targets.map((target) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<button
|
||||||
|
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
||||||
|
tabindex={0}
|
||||||
|
class="target"
|
||||||
|
data-value={`${target},${converter}`}
|
||||||
|
data-target={target}
|
||||||
|
data-converter={converter}
|
||||||
|
style={{ fontSize: "15px", padding: "5px" }}
|
||||||
|
type="button">
|
||||||
|
{target}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<select name="convert_to" aria-label="Convert to" required hidden>
|
||||||
|
<option selected disabled value="">
|
||||||
|
Convert to
|
||||||
|
</option>
|
||||||
|
{Object.entries(getPossibleTargets(body.fileType)).map(
|
||||||
|
([converter, targets]) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<optgroup label={converter}>
|
||||||
|
{targets.map((target) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<option value={`${target},${converter}`} safe>
|
||||||
|
{target}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
{ body: t.Object({ fileType: t.String() }) },
|
{ body: t.Object({ fileType: t.String() }) },
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,75 @@ const fileInput = document.querySelector('input[type="file"]');
|
||||||
const fileNames = [];
|
const fileNames = [];
|
||||||
let fileType;
|
let fileType;
|
||||||
|
|
||||||
const selectContainer = document.querySelector("form > article");
|
const selectContainer = document.querySelector("form .select_container");
|
||||||
|
|
||||||
|
const updateSearchBar = () => {
|
||||||
|
const convertToInput = document.querySelector(
|
||||||
|
"input[name='convert_to_search']",
|
||||||
|
);
|
||||||
|
const convertToPopup = document.querySelector(".convert_to_popup");
|
||||||
|
const convertToGroupElements = document.querySelectorAll(".convert_to_group");
|
||||||
|
const convertToGroups = {};
|
||||||
|
const convertToElement = document.querySelector("select[name='convert_to']");
|
||||||
|
|
||||||
|
const showMatching = (search) => {
|
||||||
|
for (const [targets, groupElement] of Object.values(convertToGroups)) {
|
||||||
|
let matchingTargetsFound = 0;
|
||||||
|
for (const target of targets) {
|
||||||
|
if (target.dataset.target.includes(search)) {
|
||||||
|
matchingTargetsFound++;
|
||||||
|
target.hidden = false;
|
||||||
|
} else {
|
||||||
|
target.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingTargetsFound === 0) {
|
||||||
|
groupElement.hidden = true;
|
||||||
|
} else {
|
||||||
|
groupElement.hidden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const groupElement of convertToGroupElements) {
|
||||||
|
const groupName = groupElement.dataset.converter;
|
||||||
|
|
||||||
|
const targetElements = groupElement.querySelectorAll(".target");
|
||||||
|
const targets = Array.from(targetElements);
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
target.onclick = () => {
|
||||||
|
convertToElement.value = target.dataset.value;
|
||||||
|
convertToInput.value = `${target.dataset.target} using ${target.dataset.converter}`;
|
||||||
|
showMatching("");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToGroups[groupName] = [targets, groupElement];
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToInput.addEventListener("input", (e) => {
|
||||||
|
showMatching(e.target.value.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
convertToInput.addEventListener("blur", (e) => {
|
||||||
|
// Keep the popup open even when clicking on a target button
|
||||||
|
// for a split second to allow the click to go through
|
||||||
|
if (e?.relatedTarget?.classList?.contains("target")) {
|
||||||
|
setTimeout(() => {
|
||||||
|
convertToPopup.hidden = true;
|
||||||
|
}, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToPopup.hidden = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
convertToInput.addEventListener("focus", () => {
|
||||||
|
convertToPopup.hidden = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// const convertFromSelect = document.querySelector("select[name='convert_from']");
|
// const convertFromSelect = document.querySelector("select[name='convert_from']");
|
||||||
|
|
||||||
|
|
@ -49,6 +117,7 @@ fileInput.addEventListener("change", (e) => {
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
.then((html) => {
|
.then((html) => {
|
||||||
selectContainer.innerHTML = html;
|
selectContainer.innerHTML = html;
|
||||||
|
updateSearchBar();
|
||||||
})
|
})
|
||||||
.catch((err) => console.log(err));
|
.catch((err) => console.log(err));
|
||||||
}
|
}
|
||||||
|
|
@ -123,3 +192,5 @@ formConvert.addEventListener("submit", (e) => {
|
||||||
const hiddenInput = document.querySelector("input[name='file_names']");
|
const hiddenInput = document.querySelector("input[name='file_names']");
|
||||||
hiddenInput.value = JSON.stringify(fileNames);
|
hiddenInput.value = JSON.stringify(fileNames);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateSearchBar();
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,47 @@ div.center {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 99999999999px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
width: 50vw !important;
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 850px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
width: 60vw !important;
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 575px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
width: 80vw !important;
|
||||||
|
height: 75vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 1000px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
height: 40vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-height: 650px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
height: 30vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 500px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
height: 25vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 400px) {
|
||||||
|
.convert_to_popup {
|
||||||
|
height: 15vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue