This repository has been archived on 2024-02-11. You can view files and clone it, but cannot push or open issues or pull requests.
Files
notes_list/src/modules/DOMController.js

352 lines
9.1 KiB
JavaScript

import Templates from '../templates/Views'
import { createNode, destroyNode, findDates, formatDate, parseURL, replaceURLPathname, ucWords } from '../helpers'
import NotesAPI from './NotesAPI'
import ValidationController from './ValidationController'
import Alert from './AlertController'
export default class DOMController {
constructor (root, routes) {
this.routes = routes
this.root = root
try {
this.root.innerHTML = Templates.grid
this.renderRoutes()
} catch (e) {
Alert.displayDialog(e.toString(), 0, 3)
}
this.btnCreateNote = this.root.querySelector(window.env.querySelectors.btnCreateNote)
this.btnPrepopulate = this.root.querySelector(window.env.querySelectors.btnPrepopulate)
this.createNotesGrid(window.env.prePopulateAmount || 0)
this.listener([
[this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), { legend: 'Create Note' }],
[this.btnPrepopulate, 'click', (this.btnPrepopulate.dataset.action || 'showError')],
])
}
/**
*
*/
renderRoutes () {
for (const [route, name] of Object.entries(this.routes.routes)) {
const tab = createNode('li')
tab.classList.add('nav-tab')
if (route === '/') {
tab.classList.add('initial')
tab.classList.add('uk-active')
}
tab.innerHTML = `<a href="${route}">${name}</a>`
const anchor = tab.querySelector('a')
this.routes.navbar.appendChild(tab)
this.listener([anchor, 'click', 'createNotesGridByRoute', anchor])
}
}
createNotesGridByRoute (navTab) {
const pathname = parseURL(navTab.href).pathname
const route = pathname.slice(1).split('\/')[0]
window.history.pushState('', navTab.innerText, pathname) //change url without reload
//history.pushState({}, null, pathname) //change url
this.routes.navbar.querySelector('.uk-active').classList.remove('uk-active')
navTab.closest('.nav-tab').classList.add('uk-active')
document.getElementById(window.env.querySelectors.analyticsGrid.slice(1)).classList.add('uk-hidden')
document.getElementById(window.env.querySelectors.notesGrid.slice(1)).classList.remove('uk-hidden')
switch (route) {
case 'all':
this.createNotesGrid(0, null)
break
case '':
this.createNotesGrid(window.env.prePopulateAmount || 0)
break
case 'archive':
this.createNotesGrid(window.env.prePopulateAmount || 0, true)
break
case 'analytics':
document.getElementById(window.env.querySelectors.notesGrid.slice(1)).classList.add('uk-hidden')
const analyticsGrid = document.getElementById(window.env.querySelectors.analyticsGrid.slice(1))
this.createAnalyticsGrid(analyticsGrid)
break
}
}
prePopulate () {
const fakerAmount = document.querySelector(env.querySelectors.selectPrepopulate)
if (+fakerAmount.value <= 0) {
return
}
env.fakerAmount = (+fakerAmount.value > 44) ? 44 : +fakerAmount.value
localStorage.removeItem(window.env.localStorageKey)
fakerAmount.selectedIndex = 0
document.querySelector('.nav-tab.uk-active > a').click()
}
/**
* The method is being called for typical DOM-event task inside the table row
* to manipulate with records: deleting, editing, archiving.
*
* @param node
* @param method
*/
noteToAction (node, method) {
const row = node.closest('tr')
NotesAPI[method](parseInt(row.dataset.id, 10))
row.classList.add('as-removing')
setTimeout(() => {
row.remove()
document.querySelector('.nav-tab.uk-active > a').click()
}, 1050)
}
/**
*
* @param node
*/
noteToArchive (node) {
this.noteToAction(node, 'archiveNote')
}
/**
*
* @param node
*/
noteFromArchive (node) {
this.noteToAction(node, 'unArchiveNote')
}
/**
*
* @param node
*/
noteToTrash (node) {
this.noteToAction(node, 'deleteNote')
}
createAnalyticsGrid (grid) {
grid.classList.remove('uk-hidden')
const aGrid = grid.querySelector(' tbody')
aGrid.innerHTML = ''
for (const [key, value] of Object.entries(NotesAPI.getAnalytics())) {
const row = createNode('tr')
const td = createNode('td', {}, ucWords(key))
row.appendChild(td)
for (const [key, val] of Object.entries( value ) ) {
const td = createNode('td', {}, val.toString())
row.appendChild(td)
}
aGrid.appendChild(row)
}
}
/**
*
* @param len
* @param archived
*/
createNotesGrid (len = window.env.prePopulateAmount || 0, archived = false) {
const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody')
notesGrid.innerHTML = ''
const notes = NotesAPI.getNotesSanitized(len, archived)
if (notes.length === 0) { return }
notes.forEach(el => {
const row = createNode('tr')
row.dataset.id = el.id
window.env.gridOrder.notes.forEach(key => {
let attrs = {}
let value = el[key] || ''
switch (key) {
case 'createdAt':
value = formatDate(value)
break
case 'category':
value = ucWords(value)
break
case 'title':
attrs.class = 'uk-text-bold'
break
case 'dates':
attrs.style = 'font-size: 12px'
value = findDates(el.content).join(', ')
break
case 'edit' :
attrs.class = 'grid-control icon icon-edit'
attrs['data-action'] = 'getEditForm'
break
case 'archive' :
attrs.class = 'grid-control icon ' + (value === 'on' ? 'icon-unarchive' : 'icon-archive')
attrs['data-action'] = value === 'on' ? 'noteFromArchive' : 'noteToArchive'
value = ''
break
case 'delete' :
attrs.class = 'icon icon-delete grid-control'
attrs['data-action'] = 'noteToTrash'
break
}
const td = createNode('td', attrs, value)
if (key.match(/^(edit|archive|delete)$/)) {
this.listener([td, 'click', td.dataset.action, td])
}
row.appendChild(td)
})
notesGrid.appendChild(row)
})
}
/**
*
* @param selector
* @returns {{}}
*/
static getFormData (selector = window.env.querySelectors.noteEditForm) {
const formData = new FormData(document.querySelector(selector))
const data = {}
for (let key of formData.keys()) {
data[key] = formData.get(key)
}
return data
}
/**
*
* @param args
*/
getEditForm (args = {}) {
const node = createNode('div', { class: window.env.querySelectors.modal.slice(1) },
Templates.templates.editForm)
if (args.legend) {
node.querySelector('legend').innerHTML = args.legend
} else {
DOMController.fillTheForm(node, args)
}
this.root.appendChild(node)
node.style.display = 'block'
const contentInput = node.querySelector('textarea[maxlength]')
const titleInput = node.querySelector('input[maxlength]')
this.listener([
[node.querySelector(window.env.querySelectors.btnDestroyModal), 'click', destroyNode, node], //Remove modal dialog
[node.querySelector('form'), 'submit', NotesAPI.saveNote],
[contentInput, 'keydown', ValidationController.lengthOnKeyUp, contentInput],
[titleInput, 'keydown', ValidationController.lengthOnKeyUp, titleInput],
])
}
/**
*
* @param $form
* @param $el
*/
static fillTheForm ($form, $el) {
if ($el instanceof Element) {
const note = NotesAPI.getNoteByID(+$el.closest('tr').dataset.id)
if (note.length === 1) {
for (const [key, value] of Object.entries(note[0])) {
const $input = $form.querySelector(`[name='${key}']`)
if ($input instanceof Element) {
if ($input.type === 'checkbox') {
if (value !== '') {
$input.click()
}
} else {
$input.value = value
}
}
}
}
}
}
/**
* The function takes single DOM element or list of them
*
* @param els
* @returns {void|*}
*/
listener (els) { return els[0] instanceof Element ? this.on(els) : els.forEach(el => this.on(el)) }
/**
* TODO: guessed this should be rethought
* @param args
*/
on (args) {
const [selector, event, method] = args
if (!(selector instanceof Element)) {
Alert.displayDialog(`Internal Error: Try reload a page`, 3000, 3)
return false
}
try {
selector.addEventListener(event, evt => {
const params = args[3] || {}
if (selector.tagName === 'A') {
evt.preventDefault()
}
if (evt.type === 'submit') {
evt.preventDefault()
setTimeout(() => {
document.querySelector('.nav-tab.uk-active > a').click()
}, 300)
}
if (typeof this[method] === 'function') {
this[method](params)
} else if (typeof method === 'function') {
method(params)
}
})
return true
} catch (err) {
Alert.displayDialog(`Error: ${err.toString()}`, 3000, 3)
return false
}
}
}