mirror of
https://github.com/wowlikon/LiB.git
synced 2026-02-04 04:31:09 +00:00
Добавление авторизации и фронтэнда
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M0.877014 7.49988C0.877014 3.84219 3.84216 0.877045 7.49985 0.877045C11.1575 0.877045 14.1227 3.84219 14.1227 7.49988C14.1227 11.1575 11.1575 14.1227 7.49985 14.1227C3.84216 14.1227 0.877014 11.1575 0.877014 7.49988ZM7.49985 1.82704C4.36683 1.82704 1.82701 4.36686 1.82701 7.49988C1.82701 8.97196 2.38774 10.3131 3.30727 11.3213C4.19074 9.94119 5.73818 9.02499 7.50023 9.02499C9.26206 9.02499 10.8093 9.94097 11.6929 11.3208C12.6121 10.3127 13.1727 8.97172 13.1727 7.49988C13.1727 4.36686 10.6328 1.82704 7.49985 1.82704ZM10.9818 11.9787C10.2839 10.7795 8.9857 9.97499 7.50023 9.97499C6.01458 9.97499 4.71624 10.7797 4.01845 11.9791C4.97952 12.7272 6.18765 13.1727 7.49985 13.1727C8.81227 13.1727 10.0206 12.727 10.9818 11.9787ZM5.14999 6.50487C5.14999 5.207 6.20212 4.15487 7.49999 4.15487C8.79786 4.15487 9.84999 5.207 9.84999 6.50487C9.84999 7.80274 8.79786 8.85487 7.49999 8.85487C6.20212 8.85487 5.14999 7.80274 5.14999 6.50487ZM7.49999 5.10487C6.72679 5.10487 6.09999 5.73167 6.09999 6.50487C6.09999 7.27807 6.72679 7.90487 7.49999 7.90487C8.27319 7.90487 8.89999 7.27807 8.89999 6.50487C8.89999 5.73167 8.27319 5.10487 7.49999 5.10487Z"
|
||||
fill="#000000"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
@@ -0,0 +1,62 @@
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect
|
||||
x="10"
|
||||
y="10"
|
||||
width="80"
|
||||
height="80"
|
||||
rx="4"
|
||||
ry="4"
|
||||
fill="#fff"
|
||||
stroke="#000"
|
||||
stroke-width="2"
|
||||
/><rect x="20" y="15" width="60" height="70" rx="10" ry="10" /><rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="66"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="62" rx="10" ry="10" /><rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="60"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="56" rx="10" ry="10" /><rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="54"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="50" rx="10" ry="10" /><rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="48"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="44" rx="10" ry="10" /><rect
|
||||
x="22"
|
||||
y="21"
|
||||
width="2"
|
||||
height="58"
|
||||
rx="10"
|
||||
ry="10"
|
||||
stroke="#000"
|
||||
stroke-width="4"
|
||||
/><rect x="22" y="55" width="4" height="26" rx="2" ry="15" /><text
|
||||
x="50"
|
||||
y="40"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
alignment-baseline="middle"
|
||||
stroke="#fff"
|
||||
stroke-width=".5"
|
||||
fill="none"
|
||||
font-size="20"
|
||||
>『LiB』</text></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,59 @@
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="20" y="15" width="60" height="70" rx="10" ry="10" /><rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="66"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="62" rx="10" ry="10" />
|
||||
<rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="60"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="56" rx="10" ry="10" />
|
||||
<rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="54"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="50" rx="10" ry="10" />
|
||||
<rect
|
||||
x="20"
|
||||
y="15"
|
||||
width="60"
|
||||
height="48"
|
||||
rx="10"
|
||||
ry="10"
|
||||
fill="#fff"
|
||||
/><rect x="20" y="15" width="60" height="44" rx="10" ry="10" />
|
||||
<rect
|
||||
x="22"
|
||||
y="21"
|
||||
width="2"
|
||||
height="58"
|
||||
rx="10"
|
||||
ry="10"
|
||||
stroke="#000"
|
||||
stroke-width="4"
|
||||
/><rect x="22" y="55" width="4" height="26" rx="2" ry="15" />
|
||||
<text
|
||||
x="50"
|
||||
y="40"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
alignment-baseline="middle"
|
||||
stroke="#fff"
|
||||
stroke-width=".5"
|
||||
fill="none"
|
||||
font-size="20"
|
||||
>『LiB』</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
@@ -0,0 +1,135 @@
|
||||
// Load authors and genres asynchronously
|
||||
Promise.all([
|
||||
fetch("/authors").then((response) => response.json()),
|
||||
fetch("/genres").then((response) => response.json()),
|
||||
])
|
||||
.then(([authorsData, genresData]) => {
|
||||
// Populate authors dropdown
|
||||
const dropdown = document.getElementById("author-dropdown");
|
||||
authorsData.authors.forEach((author) => {
|
||||
const div = document.createElement("div");
|
||||
div.className = "p-2 hover:bg-gray-100 cursor-pointer";
|
||||
div.setAttribute("data-value", author.name);
|
||||
div.textContent = author.name;
|
||||
dropdown.appendChild(div);
|
||||
});
|
||||
|
||||
// Populate genres list
|
||||
const list = document.getElementById("genres-list");
|
||||
genresData.genres.forEach((genre) => {
|
||||
const li = document.createElement("li");
|
||||
li.className = "mb-1";
|
||||
li.innerHTML = `
|
||||
<label class="custom-checkbox flex items-center">
|
||||
<input type="checkbox" />
|
||||
<span class="checkmark"></span>
|
||||
${genre.name}
|
||||
</label>
|
||||
`;
|
||||
list.appendChild(li);
|
||||
});
|
||||
|
||||
initializeAuthorDropdown();
|
||||
})
|
||||
.catch((error) => console.error("Error loading data:", error));
|
||||
|
||||
function initializeAuthorDropdown() {
|
||||
const authorSearchInput = document.getElementById("author-search-input");
|
||||
const authorDropdown = document.getElementById("author-dropdown");
|
||||
const selectedAuthorsContainer = document.getElementById(
|
||||
"selected-authors-container",
|
||||
);
|
||||
const dropdownItems = authorDropdown.querySelectorAll("[data-value]");
|
||||
let selectedAuthors = new Set();
|
||||
|
||||
// Function to update highlights in dropdown
|
||||
const updateDropdownHighlights = () => {
|
||||
dropdownItems.forEach((item) => {
|
||||
const value = item.dataset.value;
|
||||
if (selectedAuthors.has(value)) {
|
||||
item.classList.add("bg-gray-200");
|
||||
} else {
|
||||
item.classList.remove("bg-gray-200");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Function to render selected authors
|
||||
const renderSelectedAuthors = () => {
|
||||
Array.from(selectedAuthorsContainer.children).forEach((child) => {
|
||||
if (child.id !== "author-search-input") {
|
||||
child.remove();
|
||||
}
|
||||
});
|
||||
|
||||
selectedAuthors.forEach((author) => {
|
||||
const authorChip = document.createElement("span");
|
||||
authorChip.className =
|
||||
"flex items-center bg-gray-200 text-gray-800 text-sm font-medium px-2.5 py-0.5 rounded-full";
|
||||
authorChip.innerHTML = `
|
||||
${author}
|
||||
<button type="button" class="ml-1 inline-flex items-center p-0.5 text-sm text-gray-400 bg-transparent rounded-sm hover:bg-gray-200 hover:text-gray-900" data-author="${author}">
|
||||
<svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Remove author</span>
|
||||
</button>
|
||||
`;
|
||||
selectedAuthorsContainer.insertBefore(authorChip, authorSearchInput);
|
||||
});
|
||||
updateDropdownHighlights();
|
||||
};
|
||||
|
||||
// Handle input focus to show dropdown
|
||||
authorSearchInput.addEventListener("focus", () => {
|
||||
authorDropdown.classList.remove("hidden");
|
||||
});
|
||||
|
||||
// Handle input for filtering
|
||||
authorSearchInput.addEventListener("input", () => {
|
||||
const query = authorSearchInput.value.toLowerCase();
|
||||
dropdownItems.forEach((item) => {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.style.display = text.includes(query) ? "block" : "none";
|
||||
});
|
||||
authorDropdown.classList.remove("hidden");
|
||||
});
|
||||
|
||||
// Handle clicks outside to hide dropdown
|
||||
document.addEventListener("click", (event) => {
|
||||
if (
|
||||
!selectedAuthorsContainer.contains(event.target) &&
|
||||
!authorDropdown.contains(event.target)
|
||||
) {
|
||||
authorDropdown.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// Handle author selection from dropdown
|
||||
authorDropdown.addEventListener("click", (event) => {
|
||||
const selectedValue = event.target.dataset.value;
|
||||
if (selectedValue) {
|
||||
if (selectedAuthors.has(selectedValue)) {
|
||||
selectedAuthors.delete(selectedValue);
|
||||
} else {
|
||||
selectedAuthors.add(selectedValue);
|
||||
}
|
||||
authorSearchInput.value = "";
|
||||
renderSelectedAuthors();
|
||||
authorSearchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle removing selected author chip
|
||||
selectedAuthorsContainer.addEventListener("click", (event) => {
|
||||
if (event.target.closest("button")) {
|
||||
const authorToRemove = event.target.closest("button").dataset.author;
|
||||
selectedAuthors.delete(authorToRemove);
|
||||
renderSelectedAuthors();
|
||||
authorSearchInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Initial render and highlights (without auto-focus)
|
||||
renderSelectedAuthors();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
@font-face {
|
||||
font-family: "Novem";
|
||||
src: url("novem.regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Dited";
|
||||
src: url("dited.regular.ttf") format("truetype");
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: "Novem", sans-serif;
|
||||
letter-spacing: 10px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
font-family: "Dited", sans-serif;
|
||||
letter-spacing: 2.5px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
/* Custom checkbox styles */
|
||||
.custom-checkbox {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.custom-checkbox input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
background-color: #fff;
|
||||
border: 2px solid #d1d5db; /* gray-300 */
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.custom-checkbox:hover input ~ .checkmark {
|
||||
border-color: #6b7280; /* gray-500 */
|
||||
}
|
||||
|
||||
.custom-checkbox input:checked ~ .checkmark {
|
||||
background-color: #6b7280; /* gray-500 */
|
||||
border-color: #6b7280;
|
||||
}
|
||||
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.custom-checkbox input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.custom-checkbox .checkmark:after {
|
||||
left: 6.5px;
|
||||
top: 6px;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
Reference in New Issue
Block a user