templates/vitekit/__/vite-template-main/js/libs/dynamic-adapt.ts
2026-04-12 21:03:18 +03:00

154 lines
5.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

interface DynamicAdaptObject {
element: HTMLElement
parent: HTMLElement
destination: HTMLElement
breakpoint: string
place: string
index: number
}
type AdaptType = 'min' | 'max'
class DynamicAdapt {
private readonly type: AdaptType
private objects: DynamicAdaptObject[]
private daClassname: string
private nodes: HTMLElement[]
private mediaQueries: string[]
constructor(type: AdaptType) {
this.type = type
}
init(): void {
// массив объектов
this.objects = []
this.daClassname = '_dynamic_adapt_'
// массив DOM-элементов
this.nodes = Array.from(document.querySelectorAll('[data-da]')) as HTMLElement[]
// наполнение objects объeктами
this.nodes.forEach((node) => {
const data = node.dataset.da?.trim() || ''
const dataArray = data.split(',')
const object: DynamicAdaptObject = {
element: node,
parent: node.parentNode as HTMLElement,
destination: document.querySelector(dataArray[0].trim()) as HTMLElement,
breakpoint: dataArray[1] ? dataArray[1].trim() : '767',
place: dataArray[2] ? dataArray[2].trim() : 'last',
index: this.indexInParent(node.parentNode as HTMLElement, node),
}
this.objects.push(object)
})
this.arraySort(this.objects)
// массив уникальных медиа-запросов
this.mediaQueries = this.objects
.map(({ breakpoint }) => `(${this.type}-width: ${breakpoint}px),${breakpoint}`)
.filter((item, index, self) => self.indexOf(item) === index)
// навешивание слушателя на медиа-запрос
// и вызов обработчика при первом запуске
this.mediaQueries.forEach((media) => {
const mediaSplit = media.split(',')
const matchMedia = window.matchMedia(mediaSplit[0])
const mediaBreakpoint = mediaSplit[1]
// массив объектов с подходящим брейкпоинтом
const objectsFilter = this.objects.filter(({ breakpoint }) => breakpoint === mediaBreakpoint)
matchMedia.addEventListener('change', () => {
this.mediaHandler(matchMedia, objectsFilter)
})
this.mediaHandler(matchMedia, objectsFilter)
})
}
// Основна функция
private mediaHandler(matchMedia: MediaQueryList, objects: DynamicAdaptObject[]): void {
if (matchMedia.matches) {
objects.forEach((object) => {
this.moveTo(object.place, object.element, object.destination)
})
} else {
objects.forEach(({ parent, element, index }) => {
if (element.classList.contains(this.daClassname)) {
this.moveBack(parent, element, index)
}
})
}
}
// Функция перемещения
private moveTo(place: string, element: HTMLElement, destination: HTMLElement): void {
element.classList.add(this.daClassname)
if (place === 'last' || place >= destination.children.length.toString()) {
destination.append(element)
return
}
if (place === 'first') {
destination.prepend(element)
return
}
destination.children[Number.parseInt(place)].before(element)
}
// Функция возврата
private moveBack(parent: HTMLElement, element: HTMLElement, index: number): void {
element.classList.remove(this.daClassname)
if (parent.children[index] !== undefined) {
parent.children[index].before(element)
} else {
parent.append(element)
}
}
// Функция получения индекса внутри родителя
private indexInParent(parent: HTMLElement, element: HTMLElement): number {
return Array.from(parent.children).indexOf(element)
}
// Функция сортировки массива по breakpoint и place
// по возрастанию для this.type = min
// по убыванию для this.type = max
private arraySort(arr: DynamicAdaptObject[]): void {
if (this.type === 'min') {
arr.sort((a, b) => {
if (a.breakpoint === b.breakpoint) {
if (a.place === b.place) {
return 0
}
if (a.place === 'first' || b.place === 'last') {
return -1
}
if (a.place === 'last' || b.place === 'first') {
return 1
}
return 0
}
return Number.parseInt(a.breakpoint) - Number.parseInt(b.breakpoint)
})
} else {
arr.sort((a, b) => {
if (a.breakpoint === b.breakpoint) {
if (a.place === b.place) {
return 0
}
if (a.place === 'first' || b.place === 'last') {
return 1
}
if (a.place === 'last' || b.place === 'first') {
return -1
}
return 0
}
return Number.parseInt(b.breakpoint) - Number.parseInt(a.breakpoint)
})
}
}
}
const da = new DynamicAdapt('max')
da.init()