"use strict"; /** * @name gallery.mjs * * @description * Module for creating galleries with re-ordering * * @class * @public * * @example * import gallery from "https://codepen.io/mirzaev-sexy/pen/RNPdYvv.js"; * * // Initializing the instance * const instance = new gallery( * document.getElementById("wrap"), * document.getElementById("images"), * document.getElementById("gallery"), * true * ); * * {@link https://git.svoboda.works/mirzaev/gallery.mjs} * {@link https://codepen.io/mirzaev-sexy/pen/RNPdYvv} * * @todo 1. Instead of `ascend()`` create `remember()` and `restore()` methods * * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ export default class gallery { /** * @name Wrap * * @description * Wrap for the gallery * * @type {HTMLElement} * * @protected */ #wrap; /** * @name Input * * @description * Input for importing images * * @type {HTMLInputElement} * * @protected */ #input; /** * @name Gallery * * @description * Wrap for images elements (`flex-flow: row wrap`) * * @type {HTMLElement} * * @protected */ #gallery; /** * @name Identifiers * * @description * Identifiers registry of loaded images (array proxy) * * @see https://stackoverflow.com/a/76599646 by Alexander Nenashev * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy * * @type {Proxy} * * @protected */ #identifiers; /** * @name Identifiers (get) * * @description * Identifiers registry of loaded images (array proxy) * * @see https://stackoverflow.com/a/76599646 by Alexander Nenashev * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy * * @return {array} * * @public */ get identifiers() { return this.#identifiers; } /** * @name Update * * @type {Promise|null} * * @protected */ #update; /** * @name Prefixes * * @description * Prefixes for identifiers * * @type {object} */ prefixes = { wrap: "", image: "image_" }; /** * @name Dragged * * @description * Buffer of currently dragged wrap identifier * * @type {string|number} * * @protected */ #dragged; /** * @name Events * * @type {Map} * * @protected */ #events = new Map([ [ "dragstart", (event) => { // Disabling default actions this.#dragged = event.target.getAttribute("id"); // Allowing moving event.dataTransfer.effectAllowed = "move"; } ], [ "dragover", (event) => { // Disabling default actions event.preventDefault(); // Allowing moving event.dataTransfer.dropEffect = "move"; } ], [ "dragenter", (event) => { // Searching for the wrap const wrap = event.target.closest("div.image"); if (wrap?.getAttribute("id") !== this.#dragged) { // Wrap is not currently draggable wrap // Writing class about targeting wrap?.classList.add("target"); } } ], [ "dragleave", (event) => { // Searching for the closest parent wrap const wrap = event.target.closest("div.image"); if (wrap instanceof HTMLElement) { // Found the wrap // Deleting class about targeting wrap.classList.remove("target"); } } ], [ "dragend", (event) => { // Searching for the closest parent wrap const wrap = event.target.closest("div.image"); if (wrap instanceof HTMLElement) { // Found the wrap // Deleting class about targeting wrap.target.classList.remove("target"); } } ], [ "drop", (event) => { // Searching for the closest parent wrap const wrap = event.target.closest("div.image"); if ( wrap instanceof HTMLElement && wrap.getAttribute("id") && wrap.getAttribute("id") !== this.#dragged ) { // Found the wrap and has it identifier and it not currently draggable wrap // Deleting class about targeting from every wrap this.#gallery .querySelector("div.image.target") ?.classList.remove("target"); // Initializing indexes of wraps in the identifiers registry const from = this.#identifiers.indexOf(this.#dragged); const to = this.#identifiers.indexOf(wrap.getAttribute("id")); // Swapping wraps [this.#identifiers[from], this.#identifiers[to]] = [ this.#identifiers[to], this.#identifiers[from] ]; } } ] ]); /** * @name Constructor * * @description * Initialize the instance * * @param {HTMLElement} wrap The wrap element * @param {HTMLInputElement} input The input element * @param {HTMLElement} gallery The gallery element * @param {boolean} [inject=false] Write the instance into the wrap element? **/ constructor(wrap, input, gallery, inject = false) { if (wrap instanceof HTMLElement) { // Initialized the wrap element // Writing the wrap this.#wrap = wrap; // Writing the instance into the wrap element if (inject) this.#wrap.gallery = this; } if (input instanceof HTMLInputElement) { // Initialized the input element // Writing the input this.#input = input; } if (gallery instanceof HTMLElement) { // Initialized the gallery element // Writing the gallery this.#gallery = gallery; } // Initializing the identifiers registry proxy this.proxy(); // Synchronizing the identifiers registry with images in the gallery element this.#identifiers.push( ...[...this.#gallery.querySelectorAll("div.image")].map((image) => image.getAttribute("id") ) ); } /** * @name Proxy * * @description * Initialize the identifiers registry proxy */ proxy() { // Initializing the identifiers registry this.#identifiers = new Proxy([], { set: (target, property, value) => { // Postponing the update with a microtask this.#update ??= Promise.resolve().then(() => { // Deinitializing the update promise this.#update = null; // Generating the order row (can be deleted without any problems) document.getElementById("order").textContent = "Order: " + target.join(", "); // Re-ordering wraps
elements by the identifiers registry target.forEach((identifier) => { this.#gallery.appendChild(document.getElementById(identifier)); }); }); // Regenerating the identifiers registry and return (success) return Reflect.set(target, property, value); } }); } /** * @name Ascending * * @description * Sort images in ascending order */ ascending() { this.#identifiers.sort((a, b) => a - b); } /** * @name Start * * @description * Start handling moving of images */ start() { // Initializing events listeners for (const [event, handler] of this.#events) this.#gallery.addEventListener(event, handler); } /** * @name Stop * * @description * Stop handling moving of images */ stop() { // Deinitializing events listeners for (const [event, handler] of this.#events) this.#gallery.removeEventListener(event, handler); } /** * @name Import * * @description * Creating images elements by loaded images * * @param {FileList} files Loaded images */ import(files) { // Stopping events handlers this.stop(); // Initializing the identifiers registry proxy this.proxy(); // Deleting deprecated images from the gallery this.#gallery.innerHTML = ""; for (let i = 0; i < files.length; i++) { // Iterating over imported images // Creating the wrap
element const wrap = document.createElement("div"); wrap.classList.add("image"); wrap.setAttribute("id", this.prefixes.wrap + i); wrap.setAttribute("draggable", true); // Creating the button