diff --git a/src/BukiVedi.App/web/js/components/tags/index.js b/src/BukiVedi.App/web/js/components/tags/index.js new file mode 100644 index 0000000..83d7ad5 --- /dev/null +++ b/src/BukiVedi.App/web/js/components/tags/index.js @@ -0,0 +1,106 @@ +import { waitForElement, changeTags } from "../../requests/index.js"; + +export default class TagsComponent { + element; + constructor({ tags = [], bookid = null, article }) { + this.tags = tags; + this.article = article; + this.bookid = bookid; + this.isTextareaMode = false; + this.render(); + } + + get template() { + return ` +
+ Теги +
+
+
${this.getTags( + this.tags + )}
+ `; + } + getTags(tags) { + return (tags || []) + .map(({ id, name }) => { + return `#${name}`; + }) + .join(""); + } + + getTextarea(tags) { + return ` + + `; + } + + initialize() { + this.initEventListeners(); + } + + initEventListeners() { + const tagsEditorListener = this.article.querySelector( + ".main-book__tags_title" + ); + + tagsEditorListener.addEventListener("click", () => { + const tagsWrapper = this.article.querySelector(".main-book__tags"); + + if (!this.isTextareaMode) { + tagsWrapper.innerHTML = this.getTextarea(this.tags); + this.article.querySelector(".main-book__textarea").focus(); + this.isTextareaMode = true; + } + }); + + this.article.addEventListener("keypress", (event) => { + if (event.key === "Enter") { + if (this.isTextareaMode) { + this.closeTextarea(); + } + } + }); + + document.addEventListener("outside-click", async () => { + if (this.isTextareaMode) { + this.closeTextarea(); + } + }); + } + + async closeTextarea() { + const tagsWrapper = this.article.querySelector(".main-book__tags"); + const area = this.article.querySelector(".main-book__textarea"); + + const tags = area.value + .split(" ") + .map((item) => item.trim()) + .filter((i) => i); + if (!!(tags || []).length > 0) { + await this.setTags({ bookid: this.bookid, tags }); + } + tagsWrapper.innerHTML = this.getTags(this.tags); + this.isTextareaMode = false; + } + + async waitRendered() { + await waitForElement(".main-book__tags"); + this.initialize(); + } + + async render() { + this.element = document.createElement("div"); + this.element.innerHTML = this.template; + await this.waitRendered(); + } + + async setTags({ bookid, tags }) { + document.body.classList.add("blur", "spinner"); + const newTags = await changeTags({ bookid, tags }); + this.tags = newTags; + document.body.classList.remove("spinner"); + } +} diff --git a/src/BukiVedi.App/web/js/constants/index.js b/src/BukiVedi.App/web/js/constants/index.js index 641cb0b..998ca84 100644 --- a/src/BukiVedi.App/web/js/constants/index.js +++ b/src/BukiVedi.App/web/js/constants/index.js @@ -2,11 +2,6 @@ import * as requests from '../requests/index.js'; export const SUCCESS = "success"; export const menuEnum = [ - // { - // id: 0, - // label: "В избранное", - // action: requests.addBookToFavourites, - // }, { id: 1, label: "Авторов в избранное", @@ -25,11 +20,7 @@ export const menuEnum = [ { id: 4, label: "Игнорировать автора", - action: requests.ignoreAuthor, - }, - { - id: 5, - label: "Теги", + action: requests.ignoreAuthors, }, { id: 6, @@ -40,3 +31,21 @@ export const menuEnum = [ label: "Поделиться", }, ]; + +export const DEFAULT_AUTHOR = { + id: 0, + name: 'Неизвестно' +}; + +export const likeEnum = [ + { + id: 0, + label: "Нравится", + action: requests.addBookToFavourites, + }, + { + id: 1, + label: "Не нравится", + action: requests.removeBookToFavourites, + } +] \ No newline at end of file diff --git a/src/BukiVedi.App/web/js/main/index.js b/src/BukiVedi.App/web/js/main/index.js index 0050118..e6fa073 100644 --- a/src/BukiVedi.App/web/js/main/index.js +++ b/src/BukiVedi.App/web/js/main/index.js @@ -1,13 +1,22 @@ -import { menuEnum } from "../constants/index.js"; -import { fetchData, downloadBook, searchByAuthor } from "../requests/index.js"; +import { menuEnum, likeEnum, DEFAULT_AUTHOR } from "../constants/index.js"; +import { + fetchData, + downloadBook, + searchByAuthor, + searchByTag, + waitForElement, +} from "../requests/index.js"; + +import { stopPropagation } from "../utils/index.js"; +import TagsComponent from "../components/tags/index.js"; export default class BookSection { subElements = []; element; - constructor({ url = "", label = "" } = {}) { + constructor({ url = "" } = {}) { this.url = url; - this.label = label; + this.data = []; this.render(); } @@ -29,11 +38,15 @@ export default class BookSection { booksListener.addEventListener("click", (event) => { let id, title; const isLike = event.target.closest("[data-like]"); - const isMenu = event.target.closest("[data-menu]"); const isAction = event.target.closest("[data-action]"); const isLink = event.target.closest("[data-link]"); const isAuthor = event.target.closest("[data-author]"); + const isMoreDetails = event.target.closest("[data-details]"); + const isTagWrapper = event.target.closest("[data-tags-wrapper]"); + const isTag = event.target.closest("[data-tag]"); + + stopPropagation(event); switch (true) { case !!isAction: @@ -62,6 +75,21 @@ export default class BookSection { id = isAuthor.dataset.author; this.update({ isByAuthor: true, id }); + break; + case !!isMoreDetails: + this.toggleDetails(isMoreDetails); + + break; + case !isTagWrapper: + const myEvent = new CustomEvent("outside-click"); + document.dispatchEvent(myEvent); + this.closeMenu(); + + break; + case !!isTag: + id = isTag.dataset.tag; + this.update({ isByTag: true, id }); + default: this.closeMenu(); @@ -75,30 +103,33 @@ export default class BookSection { getBookBody(data) { return (data || []) .map( - ({ - id, - authors, - description, - format, - genres, - imageUrl, - title, - series, - subseries, - year, - tags, - isFavorite, - }) => { - const { name, id: authorid } = authors[0] || "Неизвестно"; + ( + { + id, + authors, + description, + format, + genres, + imageUrl, + title, + series, + subseries, + year, + isFavorite, + }, + index + ) => { + const isLast = !!(index === data?.length - 1); return ` -
+
+ }" data-like=${isFavorite} data-bookid=${id}>