{"version":3,"file":"index.mjs","sources":[""],"sourcesContent":["import { isNode } from \"@web3r/flowerkit/dom\";\r\nimport { getDocument } from \"ssr-window\";\r\nimport { Dispatcher } from \"../dispatcher\";\r\nimport { Observer } from \"../observer\";\r\n\r\n\r\n/**\r\n * Базовый контракт экземпляра элемента коллекции.\r\n */\r\nexport interface ICollectionItem {\r\n  instance: TCollectionElement;\r\n}\r\n\r\n\r\n/**\r\n * DOM-элемент, учитываемый в коллекции плагинов.\r\n * Добавляет к узлу пропс `isInCollection`\r\n */\r\nexport type TCollectionElement = Element & {\r\n  isInCollection?: boolean;\r\n};\r\n\r\n\r\n/**\r\n * Базовый тип параметров конструктора элемента коллекции.\r\n */\r\nexport type TCollectionItemDefaultCfg = Record<string, unknown>;\r\n\r\n/**\r\n * Тип конструктора плагина, совместимого с `Collection`.\r\n * @template T Тип экземпляра элемента коллекции.\r\n */\r\nexport type TCollectionPluginCtor<T extends ICollectionItem = ICollectionItem> = new (\r\n  el: TCollectionElement,\r\n  params?: TCollectionItemDefaultCfg,\r\n) => T;\r\n\r\n/**\r\n * Factory. Хранилище для экземпляров вызовов других плагинов, а также набор классов, доступных для наследования и служащих для ускорения проектирования простых пользовательских плагинов.\r\n * @module Collection\r\n * @param selector{String} селектор для идентификации класса\r\n * @param plugin{Function} плагин, добавляемый в коллекцию\r\n * @param isAutoInit{Boolean=} автоматический вызов методов `init` и `bindEvents` (подходит для большинства обычных коллекций)\r\n * @example\r\n * import { Collection } from \"@delement/ui/plugins\"\r\n *\r\n * class MyPlugin {\r\n *\r\n *   constructor(el) {\r\n *     this.instance = el;\r\n *   }\r\n *\r\n * }\r\n *\r\n * export default class CollectionMyPlugin extends Collection {\r\n *\r\n *   static selector = \"[data-js-MyPlugin]\";\r\n *\r\n *   constructor() {\r\n *     super(CollectionMyPlugin.selector, MyPlugin)\r\n *   }\r\n *\r\n *   beforeAddCallback() {\r\n *     // код выполнится, когда элемент добавится в коллекцию\r\n *   }\r\n *\r\n * }\r\n */\r\nclass Collection<T extends ICollectionItem = ICollectionItem> {\r\n\r\n  /**\r\n   * Коллекция экземпляров\r\n   * @type {Array}\r\n   * @private\r\n   */\r\n  #collectionStorage: T[] = [];\r\n\r\n  collectionObserverSelector = \"\";\r\n\r\n  collectionObserverClass: TCollectionPluginCtor<T> | null = null;\r\n\r\n  /**\r\n   * События\r\n   * @type {{mount: string, unmount: string}}\r\n   */\r\n  static bubbles = {\r\n    mount: \"collection::mount\",\r\n    unmount: \"collection::unmount\",\r\n  };\r\n\r\n  constructor(selector: string, plugin: TCollectionPluginCtor<T>, isAutoInit = true) {\r\n    if (typeof selector === \"string\" && typeof plugin === \"function\") {\r\n      this.collectionObserverSelector = selector;\r\n      this.collectionObserverClass = plugin;\r\n      Observer.subscribe = {\r\n        selector,\r\n        callback: this.#collectionObserveCallback.bind(this),\r\n      };\r\n      if (isAutoInit) {\r\n        this.init();\r\n        this.bindEvents();\r\n      }\r\n    } else {\r\n      Collection.#showError(\"Selector or class are not defined while extending\", this);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Инициализирует коллекцию, добавляя все элементы по селектору в указанном контексте\r\n   * @param context{Document|HTMLElement=} контекст для поиска элементов, по умолчанию document\r\n   */\r\n  init(context: Document | HTMLElement = getDocument()) {\r\n    if (\"querySelectorAll\" in context) {\r\n      context\r\n        .querySelectorAll(this.collectionObserverSelector)\r\n        .forEach((el: Element) => this.addToCollection(el as TCollectionElement));\r\n    } else {\r\n      console.debug(`[Collection]: Context for selector \"${this.collectionObserverSelector}\" is not a node`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Подписывается на обновления страницы для автоматического вызова init (по умолчанию через Dispatcher)\r\n   */\r\n  bindEvents() {\r\n    Dispatcher.initiator = {\r\n      selector: this.collectionObserverSelector,\r\n      initiator: (context: Document | HTMLElement) => this.init(context),\r\n      getCollection: () => this.collection || [],\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Информирование об ошибках\r\n   * @param msg{String} сообщение\r\n   * @param obj{Object} объект с информацией\r\n   * @private\r\n   */\r\n  static #showError(msg: string, obj: unknown) {\r\n    console.error(`[Collection]: ${msg}`, obj);\r\n  }\r\n\r\n  /**\r\n   * Добавление экземпляра класса в коллекцию. По-умолчанию проверяет, существует ли экземпляр с таким instance. Если существует, то добавления не происходит\r\n   * @param newClass{Function|Class}\r\n   */\r\n  #appendToCollection(newClass: T): void {\r\n    const { instance } = newClass;\r\n    if (instance && !instance.isInCollection) {\r\n      const itemInCollection = this.getByDOMElement(instance);\r\n      if (!itemInCollection) {\r\n        this.beforeAddCallback(instance, newClass);\r\n        this.#collectionStorage = [ ...this.#collectionStorage, newClass ];\r\n        this.afterAddCallback(instance, newClass);\r\n      }\r\n    } else {\r\n      Collection.#showError(\"Missed instance field for item: \", newClass);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Получает актуальный массив экземпляров коллекции\r\n   * @template T - тип элементов коллекции\r\n   * @returns {T[]} массив экземпляров\r\n   */\r\n  get collection(): T[] {\r\n    return this.#collectionStorage;\r\n  }\r\n\r\n  /**\r\n   * Ищет элемент внутри коллекции по DOM-элементу путём сверки поля instance\r\n   * @param el{Element} DOM-элемент для поиска\r\n   * @returns {T|null} найденный экземпляр или null\r\n   */\r\n  getByDOMElement(el: Element) {\r\n    return this.collection.find((item: T) => item.instance === el) || null;\r\n  }\r\n\r\n  /**\r\n   * Ищет элемент внутри коллекции по индексу\r\n   * @param index{Number=} индекс элемента, по умолчанию 0\r\n   * @returns {T|null} найденный экземпляр или null\r\n   */\r\n  getByIndex(index = 0) {\r\n    return this.collection[index] || null;\r\n  }\r\n\r\n  /**\r\n   * Callback, вызываемый перед удалением элемента из коллекции\r\n   * @param el{Element} удаляемый из коллекции элемент\r\n   * @param instance{T} экземпляр вызова класса\r\n   */\r\n  beforeRemoveCallback(el: TCollectionElement, instance: T) {\r\n  }\r\n\r\n  /**\r\n   * Callback, вызываемый после удаления элемента из коллекции\r\n   * @param el{Element} удалённый из коллекции элемент\r\n   * @param instance{T} экземпляр вызова класса\r\n   */\r\n  afterRemoveCallback(el: TCollectionElement, instance: T) {\r\n  }\r\n\r\n  /**\r\n   * Callback, вызываемый перед добавлением элемента в коллекцию\r\n   * @param el{Element} добавляемый в коллекцию элемент\r\n   * @param instance{T} экземпляр вызова класса\r\n   */\r\n  beforeAddCallback(el: TCollectionElement, instance: T) {\r\n  }\r\n\r\n  /**\r\n   * Callback, вызываемый после добавления элемента в коллекцию\r\n   * @param el{Element} добавленный в коллекцию элемент\r\n   * @param instance{T} экземпляр вызова класса\r\n   */\r\n  afterAddCallback(el: TCollectionElement, instance: T) {\r\n  }\r\n\r\n  /**\r\n   * Удаляет элемент из коллекции по экземпляру класса\r\n   * @param collectionClass{T} экземпляр класса для удаления\r\n   */\r\n  removeFromCollection(collectionClass: T) {\r\n    const { instance } = collectionClass;\r\n    if (instance) {\r\n      this.beforeRemoveCallback(instance, collectionClass);\r\n      const collectionItemIndex = this.collection.indexOf(collectionClass);\r\n      if (collectionItemIndex < 0) {\r\n        return;\r\n      }\r\n      this.#collectionStorage.splice(collectionItemIndex, 1);\r\n      this.setElementState(instance, false);\r\n      this.afterRemoveCallback(instance, collectionClass);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Callback, вызываемый при изменении содержимого страниц, поступающих через Watcher\r\n   */\r\n  #collectionObserveCallback(mutationsList: MutationRecord[] = []) {\r\n    if (!mutationsList.length) {\r\n      this.#collectionObserveRemoving();\r\n      this.#collectionObserveAdding();\r\n      return;\r\n    }\r\n\r\n    const removedElements = new Set<TCollectionElement>();\r\n    const addedElements = new Set<TCollectionElement>();\r\n\r\n    mutationsList.forEach(({ addedNodes, removedNodes }) => {\r\n      this.#getCollectionElementsFromNodes(removedNodes)\r\n        .forEach((el: TCollectionElement) => removedElements.add(el));\r\n\r\n      this.#getCollectionElementsFromNodes(addedNodes)\r\n        .forEach((el: TCollectionElement) => addedElements.add(el));\r\n    });\r\n\r\n    if (!removedElements.size && !addedElements.size) {\r\n      this.#collectionObserveRemoving();\r\n      this.#collectionObserveAdding();\r\n      return;\r\n    }\r\n\r\n    this.#collectionObserveRemoving([ ...removedElements ]);\r\n    this.#collectionObserveAdding([ ...addedElements ]);\r\n  }\r\n\r\n  #getCollectionElementsFromNodes(nodes: NodeList = [] as unknown as NodeList): TCollectionElement[] {\r\n    const elements = new Set<TCollectionElement>();\r\n    [ ...nodes ].forEach((node) => {\r\n      if (!(node instanceof Element)) {\r\n        return;\r\n      }\r\n\r\n      if (node.matches(this.collectionObserverSelector)) {\r\n        elements.add(node as TCollectionElement);\r\n      }\r\n\r\n      node.querySelectorAll(this.collectionObserverSelector)\r\n        .forEach((el: Element) => elements.add(el as TCollectionElement));\r\n    });\r\n\r\n    return [ ...elements ];\r\n  }\r\n\r\n  /**\r\n   * Проверка отсутствия DOM-элементов коллекции на странице после изменений и удаление пустых классов\r\n   */\r\n  #collectionObserveRemoving(removedElements: TCollectionElement[] = []) {\r\n    if (removedElements.length) {\r\n      removedElements.forEach((el: TCollectionElement) => {\r\n        if (el.isConnected) {\r\n          return;\r\n        }\r\n\r\n        const found = this.getByDOMElement(el);\r\n        if (found) {\r\n          this.removeFromCollection(found);\r\n        }\r\n      });\r\n      return;\r\n    }\r\n\r\n    const target = Observer.getTarget();\r\n    if (!(target instanceof Node)) {\r\n      return;\r\n    }\r\n    this.collection.forEach((collectionItem: T) => {\r\n      if (!target.contains(collectionItem.instance)) {\r\n        this.removeFromCollection(collectionItem);\r\n      }\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Устанавливает маркер состояния DOM-элемента (инициализирован или удалён из коллекции)\r\n   * @param el{Element} DOM-элемент\r\n   * @param isInCollection{Boolean=} маркер состояния (true = в коллекции)\r\n   */\r\n  setElementState(el: TCollectionElement, isInCollection = true) {\r\n    el.isInCollection = isInCollection;\r\n  }\r\n\r\n  /**\r\n   * Проверяет валидность элемента перед добавлением в коллекцию.\r\n   * Полезно, если необходимо исключить вызов класса для определенных элементов.\r\n   * @returns {Boolean}\r\n   */\r\n  isValid(el: TCollectionElement): boolean {\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * Добавляет элемент или экземпляр класса в коллекцию\r\n   * @template T\r\n   * @returns {T|null} возвращает экземпляр класса или null, если элемент уже в коллекции\r\n   */\r\n  addToCollection(item: TCollectionElement | T, params: Record<string, unknown> = {}) {\r\n    const inputEl = isNode(item as unknown as Node) ? item as TCollectionElement : (item as T).instance;\r\n    if (inputEl) {\r\n      if (!this.isValid(inputEl)) {\r\n        return null;\r\n      }\r\n      const el = inputEl as TCollectionElement;\r\n      if (!this.getByDOMElement(el) && !el.isInCollection) {\r\n        if (!this.collectionObserverClass) {\r\n          Collection.#showError(\"Observer class is not defined\", this);\r\n          return null;\r\n        }\r\n        const collectionClass = (item instanceof this.collectionObserverClass)\r\n          ? item as T\r\n          : new this.collectionObserverClass(el, params);\r\n        collectionClass.instance = collectionClass.instance || el;\r\n        this.#appendToCollection(collectionClass);\r\n        this.setElementState(el);\r\n        return collectionClass;\r\n      } else {\r\n        return null;\r\n      }\r\n    } else {\r\n      Collection.#showError(\"Can't find element of collection item:\", item);\r\n      return null;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Проверка присутствия DOM-элементов коллекции на странице после изменений и добавление новых классов\r\n   */\r\n  #collectionObserveAdding(addedElements: TCollectionElement[] = []) {\r\n    if (addedElements.length) {\r\n      addedElements.forEach((el: TCollectionElement) => this.addToCollection(el));\r\n      return;\r\n    }\r\n\r\n    const target = Observer.getTarget();\r\n    if (\r\n      typeof target === \"object\"\r\n      && target !== null\r\n      && \"querySelectorAll\" in target\r\n      && typeof target.querySelectorAll === \"function\"\r\n    ) {\r\n      const queryRoot = target as Document | Element;\r\n      queryRoot.querySelectorAll(this.collectionObserverSelector)\r\n        .forEach((el: Element) => this.addToCollection(el as TCollectionElement));\r\n    } else {\r\n      console.debug(`[Collection]: Context for selector \"${this.collectionObserverSelector}\" is not a node`);\r\n    }\r\n  }\r\n\r\n}\r\n\r\nexport {\r\n  Collection,\r\n};\r\n"],"names":["Collection","collectionStorage","collectionObserverSelector","collectionObserverClass","static","mount","unmount","constructor","selector","plugin","isAutoInit","this","Observer","subscribe","callback","collectionObserveCallback","bind","init","bindEvents","showError","context","getDocument","querySelectorAll","forEach","el","addToCollection","console","debug","Dispatcher","initiator","getCollection","collection","msg","obj","error","appendToCollection","newClass","instance","isInCollection","itemInCollection","getByDOMElement","beforeAddCallback","afterAddCallback","find","item","getByIndex","index","beforeRemoveCallback","afterRemoveCallback","removeFromCollection","collectionClass","collectionItemIndex","indexOf","splice","setElementState","mutationsList","length","collectionObserveRemoving","collectionObserveAdding","removedElements","Set","addedElements","addedNodes","removedNodes","getCollectionElementsFromNodes","add","size","nodes","elements","node","Element","matches","isConnected","found","target","getTarget","Node","collectionItem","contains","isValid","params","inputEl","isNode","queryRoot"],"mappings":"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"}