diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/Dropdown.js b/Dropdown.js deleted file mode 100644 index 9f46766..0000000 --- a/Dropdown.js +++ /dev/null @@ -1,85 +0,0 @@ -(function () { - 'use strict'; - - class Dropdown { - constructor (options) { - this.el = options.el; - - this._itemSelectCallbacks = []; - this._initEvents(); - } - - /** - * Add classname dropdown_open to element - */ - open () { - this.el.classList.add('dropdown_open'); - } - - /** - * Remove classname dropdown_open to element - */ - close () { - this.el.classList.remove('dropdown_open'); - } - - /** - * Open or close? - */ - toggle () { - if (this.isOpen()) { - this.close(); - } else { - this.open(); - } - } - - /** - * Set callback on user select event - * @param {Function} callback - */ - onSelect (callback) { - this._itemSelectCallbacks.push(callback); - } - - isOpen () { - return this.el.classList.contains('dropdown_open'); - } - - _initEvents () { - this.el.addEventListener('click', this._onClick.bind(this)); - } - - _onClick (event) { - if (event.target.classList.contains('dropdown__item')) { - event.preventDefault(); - this._onItemClick(event); - } else { - this.toggle(); - } - } - - _onItemClick (event) { - var itemHtml = event.target.innerHTML; - this.el.querySelector('.js-title').innerHTML = itemHtml; - - this._itemSelectCallbacks.forEach(callback => { - callback({ - el: this.el, - item: this, - text: itemHtml - }); - }); - - this.close(); - } - - - - //TODO: method addItem - } - - //EXPORT - window.Dropdown = Dropdown; - -})(); \ No newline at end of file diff --git a/blocks/form/form.jade b/blocks/form/form.jade new file mode 100644 index 0000000..caf1c28 --- /dev/null +++ b/blocks/form/form.jade @@ -0,0 +1,6 @@ +a(data-action='show' class='js-button pure-button' href='#')= 'Add new' +form(class='js-form pure-form hidden') + fieldset + input(required='required' type='url' name='href' placeholder='url') + input(required='required' type='text' name='anchor' placeholder='text') + button(type='submit' class='pure-button pure-button-primary')= 'Save' diff --git a/blocks/form/form.js b/blocks/form/form.js new file mode 100644 index 0000000..aa11da3 --- /dev/null +++ b/blocks/form/form.js @@ -0,0 +1,74 @@ +'use strict'; + +import template from './form.jade' + +export default class Form { + constructor(opts) { + console.time('Handlebars set form'); + this._template = template; + console.timeEnd('Handlebars set form'); + + this.el = document.createElement('div'); + + this.setData(opts.data); + this._initEvents(); + } + + render() { + this.el.innerHTML = this._template(this.data); + + this.formEl = this.el.querySelector('.js-form'); + this.btnEl = this.el.querySelector('.js-button'); + } + + setData(data) { + this.data = data; + } + + toggle() { + this.formEl.classList.toggle('hidden'); + this.btnEl.classList.toggle('hidden'); + } + + getField(name) { + return this.el.querySelector(`[name="${name}"]`); + } + + trigger(name, data) { + let widgetEvent = new CustomEvent(name, { + bubbles: true, + detail: data + }); + + this.el.dispatchEvent(widgetEvent); + } + + _initEvents() { + this.el.addEventListener('click', this._onClick.bind(this)); + this.el.addEventListener('submit', this._onSubmit.bind(this)); + } + + _onClick(event) { + let item = event.target; + + switch (item.dataset.action) { + case 'show': + this.toggle(); + break; + } + } + + _onSubmit(event) { + event.preventDefault(); + + this.trigger('add', { + href: this.getField('href').value, + anchor: this.getField('anchor').value + }); + + this.toggle(); + event.target.reset(); + } + + +} diff --git a/blocks/menu/menu.css b/blocks/menu/menu.css new file mode 100644 index 0000000..69005e4 --- /dev/null +++ b/blocks/menu/menu.css @@ -0,0 +1,36 @@ +/* Styles go here */ + +.close { + position: absolute; + right: 32px; + top: 0; + width: 32px; + height: 32px; + opacity: 0.3; + cursor: pointer; +} +.close:hover { + opacity: 1; +} +.close:before, .close:after { + position: absolute; + left: 15px; + content: ' '; + height: 33px; + width: 2px; + background-color: #333; +} +.close:before { + transform: rotate(45deg); +} +.close:after { + transform: rotate(-45deg); +} + +.pure-menu-item .close { + display: none; +} + +.pure-menu-item:hover .close { + display: block; +} diff --git a/blocks/menu/menu.jade b/blocks/menu/menu.jade new file mode 100644 index 0000000..2d338a9 --- /dev/null +++ b/blocks/menu/menu.jade @@ -0,0 +1,7 @@ +div(class='pure-menu custom-restricted-width') + span.pure-menu-heading= title + ul.pure-menu-list + each item, index in items + li.pure-menu-item(data-index=index) + a.pure-menu-link(data-action='pick' href=item.href)= item.anchor + i.close(data-action='remove') diff --git a/blocks/menu/menu.js b/blocks/menu/menu.js new file mode 100644 index 0000000..5423785 --- /dev/null +++ b/blocks/menu/menu.js @@ -0,0 +1,70 @@ +'use strict'; + +import './menu.css' +import template from './menu.jade' + +export default class Menu { + constructor(opts) { + console.time('Handlebars set menu'); + this._template = template; + console.timeEnd('Handlebars set menu'); + + this.el = document.createElement('div'); + + this.setData(opts.data || {title: '', items: []}); + this._initEvents(); + } + + render() { + this.el.innerHTML = this._template(this.data); + } + + setData(data) { + this.data = data; + } + + removeItem(item) { + let index = parseInt(item.parentNode.dataset.index, 10); + + this.trigger('remove', { + index + }); + } + + pickItem(item) { + this.trigger('pick', { + href: item.getAttribute('href'), + anchor: item.textContent + }); + } + + trigger(name, data) { + let widgetEvent = new CustomEvent(name, { + bubbles: true, + detail: data + }); + + this.el.dispatchEvent(widgetEvent); + } + + _initEvents() { + this.el.addEventListener('click', this._onClick.bind(this)); + } + + _onClick(event) { + event.preventDefault(); + let item = event.target; + + switch (item.dataset.action) { + case 'remove': + this.removeItem(item); + break; + + case 'pick': + this.pickItem(item); + break; + } + } + + +} diff --git a/bundle.js b/bundle.js new file mode 100644 index 0000000..4260ee0 --- /dev/null +++ b/bundle.js @@ -0,0 +1,1124 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + + __webpack_require__(1); + + var _model = __webpack_require__(5); + + var _model2 = _interopRequireDefault(_model); + + var _form = __webpack_require__(6); + + var _form2 = _interopRequireDefault(_form); + + var _menu = __webpack_require__(10); + + var _menu2 = _interopRequireDefault(_menu); + + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + + var APP_EL = document.querySelector('.app'), + DATA_KEY = '-KIIIl-A1w7peqCRVZ0R'; + + var menu = new _menu2.default({ + tmpl: '#menu' + }); + + var form = new _form2.default({ + tmpl: '#form' + }); + + var model = new _model2.default({ + resource: 'menu', + key: DATA_KEY + }); + + form.render(); + + APP_EL.appendChild(menu.el); + APP_EL.appendChild(form.el); + + form.el.addEventListener('add', function (event) { + var data = model.getData(); + data.items.push(event.detail); + model.save(function () { + menu.setData(model.getData()); + menu.render(); + }); + }); + + menu.el.addEventListener('remove', function (event) { + //TODO: seems like part of model + var data = model.getData(); + data.items.splice(event.detail.index, 1); + model.setData(data); + + model.save(function () { + menu.setData(model.getData()); + menu.render(); + }); + }); + + //TODO: create smth smarter + menu.el.addEventListener('pick', function (event) { + var faviconer = document.querySelector('.faviconer'), + img = document.createElement('img'); + + img.onload = function () { + faviconer.appendChild(img); + }; + img.src = event.detail.href + '/favicon.ico'; + }); + + // Load data + model.fetch(function () { + menu.setData(model.getData()); + menu.render(); + }); + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + // style-loader: Adds some css to the DOM by adding a