Compare commits
5 Commits
505f4dffc0
...
239452682c
| Author | SHA1 | Date | |
|---|---|---|---|
| 239452682c | |||
| 6f8805e23a | |||
| f5efd4b551 | |||
| 5433fe65ea | |||
| dc19bb1fa5 |
4
.gitignore
vendored
@@ -86,7 +86,6 @@ out
|
|||||||
|
|
||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
@@ -204,7 +203,6 @@ out
|
|||||||
|
|
||||||
# Nuxt.js build / generate output
|
# Nuxt.js build / generate output
|
||||||
.nuxt
|
.nuxt
|
||||||
dist
|
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
@@ -237,3 +235,5 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
/.trash/
|
||||||
|
/dist/*.LICENSE.txt
|
||||||
|
|||||||
2
dist/app.19f5ee6c7563dd0839a8.js
vendored
Normal file
1
dist/app.css
vendored
Normal file
2
dist/faker.19f5ee6c7563dd0839a8.js
vendored
Normal file
15
dist/index.html
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Notes in JS (Home task 1)</title>
|
||||||
|
|
||||||
|
<link href="app.css" rel="stylesheet"></head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="appConsole" class="uk-container uk-margin-remove uk-background-muted uk-text-small"></div>
|
||||||
|
<div id="app" class="uk-container"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script defer src="vendors-node_modules_faker_index_js.19f5ee6c7563dd0839a8.js"></script><script defer src="faker.19f5ee6c7563dd0839a8.js"></script><script defer src="app.19f5ee6c7563dd0839a8.js"></script></body>
|
||||||
|
</html>
|
||||||
2
dist/vendors-node_modules_faker_index_js.19f5ee6c7563dd0839a8.js
vendored
Normal file
16
package.json
@@ -7,25 +7,37 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "cross-env SOURCE_MAP_ENV=true webpack serve --open",
|
"start": "cross-env SOURCE_MAP_ENV=true webpack serve --open",
|
||||||
"srv": "cross-env SOURCE_MAP_ENV=true webpack serve",
|
"srv": "cross-env SOURCE_MAP_ENV=true webpack serve",
|
||||||
|
"srv-prod": "cross-env SOURCE_MAP_ENV=true NODE_ENV=production webpack serve",
|
||||||
"watch": "webpack --watch",
|
"watch": "webpack --watch",
|
||||||
"build": "cross-env NODE_ENV=production webpack",
|
"build": "cross-env NODE_ENV=production webpack",
|
||||||
"build-dev": "cross-env SOURCE_MAP_ENV=true webpack"
|
"build-dev": "cross-env SOURCE_MAP_ENV=true NODE_ENV=production webpack"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.15.5",
|
"@babel/core": "^7.15.5",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||||
"@babel/preset-env": "^7.15.6",
|
"@babel/preset-env": "^7.15.6",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^6.3.0",
|
"css-loader": "^6.3.0",
|
||||||
|
"css-minimizer-webpack-plugin": "^3.1.1",
|
||||||
|
"css-mqpacker-webpack-plugin": "^0.12.1",
|
||||||
|
"glob": "^7.2.0",
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"mini-css-extract-plugin": "^2.3.0",
|
"mini-css-extract-plugin": "^2.3.0",
|
||||||
"terser-webpack-plugin": "^5.2.4",
|
"mini-svg-data-uri": "^1.3.3",
|
||||||
|
"postcss-loader": "^6.1.1",
|
||||||
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
"purgecss-webpack-plugin": "^4.0.3",
|
||||||
|
"style-loader": "^3.3.0",
|
||||||
"webpack": "^5.56.1",
|
"webpack": "^5.56.1",
|
||||||
"webpack-cli": "^4.8.0",
|
"webpack-cli": "^4.8.0",
|
||||||
"webpack-dev-server": "^4.3.1"
|
"webpack-dev-server": "^4.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"faker": "^5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/env.json
@@ -1,6 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"routes": {
|
||||||
|
"/all": "All Notes",
|
||||||
|
"/": "Recent Notes",
|
||||||
|
"/archive": "Archived Notes",
|
||||||
|
"/analytics": "Analytics"
|
||||||
|
},
|
||||||
"localStorageKey": "notes-home-task-1",
|
"localStorageKey": "notes-home-task-1",
|
||||||
"prePopulateAmount": 7,
|
"prePopulateAmount": 7,
|
||||||
|
"fakerAmount": 44,
|
||||||
"lists": {
|
"lists": {
|
||||||
"monthsFull": ["January","February","March","April","May","June","July", "August","September","October","November","December"]
|
"monthsFull": ["January","February","March","April","May","June","July", "August","September","October","November","December"]
|
||||||
},
|
},
|
||||||
@@ -8,10 +15,15 @@
|
|||||||
"primary", "success", "warning", "danger"
|
"primary", "success", "warning", "danger"
|
||||||
],
|
],
|
||||||
"querySelectors": {
|
"querySelectors": {
|
||||||
|
"root": "#app",
|
||||||
"console": "#appConsole",
|
"console": "#appConsole",
|
||||||
|
"navTabs": "#asSwitcher",
|
||||||
"modal": ".modal",
|
"modal": ".modal",
|
||||||
|
"analyticsGrid": "#analyticsGrid",
|
||||||
"notesGrid": "#notesGrid",
|
"notesGrid": "#notesGrid",
|
||||||
"noteEditForm": "#noteEditForm",
|
"noteEditForm": "#noteEditForm",
|
||||||
|
"btnPrepopulate": "#btnPrepopulate",
|
||||||
|
"selectPrepopulate": "#selectPrepopulate",
|
||||||
"btnCreateNote": "#btnCreateNote",
|
"btnCreateNote": "#btnCreateNote",
|
||||||
"btnDestroyModal": "#btnDestroyModal",
|
"btnDestroyModal": "#btnDestroyModal",
|
||||||
"saveNote": "#saveNote",
|
"saveNote": "#saveNote",
|
||||||
@@ -22,8 +34,8 @@
|
|||||||
},
|
},
|
||||||
"gridOrder": {
|
"gridOrder": {
|
||||||
"notes": [
|
"notes": [
|
||||||
"title",
|
|
||||||
"createdAt",
|
"createdAt",
|
||||||
|
"title",
|
||||||
"category",
|
"category",
|
||||||
"content",
|
"content",
|
||||||
"dates",
|
"dates",
|
||||||
@@ -35,10 +47,10 @@
|
|||||||
"schemas": {
|
"schemas": {
|
||||||
"note": {
|
"note": {
|
||||||
"id": [ false, "^[0-9]{6}$" ],
|
"id": [ false, "^[0-9]{6}$" ],
|
||||||
"title": [ true, "^(.{1,128})$" ],
|
"title": [ true, 128 ],
|
||||||
"category": [ true, "^(task|random_thought|idea|quote)$" ],
|
"category": [ true, "^(task|random_thought|idea|quote)$" ],
|
||||||
"content": [ false, "^(.{1,1024}$)" ],
|
"content": [ false, 1024 ],
|
||||||
"archive": [ false, "^(on|)$" ],
|
"archive": [ false, "^(on|true|)$" ],
|
||||||
"createdAt": [ false, "^202[1-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$" ],
|
"createdAt": [ false, "^202[1-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$" ],
|
||||||
"updatedAt": [ false, "^202[1-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$" ]
|
"updatedAt": [ false, "^202[1-9]-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$" ]
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/faker.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { env, randomID6n } from './helpers'
|
||||||
|
|
||||||
|
const faker = require('faker')
|
||||||
|
|
||||||
|
export const fakerDB = length => {
|
||||||
|
const dates = [
|
||||||
|
'1/1/11', '1.1.11', '1-1-11', '01/01/11', '01.01.11', '01-01-11',
|
||||||
|
'01/01/2011', '01.01.2011', '01-01-2011', '01/1/2011', '01.1.2011', '01-1-2011',
|
||||||
|
'1/11/2011', '1.11.2011', '1-11-2011', '1/11/11', '1.11.11', '1-11-11',
|
||||||
|
'11/1/11', '11.1.11', '11-1-11', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||||
|
]
|
||||||
|
|
||||||
|
const data = []
|
||||||
|
const cat = env.schemas.note.category[1].slice(2, -2).split('|')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
|
||||||
|
const up = faker.date.between('2021-07-01', '2021-10-07')
|
||||||
|
let pickedDates = []
|
||||||
|
for( let i = 0; i< 4; i++ ) {
|
||||||
|
pickedDates.push( dates[(Math.random() * dates.length) | 0] )
|
||||||
|
}
|
||||||
|
pickedDates = pickedDates.filter( el => el !== '' )
|
||||||
|
const rec = {
|
||||||
|
id: randomID6n(),
|
||||||
|
title: faker.animal.snake(),
|
||||||
|
category: cat[(Math.random() * cat.length) | 0],
|
||||||
|
archive: ['', 'on'][(Math.random() * 2) | 0],
|
||||||
|
content: faker.hacker.phrase() + (pickedDates.length > 0 ? "<br/><strong style='color: green'>Dates:</strong> [ " + pickedDates.join(', ') : '') + " ]",
|
||||||
|
createdAt: faker.date.between('2021-07-06', '2021-10-01'),
|
||||||
|
updatedAt: ['', up][(Math.random() * 2) | 0],
|
||||||
|
}
|
||||||
|
data.push(rec)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log( JSON.stringify( data, null, 2 ))
|
||||||
86
src/helpers.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
export const env = require('./env.json')
|
||||||
|
export const msgBox = document.getElementById(env.querySelectors.console.slice(1))
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {{}}
|
||||||
|
*/
|
||||||
|
const monthsNamesFull = env.lists.monthsFull
|
||||||
|
|
||||||
|
export const randomID6n = () => Math.floor(100000 + Math.random() * 900000)
|
||||||
|
|
||||||
|
export const parseURL = url => new URL(url)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const ucWords = string => {
|
||||||
|
return string.split('_').map(word => word.charAt(0).toUpperCase() + word.substr(1).toLowerCase()).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param date
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const formatDate = date => {
|
||||||
|
const dt = new Date(date)
|
||||||
|
return [monthsNamesFull[dt.getMonth()], dt.getDate(), `, ${dt.getFullYear()}`].join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
export const findDates = str => {
|
||||||
|
const dates = []
|
||||||
|
const regex = new RegExp('[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}', 'mg')
|
||||||
|
|
||||||
|
for (const match of (str || '').matchAll(regex)) {
|
||||||
|
dates.push(match[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export const createNode = (...args) => {
|
||||||
|
const [tag, attrs, content, callback] = args
|
||||||
|
const node = document.createElement(tag)
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(attrs || [])) {
|
||||||
|
node.setAttribute(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.innerHTML = content || ''
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
export const destroyNode = (node) => {
|
||||||
|
|
||||||
|
if (!(node instanceof Node) && typeof node === 'string') {
|
||||||
|
node = document.querySelector(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
node.remove()
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1
src/images/archive_48dp.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#fcc343"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zm-8.89 11.92L6.5 12H10v-2h4v2h3.5l-5.15 5.15c-.19.19-.51.19-.7 0zM5.12 5l.81-1h12l.94 1H5.12z"/></svg>
|
||||||
|
After Width: | Height: | Size: 418 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#999"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zm-8.89 11.92L6.5 12H10v-2h4v2h3.5l-5.15 5.15c-.19.19-.51.19-.7 0zM5.12 5l.81-1h12l.94 1H5.12z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 415 B |
1
src/images/delete_48dp.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#d6676d"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v10zM18 4h-2.5l-.71-.71c-.18-.18-.44-.29-.7-.29H9.91c-.26 0-.52.11-.7.29L8.5 4H6c-.55 0-1 .45-1 1s.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1z"/></svg>
|
||||||
|
After Width: | Height: | Size: 368 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#999"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v10zM18 4h-2.5l-.71-.71c-.18-.18-.44-.29-.7-.29H9.91c-.26 0-.52.11-.7.29L8.5 4H6c-.55 0-1 .45-1 1s.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 365 B |
1
src/images/edit_48dp.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#abcdef"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM21.41 6.34l-3.75-3.75-2.53 2.54 3.75 3.75 2.53-2.54z"/></svg>
|
||||||
|
After Width: | Height: | Size: 259 B |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#999"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM21.41 6.34l-3.75-3.75-2.53 2.54 3.75 3.75 2.53-2.54z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 256 B |
1
src/images/unarchive_48dp.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#bada55"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18.71 3H5.29L3 5.79V21h18V5.79L18.71 3zM14 15v2h-4v-2H6.5L12 9.5l5.5 5.5H14zM5.12 5l.81-1h12l.94 1H5.12z"/></svg>
|
||||||
|
After Width: | Height: | Size: 264 B |
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="appConsole" class="uk-container"></div>
|
|
||||||
<div id="app" class="uk-container"></div>
|
<div id="appConsole" class="uk-container uk-margin-remove uk-background-muted uk-text-small"></div>
|
||||||
|
<div id="app" class="uk-container"></div>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
47
src/index.js
@@ -1,50 +1,11 @@
|
|||||||
import env from './env.json'
|
import { env } from './helpers'
|
||||||
import './styles/uikit.min.css'
|
import './styles/uikit.min.css'
|
||||||
import './styles/main.css'
|
import './styles/main.css'
|
||||||
import DOMController from './modules/DOMController'
|
import DOMController from './modules/DOMController'
|
||||||
|
import RouteController from './modules/RouteController'
|
||||||
//localStorage.clear()
|
|
||||||
|
|
||||||
window.env = env
|
window.env = env
|
||||||
const monthsNamesFull = window.env.lists.monthsFull
|
|
||||||
|
|
||||||
/**
|
const app = document.getElementById(env.querySelectors.root.slice(1))
|
||||||
*
|
|
||||||
* @param str
|
|
||||||
* @returns {*[]}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
window.___findDates = str => {
|
|
||||||
const dates = []
|
|
||||||
const regex = new RegExp('[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}', 'mg')
|
|
||||||
|
|
||||||
for (const match of (str || '').matchAll(regex)) {
|
new DOMController(app, new RouteController())
|
||||||
dates.push(match[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
return dates
|
|
||||||
}
|
|
||||||
|
|
||||||
window.___formatDate = date => {
|
|
||||||
const dt = new Date(date)
|
|
||||||
return [monthsNamesFull[dt.getMonth()], dt.getDate(), `, ${dt.getFullYear()}`].join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = document.getElementById('app')
|
|
||||||
|
|
||||||
const view = new DOMController(app)
|
|
||||||
|
|
||||||
//console.log(dt, dt.match(/^(((2000|2400|2800|((19|2[0-9])(0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T([01][0-9]|[2][0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$/) )
|
|
||||||
|
|
||||||
//localStorage.clear()
|
|
||||||
|
|
||||||
/*NotesAPI.saveNote({
|
|
||||||
//id: 441512,
|
|
||||||
title: 'Note 1234',
|
|
||||||
category: 'category 1234',
|
|
||||||
content: 'contents 1234',
|
|
||||||
updatedAt: null
|
|
||||||
})*/
|
|
||||||
|
|
||||||
//NotesAPI.deleteNote(441512)
|
|
||||||
//console.log(NotesAPI.getNotes())
|
|
||||||
28
src/modules/AlertController.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { createNode, msgBox } from '../helpers'
|
||||||
|
|
||||||
|
export default class AlertController {
|
||||||
|
|
||||||
|
static showError () {
|
||||||
|
this.displayDialog('Error occurred :(', 3000, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param msg
|
||||||
|
* @param fadeOut
|
||||||
|
* @param type
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
static displayDialog (msg = 'Alert without message', fadeOut = 0, type = 0, parent = null) {
|
||||||
|
const alertTypes = window.env.alertTypes
|
||||||
|
const dialog = createNode('div', { class: `uk-alert-${alertTypes[type]} uk-padding-small` }, msg)
|
||||||
|
parent instanceof Element ? parent.appendChild(alert) : msgBox.appendChild(dialog)
|
||||||
|
|
||||||
|
if (fadeOut > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
dialog.remove()
|
||||||
|
}, fadeOut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,56 +1,179 @@
|
|||||||
import Templates from '../templates/Views'
|
import Templates from '../templates/Views'
|
||||||
|
import { createNode, destroyNode, findDates, formatDate, parseURL, replaceURLPathname, ucWords } from '../helpers'
|
||||||
import NotesAPI from './NotesAPI'
|
import NotesAPI from './NotesAPI'
|
||||||
|
import ValidationController from './ValidationController'
|
||||||
|
import Alert from './AlertController'
|
||||||
|
|
||||||
export default class DOMController {
|
export default class DOMController {
|
||||||
|
|
||||||
constructor (root) {
|
constructor (root, routes) {
|
||||||
|
this.routes = routes
|
||||||
this.root = root
|
this.root = root
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.root.innerHTML = Templates.grid
|
this.root.innerHTML = Templates.grid
|
||||||
|
this.renderRoutes()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
DOMController.alert(e.toString(), 0, 3)
|
Alert.displayDialog(e.toString(), 0, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.btnCreateNote = this.root.querySelector(window.env.querySelectors.btnCreateNote)
|
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.createNotesGrid(window.env.prePopulateAmount || 0)
|
||||||
|
|
||||||
this.listener([
|
this.listener([
|
||||||
[
|
[this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), { legend: 'Create Note' }],
|
||||||
this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), {
|
[this.btnPrepopulate, 'click', (this.btnPrepopulate.dataset.action || 'showError')],
|
||||||
legend: 'Create Note',
|
|
||||||
}],
|
|
||||||
])
|
])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param msg
|
|
||||||
* @param fadeOut
|
|
||||||
* @param type
|
|
||||||
*/
|
*/
|
||||||
static alert (msg = '', fadeOut = 0, type = 0) {
|
renderRoutes () {
|
||||||
const alertTypes = window.env.alertTypes
|
|
||||||
const alert = this.createNode('div', { class: `uk-alert-${alertTypes[type]}`, 'uk-alert': null },
|
for (const [route, name] of Object.entries(this.routes.routes)) {
|
||||||
`<a class="uk-alert-close" uk-close></a>\n` + msg)
|
|
||||||
alert.style.display = 'block'
|
const tab = createNode('li')
|
||||||
document.getElementById(window.env.querySelectors.console.slice(1)).appendChild(alert)
|
tab.classList.add('nav-tab')
|
||||||
|
|
||||||
|
if (route === '/') {
|
||||||
|
tab.classList.add('initial')
|
||||||
|
tab.classList.add('uk-active')
|
||||||
}
|
}
|
||||||
|
|
||||||
createNotesGrid (len, archived = false) {
|
tab.innerHTML = `<a href="${route}">${name}</a>`
|
||||||
|
const anchor = tab.querySelector('a')
|
||||||
|
this.routes.navbar.appendChild(tab)
|
||||||
|
|
||||||
const notes = NotesAPI.getNotesSanitized(len, archived)
|
this.listener([anchor, 'click', 'createNotesGridByRoute', anchor])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (notes.length === 0) { return }
|
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 nodeToListen = []
|
|
||||||
const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody')
|
const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody')
|
||||||
notesGrid.innerHTML = ''
|
notesGrid.innerHTML = ''
|
||||||
|
|
||||||
|
const notes = NotesAPI.getNotesSanitized(len, archived)
|
||||||
|
if (notes.length === 0) { return }
|
||||||
|
|
||||||
notes.forEach(el => {
|
notes.forEach(el => {
|
||||||
|
|
||||||
const row = DOMController.createNode('tr')
|
const row = createNode('tr')
|
||||||
row.dataset.id = el.id
|
row.dataset.id = el.id
|
||||||
|
|
||||||
window.env.gridOrder.notes.forEach(key => {
|
window.env.gridOrder.notes.forEach(key => {
|
||||||
@@ -59,34 +182,37 @@ export default class DOMController {
|
|||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'createdAt':
|
case 'createdAt':
|
||||||
value = ___formatDate(value)
|
value = formatDate(value)
|
||||||
break
|
break
|
||||||
case 'content':
|
case 'category':
|
||||||
attrs.class = 'uk-text-truncate'
|
value = ucWords(value)
|
||||||
|
break
|
||||||
|
case 'title':
|
||||||
|
attrs.class = 'uk-text-bold'
|
||||||
break
|
break
|
||||||
case 'dates':
|
case 'dates':
|
||||||
attrs.class = 'uk-text-truncate'
|
attrs.style = 'font-size: 12px'
|
||||||
value = ___findDates(el.content).join(', ')
|
value = findDates(el.content).join(', ')
|
||||||
break
|
break
|
||||||
case 'edit' :
|
case 'edit' :
|
||||||
attrs.class = 'icon icon-edit grid-control'
|
attrs.class = 'grid-control icon icon-edit'
|
||||||
attrs['data-action'] = 'getEditForm'
|
attrs['data-action'] = 'getEditForm'
|
||||||
break
|
break
|
||||||
case 'archive' :
|
case 'archive' :
|
||||||
attrs.class = 'icon icon-archive grid-control'
|
attrs.class = 'grid-control icon ' + (value === 'on' ? 'icon-unarchive' : 'icon-archive')
|
||||||
attrs['data-action'] = 'toArchive'
|
attrs['data-action'] = value === 'on' ? 'noteFromArchive' : 'noteToArchive'
|
||||||
|
value = ''
|
||||||
break
|
break
|
||||||
case 'delete' :
|
case 'delete' :
|
||||||
attrs.class = 'icon icon-delete grid-control'
|
attrs.class = 'icon icon-delete grid-control'
|
||||||
attrs['data-action'] = 'toTrash'
|
attrs['data-action'] = 'noteToTrash'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
const td = DOMController.createNode('td', attrs, value)
|
const td = createNode('td', attrs, value)
|
||||||
|
|
||||||
if (key.match(/^(edit|archive|delete)$/)) {
|
if (key.match(/^(edit|archive|delete)$/)) {
|
||||||
nodeToListen.push(td)
|
this.listener([td, 'click', td.dataset.action, td])
|
||||||
this.listener([td, 'click', td.dataset.action])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
row.appendChild(td)
|
row.appendChild(td)
|
||||||
@@ -96,89 +222,6 @@ export default class DOMController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
showError () {
|
|
||||||
console.error('Error occurred :(')
|
|
||||||
}
|
|
||||||
|
|
||||||
static createNode (...args) {
|
|
||||||
const [tag, attrs, content, callback] = args
|
|
||||||
const node = document.createElement(tag)
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(attrs || [])) {
|
|
||||||
node.setAttribute(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
node.innerHTML = content || ''
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyNode (selector) {
|
|
||||||
|
|
||||||
if (!(selector instanceof Node) && typeof selector === 'string') {
|
|
||||||
selector = document.querySelector(selector)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
selector.remove()
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getEditForm (args = {}) {
|
|
||||||
|
|
||||||
const node = DOMController.createNode('div', { class: window.env.querySelectors.modal.slice(1) },
|
|
||||||
Templates.templates.editForm)
|
|
||||||
|
|
||||||
if (args.legend) {
|
|
||||||
node.querySelector('legend').innerHTML = args.legend
|
|
||||||
}
|
|
||||||
|
|
||||||
this.root.appendChild(node)
|
|
||||||
node.style.display = 'block'
|
|
||||||
|
|
||||||
this.listener([
|
|
||||||
[node.querySelector(window.env.querySelectors.btnDestroyModal), 'click', 'destroyNode', node],
|
|
||||||
[node.querySelector('form'), 'submit', NotesAPI.saveNote],
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
listener (els) {
|
|
||||||
els[0] instanceof Element ? this.on(els) : els.forEach(el => this.on(el))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param args //selector, event, method, params
|
|
||||||
*/
|
|
||||||
on (args) {
|
|
||||||
|
|
||||||
const [selector, event, method] = args
|
|
||||||
|
|
||||||
if (!(selector instanceof Element)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
selector.addEventListener(event, evt => {
|
|
||||||
const params = args[3] || {}
|
|
||||||
|
|
||||||
if (evt.type === 'submit') {
|
|
||||||
evt.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this[method] === 'function') {
|
|
||||||
this[method](params)
|
|
||||||
} else if (typeof method === 'function') {
|
|
||||||
method(params)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param selector
|
* @param selector
|
||||||
@@ -191,7 +234,119 @@ export default class DOMController {
|
|||||||
for (let key of formData.keys()) {
|
for (let key of formData.keys()) {
|
||||||
data[key] = formData.get(key)
|
data[key] = formData.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,39 @@
|
|||||||
import DOMController from './DOMController'
|
import DOMController from './DOMController'
|
||||||
import Validation from './ValidationController'
|
import Validation from './ValidationController'
|
||||||
|
import { fakerDB } from '../faker'
|
||||||
|
import Alert from './AlertController'
|
||||||
|
|
||||||
export default class NotesAPI{
|
export default class NotesAPI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {any}
|
||||||
|
* @param save
|
||||||
|
*/
|
||||||
|
static getStorage (save = true) {
|
||||||
|
const key = window.env.localStorageKey
|
||||||
|
let storage = localStorage.getItem(key)
|
||||||
|
|
||||||
|
if (storage === null) {
|
||||||
|
storage = JSON.stringify(fakerDB(env.fakerAmount)) || '[]'
|
||||||
|
if (save) localStorage.setItem(key, storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(storage)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param n
|
* @param n
|
||||||
* @param archive
|
* @param archive
|
||||||
* @param key
|
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
static getNotes (n, archive = false, key = (window.env.localStorageKey || '') ) {
|
static getNotes (n = 0, archive = false) {
|
||||||
let notes = JSON.parse(localStorage.getItem(key) || '[]')
|
|
||||||
|
|
||||||
if( archive === false ){
|
let notes = NotesAPI.getStorage()
|
||||||
notes = notes.filter( el => el.archive !== 'on' )
|
|
||||||
|
if (typeof archive === 'boolean') {
|
||||||
|
notes = notes.filter(el => (archive ? el.archive === 'on' : el.archive !== 'on'))
|
||||||
}
|
}
|
||||||
|
|
||||||
notes.sort((a, b) => {
|
notes.sort((a, b) => {
|
||||||
@@ -24,36 +43,46 @@ export default class NotesAPI{
|
|||||||
return n > 0 ? notes.slice(0, n) : notes
|
return n > 0 ? notes.slice(0, n) : notes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
static getNoteByID (id) {
|
||||||
|
return NotesAPI.getStorage().filter(el => el.id === id)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param n
|
* @param n
|
||||||
* @param archive
|
* @param archive
|
||||||
* @returns {*[]|[]}
|
* @returns {*[]|[]}
|
||||||
*/
|
*/
|
||||||
static getNotesSanitized( n = 0, archive = false){
|
static getNotesSanitized (n = 0, archive = false) {
|
||||||
let notes = NotesAPI.getNotes(0, archive)
|
let notes = NotesAPI.getNotes(0, archive)
|
||||||
notes = Validation.allAgainstSchema(notes, window.env.schemas.note )
|
|
||||||
|
notes = Validation.allAgainstSchema(notes, window.env.schemas.note)
|
||||||
return n > 0 ? notes.slice(0, n) : notes
|
return n > 0 ? notes.slice(0, n) : notes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param noteToStore
|
* @param noteToStore
|
||||||
|
* @param action
|
||||||
*/
|
*/
|
||||||
static saveNote (noteToStore = null) {
|
static saveNote (noteToStore = null, action = 'modal') {
|
||||||
|
|
||||||
if( Object.keys(noteToStore).length === 0 ){
|
if (Object.keys(noteToStore).length === 0) {
|
||||||
noteToStore = DOMController.getFormData()
|
noteToStore = DOMController.getFormData()
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValid = Validation.againstSchema( noteToStore, window.env.schemas.note )
|
const isValid = Validation.againstSchema(noteToStore, window.env.schemas.note)
|
||||||
|
|
||||||
if( isValid.status === true ){
|
if (isValid.status === true) {
|
||||||
|
|
||||||
noteToStore = isValid.data
|
noteToStore = isValid.data
|
||||||
|
const notes = NotesAPI.getNotes(0, null)
|
||||||
const notes = NotesAPI.getNotes()
|
const noteExists = notes.find(note => note.id === +noteToStore.id)
|
||||||
const noteExists = notes.find(note => note.id === noteToStore.id)
|
|
||||||
|
|
||||||
if (noteExists) {
|
if (noteExists) {
|
||||||
noteExists.title = noteToStore.title
|
noteExists.title = noteToStore.title
|
||||||
@@ -67,12 +96,18 @@ export default class NotesAPI{
|
|||||||
noteToStore.createdAt = new Date().toISOString()
|
noteToStore.createdAt = new Date().toISOString()
|
||||||
notes.push(noteToStore)
|
notes.push(noteToStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
NotesAPI.saveNotesToStorage(notes)
|
NotesAPI.saveNotesToStorage(notes)
|
||||||
} else {
|
|
||||||
DOMController.alert( 'Could save the note! ' + isValid.errors.join(', '),0, 2 )
|
if (action === 'modal') {
|
||||||
|
//TODO: Need to implement in different way
|
||||||
|
document.querySelector(window.env.querySelectors.noteEditForm).closest('.modal').remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Alert.displayDialog('Could save the note! ' + isValid.errors.join(', '), 6000, 2) //, document.getElementById('modalMsgBox')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(NotesAPI.getNotes())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,11 +115,35 @@ export default class NotesAPI{
|
|||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
static deleteNote (id) {
|
static deleteNote (id) {
|
||||||
const notes = NotesAPI.getNotes()
|
const notes = NotesAPI.getStorage()
|
||||||
const filteredNotesByID = notes.filter(note => note.id !== id)
|
const filteredNotesByID = notes.filter(note => note.id !== id)
|
||||||
NotesAPI.saveNotesToStorage(filteredNotesByID)
|
NotesAPI.saveNotesToStorage(filteredNotesByID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unArchiveNote (id) {
|
||||||
|
this.archiveNote(id, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
static archiveNote (id, value = 'on') {
|
||||||
|
|
||||||
|
if (value !== '') value = 'on'
|
||||||
|
|
||||||
|
let note = NotesAPI.getNoteByID(id)
|
||||||
|
|
||||||
|
if (note.length > 0 && note[0].archive !== value) {
|
||||||
|
note[0].archive = value
|
||||||
|
NotesAPI.saveNote(note[0], 'archive')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param notes
|
* @param notes
|
||||||
@@ -93,4 +152,32 @@ export default class NotesAPI{
|
|||||||
static saveNotesToStorage (notes, key = window.env.localStorageKey) {
|
static saveNotesToStorage (notes, key = window.env.localStorageKey) {
|
||||||
localStorage.setItem(key, JSON.stringify(notes, null, 2))
|
localStorage.setItem(key, JSON.stringify(notes, null, 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {{}}
|
||||||
|
*/
|
||||||
|
static getAnalytics () {
|
||||||
|
|
||||||
|
const analytics = {}
|
||||||
|
const data = NotesAPI.getStorage()
|
||||||
|
const cats = [...new Set(data.map(item => item.category))]
|
||||||
|
|
||||||
|
if (typeof cats[0] !== 'undefined') {
|
||||||
|
const isArchive = data.map(item => {
|
||||||
|
const container = {}
|
||||||
|
container[item.category] = item.archive
|
||||||
|
return container
|
||||||
|
})
|
||||||
|
|
||||||
|
cats.forEach((cat, i) => {
|
||||||
|
analytics[cat] = {
|
||||||
|
Active: isArchive.filter(el => el[cat] === 'on').length,
|
||||||
|
Archived: isArchive.filter(el => el[cat] == '').length,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return analytics
|
||||||
|
}
|
||||||
}
|
}
|
||||||
55
src/modules/RouteController.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import Views from '../templates/Views'
|
||||||
|
|
||||||
|
export default class RouteController {
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
document.addEventListener('DOMContentLoaded', e => {
|
||||||
|
this._navigation()
|
||||||
|
this._history()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get routes () {
|
||||||
|
return window.env.routes
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get navbar () {
|
||||||
|
return document.getElementById(window.env.querySelectors.navTabs.slice(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
get page404(){
|
||||||
|
document.body.innerHTML = Views.templates.notFound
|
||||||
|
setTimeout( () => {
|
||||||
|
window.location.assign('/')
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_navigation(){
|
||||||
|
|
||||||
|
if( Object.keys(this.routes).filter( path => window.location.pathname === path ).length === 0 ){
|
||||||
|
this.page404
|
||||||
|
} else{
|
||||||
|
document.querySelector( `a[href='${window.location.pathname}']` ).click()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_history(){
|
||||||
|
window.addEventListener('popstate', event => {
|
||||||
|
try {
|
||||||
|
document.querySelector('.uk-active').classList.remove('uk-active')
|
||||||
|
document.querySelector(window.env.querySelectors.navTabs + ` a[href='${event.target.location.pathname}']`).closest('.nav-tab').classList.add('uk-active')
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,14 +1,21 @@
|
|||||||
|
import { env } from '../helpers'
|
||||||
|
|
||||||
export default class ValidationController {
|
export default class ValidationController {
|
||||||
|
|
||||||
|
/**
|
||||||
static allAgainstSchema(array, schema) {
|
*
|
||||||
|
* @param array
|
||||||
|
* @param schema
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
static allAgainstSchema (array, schema) {
|
||||||
const objs = []
|
const objs = []
|
||||||
|
|
||||||
array.forEach( obj => {
|
array.forEach(obj => {
|
||||||
const res = this.againstSchema(obj, schema)
|
const res = this.againstSchema(obj, schema)
|
||||||
|
|
||||||
if( res.status === true ){
|
if (res.status === true) {
|
||||||
objs.push( res.data )
|
objs.push(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -38,6 +45,8 @@ export default class ValidationController {
|
|||||||
const field = (obj[key] || '').toString().trim()
|
const field = (obj[key] || '').toString().trim()
|
||||||
|
|
||||||
if (field !== '') {
|
if (field !== '') {
|
||||||
|
|
||||||
|
if (typeof value[1] !== 'number') {
|
||||||
const regex = new RegExp(value[1].toString(), 'i')
|
const regex = new RegExp(value[1].toString(), 'i')
|
||||||
|
|
||||||
if (regex.exec(field)) {
|
if (regex.exec(field)) {
|
||||||
@@ -50,6 +59,9 @@ export default class ValidationController {
|
|||||||
out.error_text.push(`${key} is required`)
|
out.error_text.push(`${key} is required`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (field.length > 0) {
|
||||||
|
out.data[key] = field.slice(0, value[1])
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -61,9 +73,7 @@ export default class ValidationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
out.error_text.push(`Critical error! ` + e.toString())
|
||||||
console.error(e.toString())
|
|
||||||
out.error_text.push(`Critical error! See console`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.status = !(out.error_text.length > 0 && out.errors.length > 0)
|
out.status = !(out.error_text.length > 0 && out.errors.length > 0)
|
||||||
@@ -71,4 +81,18 @@ export default class ValidationController {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
static lengthOnKeyUp (node) {
|
||||||
|
const logger = document.querySelector(`[for='${node.id}'] > span`)
|
||||||
|
logger.innerHTML = `${node.value.length}/${env.schemas.note[node.getAttribute('name')][1]}`
|
||||||
|
|
||||||
|
node.value.length > env.schemas.note[node.getAttribute('name')][1]
|
||||||
|
? logger.classList.add('uk-text-danger')
|
||||||
|
: logger.classList.remove(
|
||||||
|
'uk-text-danger')
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,29 @@
|
|||||||
body{
|
body{
|
||||||
background-color: aliceblue;
|
margin-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#as-navbar{
|
||||||
|
overflow: hidden; position: fixed; left: 0; top: 0; width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.uk-text-truncate{
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
#appConsole{
|
||||||
|
display: flex;
|
||||||
|
z-index: 2;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: Consolas, monospace;
|
||||||
|
box-sizing: content-box;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon{
|
.icon{
|
||||||
@@ -10,14 +34,39 @@ body{
|
|||||||
|
|
||||||
.icon.grid-control{
|
.icon.grid-control{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
filter: opacity(.5);
|
||||||
transition: transform 250ms;
|
transition: transform 250ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon.grid-control:hover{
|
.icon.grid-control:hover{
|
||||||
filter: brightness(0.1) sepia(1) hue-rotate(90deg) saturate(1);
|
filter: opacity(1);
|
||||||
transform: translateY(5%);
|
}
|
||||||
|
.icon.grid-control:not(.icon-unarchive):hover{
|
||||||
|
transform: translateY(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon.grid-control.icon-unarchive:hover{
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon.as-gray{
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInAnimation {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.as-removing{
|
||||||
|
animation: fadeInAnimation ease 1s;
|
||||||
|
animation-iteration-count: 1;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
.icon.as-transition:hover{
|
.icon.as-transition:hover{
|
||||||
transition: ease-in-out 100ms;
|
transition: ease-in-out 100ms;
|
||||||
@@ -25,17 +74,22 @@ body{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon-edit{
|
.icon-edit{
|
||||||
background-image: url(../images/edit_black_48dp.svg)
|
background-image: url(../images/edit_48dp.svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-archive{
|
.icon-archive{
|
||||||
background-image: url(../images/archive_black_48dp.svg)
|
background-image: url(../images/archive_48dp.svg)
|
||||||
|
}
|
||||||
|
.icon-unarchive{
|
||||||
|
background-image: url(../images/unarchive_48dp.svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-delete{
|
.icon-delete{
|
||||||
background-image: url(../images/delete_black_48dp.svg)
|
background-image: url(../images/delete_48dp.svg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* The Modal (background) */
|
/* The Modal (background) */
|
||||||
#noteEditFormModal, .modal {
|
#noteEditFormModal, .modal {
|
||||||
display: none; /* Hidden by default */
|
display: none; /* Hidden by default */
|
||||||
@@ -77,3 +131,10 @@ body{
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[readonly]{
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
15
src/templates/404.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export default `<div class="uk-container uk-text-center">
|
||||||
|
<div class="uk-container">
|
||||||
|
<ul id="asSwitcher"
|
||||||
|
class="uk-subnav uk-subnav-divider uk-margin-large-bottom uk-flex-center">
|
||||||
|
<li class="nav-tab"><a href="/all">All Notes</a></li>
|
||||||
|
<li class="nav-tab initial uk-active"><a href="/">Recent Notes</a></li>
|
||||||
|
<li class="nav-tab"><a href="/archive">Archived Notes</a></li>
|
||||||
|
<li class="nav-tab"><a href="/analytics">Analytics</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class=" uk-text-danger">404 - NOT FOUND</h1>
|
||||||
|
<div class="uk-text-large uk-text-muted uk-text-bold">U will be redirected soon</div>
|
||||||
|
|
||||||
|
</div>`
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import grid from './grid'
|
import grid from './grid'
|
||||||
import editForm from './editForm'
|
import editForm from './editForm'
|
||||||
import gridRow from './gridRow'
|
import _404 from './404'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
templates: {
|
templates: {
|
||||||
|
notFound: _404,
|
||||||
editForm: editForm,
|
editForm: editForm,
|
||||||
gridRow: gridRow
|
|
||||||
},
|
},
|
||||||
grid: grid
|
grid: grid
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,24 @@
|
|||||||
|
import { env } from '../helpers'
|
||||||
|
|
||||||
export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-large modal-content" style="background: ghostwhite">
|
export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-large modal-content" style="background: ghostwhite">
|
||||||
<fieldset class="uk-fieldset">
|
<fieldset class="uk-fieldset">
|
||||||
<legend class="uk-legend">Edit Note</legend>
|
<legend class="uk-legend">
|
||||||
|
<span class="uk-float-right uk-text-small">
|
||||||
|
created: <input class="" type="text" name="createdAt" readonly="readonly" value="" placeholder="0000-00-00">
|
||||||
|
updated: <input type="text" name="updatedAt" readonly="readonly" value="" placeholder="0000-00-00">
|
||||||
|
</span>
|
||||||
|
<span>Edit Note #<input class="uk-text-large" type="text" name="id" readonly="readonly" value="" placeholder="0"></span>
|
||||||
|
</legend>
|
||||||
|
<div id="modalMsgBox"></div>
|
||||||
|
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<label class="uk-form-label" for="formTitle">Title</label>
|
<label class="uk-form-label" for="formTitle">Title <span></span></label>
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
<input id="formTitle" name="title" class="uk-input" type="text" maxlength="127" placeholder="Add a title...">
|
<input id="formTitle" name="title" class="uk-input" type="text" maxlength="${env.schemas.note.title[1]}" placeholder="Add a title...">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-margin" style="background: aquamarine">
|
<div class="uk-margin">
|
||||||
<label class="uk-form-label uk-float-right" for="formCategory">Category</label>
|
<label class="uk-form-label uk-float-right" for="formCategory">Category</label>
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
<select id="formCategory" name="category" class="uk-select">
|
<select id="formCategory" name="category" class="uk-select">
|
||||||
@@ -24,7 +33,8 @@ export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-larg
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<textarea name="content" class="uk-textarea" rows="6" placeholder="Description..."></textarea>
|
<label class="uk-form-label" for="noteContentInput">Description <span></span></label>
|
||||||
|
<textarea id="noteContentInput" name="content" class="uk-textarea" rows="6" maxlength="${env.schemas.note.content[1]}" placeholder="Description..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
|
|||||||
@@ -1,21 +1,60 @@
|
|||||||
export default `<table id="notesGrid" class="uk-table uk-table-small uk-table-responsive1 uk-table-striped uk-table-hover uk-table-divider">
|
export default `
|
||||||
|
<nav id="as-navbar" class="uk-background-secondary">
|
||||||
|
<div class="uk-container">
|
||||||
|
<ul id="asSwitcher" class="uk-subnav uk-subnav-pill uk-margin-small-top uk-margin-small-bottom uk-padding-small"></ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<table id="notesGrid" class="uk-table uk-table-small uk-table-striped uk-table-hover uk-table-divider">
|
||||||
|
<col width="156">
|
||||||
|
<col width="250">
|
||||||
|
<col width="150">
|
||||||
|
<col>
|
||||||
|
<col width="180">
|
||||||
|
<col width="32">
|
||||||
|
<col width="32">
|
||||||
|
<col width="32">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Title</th>
|
|
||||||
<th>Created</th>
|
<th>Created</th>
|
||||||
|
<th>Title</th>
|
||||||
<th>Category</th>
|
<th>Category</th>
|
||||||
<th>Content</th>
|
<th>Content</th>
|
||||||
<th>Dates</th>
|
<th>Dates</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th class="icon icon-archive"></th>
|
<th class="icon icon-archive as-gray"></th>
|
||||||
<th class="icon icon-delete"></th>
|
<th class="icon icon-delete as-gray"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table id="analyticsGrid" class="uk-table uk-table-small uk-table-striped uk-table-hover uk-table-divider uk-hidden">
|
||||||
|
<col width="156">
|
||||||
|
<col width="250">
|
||||||
|
<col width="150">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="8" class="uk-margin uk-text-right">
|
<th>Note Category</th>
|
||||||
<button id="btnCreateNote" class="uk-button uk-button-primary" data-action="getEditForm">Create Note</button>
|
<th>Active</th>
|
||||||
</td>
|
<th>Archived</th>
|
||||||
</tr>
|
</tr>
|
||||||
</table>`
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="uk-margin" uk-margin>
|
||||||
|
<div class="uk-margin-large-top">
|
||||||
|
<button id="btnCreateNote" class="uk-button uk-button-primary uk-text-bold uk-border-rounded uk-float-right" data-action="getEditForm">Create Note</button>
|
||||||
|
<select id="selectPrepopulate" class="uk-input uk-form-width-medium">
|
||||||
|
<option value="" selected disabled>Select amount to...</option>
|
||||||
|
<option>7</option>
|
||||||
|
<option>15</option>
|
||||||
|
<option>25</option>
|
||||||
|
<option>44</option>
|
||||||
|
</select>
|
||||||
|
<button id="btnPrepopulate" class="uk-button uk-button-danger uk-text-bold uk-border-rounded" data-action="prePopulate">Pre-populate</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
export default `
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td class="uk-text-right">
|
|
||||||
<div class="uk-button-group">
|
|
||||||
<button class="uk-button uk-button-default icon icon-archive"></button>
|
|
||||||
<button class="uk-button uk-button-default"></button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
`
|
|
||||||
@@ -1,91 +1,149 @@
|
|||||||
|
const os = require('os')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const glob = require('glob')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
|
||||||
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
|
|
||||||
const TerserPlugin = require("terser-webpack-plugin")
|
|
||||||
|
|
||||||
let mode = 'development', target = 'web'
|
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
|
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
|
||||||
|
const svgToMiniDataURI = require('mini-svg-data-uri')
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
||||||
|
const CSSMQPackerPlugin = require('css-mqpacker-webpack-plugin')
|
||||||
|
|
||||||
|
let mode = 'development', target = 'web', isProd = false
|
||||||
|
const babelExclude = /(node_modules|bower_components)/
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
mode = 'development'
|
mode = 'development'
|
||||||
target = 'browserslist'
|
target = 'browserslist'
|
||||||
|
isProd = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV !== 'production'
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
new CleanWebpackPlugin(),
|
new CleanWebpackPlugin(),
|
||||||
new MiniCssExtractPlugin(),
|
new MiniCssExtractPlugin(),
|
||||||
|
new PurgeCSSPlugin({
|
||||||
|
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
|
||||||
|
//only: ['bundle', 'vendor']
|
||||||
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: './src/index.html',
|
template: './src/index.html',
|
||||||
inject: 'body'
|
inject: 'body',
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
console.log( process.env.SOURCE_MAP_ENV )
|
const stylesLoaders = loader => {
|
||||||
|
const loaders = [
|
||||||
|
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
|
||||||
|
'css-loader',
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
ident: 'postcss',
|
||||||
|
plugins: [
|
||||||
|
require('postcss-preset-env'),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
if (loader) {
|
||||||
|
loaders.push(loader)
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaders
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: mode,
|
mode: mode,
|
||||||
target: target,
|
target: target,
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
test: /\.(jpe?g|png|gif|svg)$/i,
|
|
||||||
type: "asset",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.css$/i,
|
test: /\.css$/i,
|
||||||
use: [
|
use: stylesLoaders(),
|
||||||
{
|
|
||||||
loader: MiniCssExtractPlugin.loader,
|
|
||||||
// This is required for asset imports in CSS, such as url()
|
|
||||||
options: { publicPath: '' },
|
|
||||||
},
|
|
||||||
'css-loader',
|
|
||||||
///"postcss-loader",
|
|
||||||
// according to the docs, sass-loader should be at the bottom, which
|
|
||||||
// loads it first to avoid prefixes in your sourcemaps and other issues.
|
|
||||||
//"sass-loader",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.s[ac]ss$/i,
|
||||||
exclude: /node_modules/,
|
use: stylesLoaders('sass-loader'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.m?js$/i,
|
||||||
|
exclude: babelExclude,
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-env',
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
'@babel/plugin-proposal-class-properties',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(jpe?g|webp|png|gif|svg)$/i,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(svg)$/i,
|
||||||
|
type: 'asset/inline',
|
||||||
|
generator: {
|
||||||
|
dataUrl: content => {
|
||||||
|
content = content.toString()
|
||||||
|
return svgToMiniDataURI(content)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
entry: { app: './src/index' },
|
//
|
||||||
|
entry: { faker: './src/faker', app: './src/index' },
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
filename: '[name].[hash].js',
|
filename: '[name].[hash].js',
|
||||||
},
|
},
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
devtool: mode === 'production' ? 'source-map' : ( process.env.SOURCE_MAP_ENV ? 'source-map' : false ),
|
devtool: mode !== 'production' ? (process.env.SOURCE_MAP_ENV ? 'eval-cheap-module-source-map' : false) : false,
|
||||||
|
// devtool: 'source-map',
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: true,
|
minimize: isProd,
|
||||||
minimizer: [
|
splitChunks: {
|
||||||
new TerserPlugin({
|
chunks: 'all',
|
||||||
extractComments: true,
|
|
||||||
terserOptions: {
|
|
||||||
ecma: 5,
|
|
||||||
compress: {
|
|
||||||
drop_console: mode === 'production',
|
|
||||||
},
|
},
|
||||||
|
minimizer: [
|
||||||
|
`...`,
|
||||||
|
new CssMinimizerPlugin({
|
||||||
|
parallel: os.cpus().length,
|
||||||
|
minimizerOptions: {
|
||||||
|
preset: [
|
||||||
|
'default', // for advanced need to run `npm i -D cssnano-preset-advanced`
|
||||||
|
{
|
||||||
|
discardComments: { removeAll: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new CSSMQPackerPlugin({
|
||||||
|
test: /\.css$/i,
|
||||||
|
sort: true,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
static: path.resolve(__dirname, 'dist'),
|
static: path.resolve(__dirname, 'dist'),
|
||||||
compress: true,
|
compress: true,
|
||||||
port: 1976,
|
port: 1976,
|
||||||
|
//disableHostCheck: true,
|
||||||
|
allowedHosts: [
|
||||||
|
'dev.amok.space',
|
||||||
|
],
|
||||||
liveReload: true,
|
liveReload: true,
|
||||||
/*overlay: {
|
historyApiFallback: true,
|
||||||
warnings: true,
|
|
||||||
errors: true,
|
|
||||||
}*/
|
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||