From 1227b1479c587ff202241cb6aca4a14b606bea8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C4=85sior?= Date: Tue, 8 Sep 2020 22:03:09 +0200 Subject: [PATCH] Port assets to Webpack Encore Fixes #111 --- {public/assets => assets/css}/admin.css | 0 assets/css/app.css | 3 - assets/css/common.css | 2 + {public/assets => assets/css}/error.css | 4 +- {public/assets => assets/css}/main.css | 10 +- {public/assets => assets/css}/mobile.css | 0 {public/assets => assets/css}/themes.css | 8 +- .../fonts => assets/font}/Inconsolata.woff2 | Bin .../font}/LatoLatin-Heavy.woff2 | Bin .../font}/LatoLatin-Italic.woff2 | Bin .../font}/LatoLatin-Regular.woff | Bin .../font}/LatoLatin-Regular.woff2 | Bin {public/assets => assets/img}/icon.png | Bin {public/assets => assets/img}/logo.svg | 0 .../img}/themes/bieszczady.jpg | Bin .../assets => assets/img}/themes/night.jpg | Bin .../assets => assets/img}/themes/rainbow.jpg | Bin {public/assets => assets/img}/themes/wood.jpg | Bin assets/js/app.js | 14 -- assets/js/common.js | 23 ++ assets/js/radio-table-settings.js | 9 + assets/js/radio-table.js | 15 ++ assets/js/src/NumberIndentManager.js | 59 +++++ assets/js/src/OverflowStylesManager.js | 32 +++ .../js/src/RDSPopupManager.js | 105 +-------- .../js/src/RadioTableColumnsUI.js | 10 +- assets/js/src/SortableTable.js | 104 ++++++++ assets/js/src/TabbedUI.js | 99 ++++++++ public/assets/common.js | 222 ------------------ templates/admin/_admin_layout.html.twig | 2 +- templates/dark-error.html.twig | 2 +- templates/layout.html.twig | 9 +- templates/radio_table/settings.html.twig | 3 +- templates/radio_table/show.html.twig | 8 +- webpack.config.js | 12 +- 35 files changed, 376 insertions(+), 379 deletions(-) rename {public/assets => assets/css}/admin.css (100%) delete mode 100644 assets/css/app.css create mode 100644 assets/css/common.css rename {public/assets => assets/css}/error.css (89%) rename {public/assets => assets/css}/main.css (99%) rename {public/assets => assets/css}/mobile.css (100%) rename {public/assets => assets/css}/themes.css (87%) rename {public/assets/fonts => assets/font}/Inconsolata.woff2 (100%) rename {public/assets/fonts => assets/font}/LatoLatin-Heavy.woff2 (100%) rename {public/assets/fonts => assets/font}/LatoLatin-Italic.woff2 (100%) rename {public/assets/fonts => assets/font}/LatoLatin-Regular.woff (100%) rename {public/assets/fonts => assets/font}/LatoLatin-Regular.woff2 (100%) rename {public/assets => assets/img}/icon.png (100%) rename {public/assets => assets/img}/logo.svg (100%) rename {public/assets => assets/img}/themes/bieszczady.jpg (100%) rename {public/assets => assets/img}/themes/night.jpg (100%) rename {public/assets => assets/img}/themes/rainbow.jpg (100%) rename {public/assets => assets/img}/themes/wood.jpg (100%) delete mode 100644 assets/js/app.js create mode 100644 assets/js/common.js create mode 100644 assets/js/radio-table-settings.js create mode 100644 assets/js/radio-table.js create mode 100644 assets/js/src/NumberIndentManager.js create mode 100644 assets/js/src/OverflowStylesManager.js rename public/assets/radio-table.js => assets/js/src/RDSPopupManager.js (60%) rename public/assets/radio-table-settings.js => assets/js/src/RadioTableColumnsUI.js (95%) create mode 100644 assets/js/src/SortableTable.js create mode 100644 assets/js/src/TabbedUI.js delete mode 100644 public/assets/common.js diff --git a/public/assets/admin.css b/assets/css/admin.css similarity index 100% rename from public/assets/admin.css rename to assets/css/admin.css diff --git a/assets/css/app.css b/assets/css/app.css deleted file mode 100644 index cb33b131..00000000 --- a/assets/css/app.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - background-color: lightgray; -} diff --git a/assets/css/common.css b/assets/css/common.css new file mode 100644 index 00000000..4a7079fa --- /dev/null +++ b/assets/css/common.css @@ -0,0 +1,2 @@ +@import './main.css'; +@import './mobile.css' (max-width: 800px); diff --git a/public/assets/error.css b/assets/css/error.css similarity index 89% rename from public/assets/error.css rename to assets/css/error.css index d8446715..3128af08 100644 --- a/public/assets/error.css +++ b/assets/css/error.css @@ -2,7 +2,7 @@ @font-face { font-family: "LatoLatinWeb"; - src: url('fonts/LatoLatin-Regular.woff2'), url('fonts/LatoLatin-Regular.woff'); + src: url('../font/LatoLatin-Regular.woff2'), url('../font/LatoLatin-Regular.woff'); } body { @@ -31,7 +31,7 @@ div.logo { } div.logo a { - background: url('logo.svg') no-repeat; + background: url('../img/logo.svg') no-repeat; background-size: contain; height: 50px; width: 200px; diff --git a/public/assets/main.css b/assets/css/main.css similarity index 99% rename from public/assets/main.css rename to assets/css/main.css index 801073f5..73e4e891 100644 --- a/public/assets/main.css +++ b/assets/css/main.css @@ -2,21 +2,21 @@ @font-face { font-family: "LatoLatinWeb"; - src: url('fonts/LatoLatin-Regular.woff2'), url('fonts/LatoLatin-Regular.woff'); + src: url('../font/LatoLatin-Regular.woff2'), url('../font/LatoLatin-Regular.woff'); } @font-face { font-family: "LatoLatinWeb"; - src: url('fonts/LatoLatin-Heavy.woff2'); + src: url('../font/LatoLatin-Heavy.woff2'); font-weight: bold; } @font-face { font-family: "LatoLatinWeb"; - src: url('fonts/LatoLatin-Italic.woff2'); + src: url('../font/LatoLatin-Italic.woff2'); font-style: italic; } @font-face { font-family: "Inconsolata"; - src: url('fonts/Inconsolata.woff2'); + src: url('../font/Inconsolata.woff2'); } body { @@ -104,7 +104,7 @@ a:active { } .site-header .site-logo { - background: url('logo.svg') no-repeat; + background: url('../img/logo.svg') no-repeat; background-size: 238px auto; background-position: 4px 4px; width: 245px; diff --git a/public/assets/mobile.css b/assets/css/mobile.css similarity index 100% rename from public/assets/mobile.css rename to assets/css/mobile.css diff --git a/public/assets/themes.css b/assets/css/themes.css similarity index 87% rename from public/assets/themes.css rename to assets/css/themes.css index 4e56b819..5af2e5ca 100644 --- a/public/assets/themes.css +++ b/assets/css/themes.css @@ -1,7 +1,7 @@ /* theme — bieszczady */ .theme-bieszczady .customizable-background { - background: #E1E1E1 url('themes/bieszczady.jpg') no-repeat center / cover fixed; + background: #E1E1E1 url('../img/themes/bieszczady.jpg') no-repeat center / cover fixed; } .theme-bieszczady .customizable-color { color: #000; @@ -17,7 +17,7 @@ /* https://pixnio.com/textures-and-patterns/wood-texture/texture-old-retro-dirty-wood-knot */ .theme-wood .customizable-background { - background: #F2DAB6 url('themes/wood.jpg') no-repeat center / cover fixed; + background: #F2DAB6 url('../img/themes/wood.jpg') no-repeat center / cover fixed; } .theme-wood .customizable-color { color: #000; @@ -36,7 +36,7 @@ /* http://www.publicdomainpictures.net/view-image.php?image=25019&picture=dark-clouds-and-rainbow/ */ .theme-rainbow .customizable-background { - background: #D8DAD7 url('themes/rainbow.jpg') no-repeat center / cover fixed; + background: #D8DAD7 url('../img/themes/rainbow.jpg') no-repeat center / cover fixed; } .theme-rainbow .customizable-color { color: #060015; @@ -59,7 +59,7 @@ /* http://magdeleine.co/photo-by-caleb-ralston-n-491/ */ .theme-night .customizable-background { - background: #2A3944 url('themes/night.jpg') no-repeat center / cover fixed; + background: #2A3944 url('../img/themes/night.jpg') no-repeat center / cover fixed; } .theme-night .customizable-color { color: #eee; diff --git a/public/assets/fonts/Inconsolata.woff2 b/assets/font/Inconsolata.woff2 similarity index 100% rename from public/assets/fonts/Inconsolata.woff2 rename to assets/font/Inconsolata.woff2 diff --git a/public/assets/fonts/LatoLatin-Heavy.woff2 b/assets/font/LatoLatin-Heavy.woff2 similarity index 100% rename from public/assets/fonts/LatoLatin-Heavy.woff2 rename to assets/font/LatoLatin-Heavy.woff2 diff --git a/public/assets/fonts/LatoLatin-Italic.woff2 b/assets/font/LatoLatin-Italic.woff2 similarity index 100% rename from public/assets/fonts/LatoLatin-Italic.woff2 rename to assets/font/LatoLatin-Italic.woff2 diff --git a/public/assets/fonts/LatoLatin-Regular.woff b/assets/font/LatoLatin-Regular.woff similarity index 100% rename from public/assets/fonts/LatoLatin-Regular.woff rename to assets/font/LatoLatin-Regular.woff diff --git a/public/assets/fonts/LatoLatin-Regular.woff2 b/assets/font/LatoLatin-Regular.woff2 similarity index 100% rename from public/assets/fonts/LatoLatin-Regular.woff2 rename to assets/font/LatoLatin-Regular.woff2 diff --git a/public/assets/icon.png b/assets/img/icon.png similarity index 100% rename from public/assets/icon.png rename to assets/img/icon.png diff --git a/public/assets/logo.svg b/assets/img/logo.svg similarity index 100% rename from public/assets/logo.svg rename to assets/img/logo.svg diff --git a/public/assets/themes/bieszczady.jpg b/assets/img/themes/bieszczady.jpg similarity index 100% rename from public/assets/themes/bieszczady.jpg rename to assets/img/themes/bieszczady.jpg diff --git a/public/assets/themes/night.jpg b/assets/img/themes/night.jpg similarity index 100% rename from public/assets/themes/night.jpg rename to assets/img/themes/night.jpg diff --git a/public/assets/themes/rainbow.jpg b/assets/img/themes/rainbow.jpg similarity index 100% rename from public/assets/themes/rainbow.jpg rename to assets/img/themes/rainbow.jpg diff --git a/public/assets/themes/wood.jpg b/assets/img/themes/wood.jpg similarity index 100% rename from public/assets/themes/wood.jpg rename to assets/img/themes/wood.jpg diff --git a/assets/js/app.js b/assets/js/app.js deleted file mode 100644 index 1748c149..00000000 --- a/assets/js/app.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Welcome to your app's main JavaScript file! - * - * We recommend including the built version of this JavaScript file - * (and its CSS file) in your base layout (base.html.twig). - */ - -// any CSS you import will output into a single css file (app.css in this case) -import '../css/app.css'; - -// Need jQuery? Install it with "yarn add jquery", then uncomment to import it. -// import $ from 'jquery'; - -console.log('Hello Webpack Encore! Edit me in assets/js/app.js'); diff --git a/assets/js/common.js b/assets/js/common.js new file mode 100644 index 00000000..5751a08c --- /dev/null +++ b/assets/js/common.js @@ -0,0 +1,23 @@ +import '../css/common.css'; +import '../img/icon.png'; + +import { SortableTable } from './src/SortableTable.js'; +import { TabbedUI } from './src/TabbedUI.js'; + +document.documentElement.classList.add('JS'); + +document.addEventListener('DOMContentLoaded', function(){ + let sortableTable = document.querySelector('table.sortable'); + let tabbedUI = document.querySelector('.tabbed-ui'); + let notification = document.querySelector('.notification-wrapper'); + + if (sortableTable) { + new SortableTable(sortableTable); + } + if (tabbedUI) { + new TabbedUI(tabbedUI); + } + if (notification) { + setTimeout(() => { notification.classList.add('hidden'); }, 10000); + } +}); diff --git a/assets/js/radio-table-settings.js b/assets/js/radio-table-settings.js new file mode 100644 index 00000000..4e387bd0 --- /dev/null +++ b/assets/js/radio-table-settings.js @@ -0,0 +1,9 @@ +import { RadioTableColumnsUI } from './src/RadioTableColumnsUI.js'; + +document.addEventListener('DOMContentLoaded', function(){ + let container = document.querySelector('.radio-table-columns'); + + if (container) { + new RadioTableColumnsUI(container); + } +}); diff --git a/assets/js/radio-table.js b/assets/js/radio-table.js new file mode 100644 index 00000000..76711802 --- /dev/null +++ b/assets/js/radio-table.js @@ -0,0 +1,15 @@ +import '../css/themes.css'; + +import { NumberIndentManager } from './src/NumberIndentManager.js'; +import { OverflowStylesManager } from './src/OverflowStylesManager.js'; +import { RDSPopupManager } from './src/RDSPopupManager.js'; + +document.addEventListener('DOMContentLoaded', function(){ + let container = document.querySelector('.radio-table-container'); + + if (container) { + new NumberIndentManager(container); + new OverflowStylesManager(container); + new RDSPopupManager(container); + } +}); diff --git a/assets/js/src/NumberIndentManager.js b/assets/js/src/NumberIndentManager.js new file mode 100644 index 00000000..a88e48ba --- /dev/null +++ b/assets/js/src/NumberIndentManager.js @@ -0,0 +1,59 @@ +export class NumberIndentManager +{ + constructor(container) + { + this.container = container; + this.itemsGroups = new Map; + + this.setupItemsGroups(); + this.applyNumberIndent(); + } + + setupItemsGroups() + { + this.container.querySelectorAll('[data-number-indent]').forEach(item => { + let indentType = item.dataset.numberIndent; + + if (!this.itemsGroups.has(indentType)) { + this.itemsGroups.set(indentType, []); + } + + this.itemsGroups.get(indentType).push(item); + }); + } + + applyNumberIndent() + { + this.itemsGroups.forEach(items => { + let maxNumber = this.getMaxNumberFromItems(items); + let maxDigits = this.getDigitsCountOfInteger(maxNumber); + + items.forEach(item => { + let number = this.getIntegerFromItem(item); + let digits = this.getDigitsCountOfInteger(number); + let digitsDifference = maxDigits - digits; + + if (digitsDifference) { + item.classList.add(`number-indent-${digitsDifference}`); + } + }); + }); + } + + getIntegerFromItem(item) + { + return Number.parseInt(item.textContent); + } + + getDigitsCountOfInteger(number) + { + return number.toString().length; + } + + getMaxNumberFromItems(items) + { + let numbers = items.map(item => this.getIntegerFromItem(item)); + + return numbers.reduce((prev, value) => prev > value ? prev : value); + } +} diff --git a/assets/js/src/OverflowStylesManager.js b/assets/js/src/OverflowStylesManager.js new file mode 100644 index 00000000..d41a8d90 --- /dev/null +++ b/assets/js/src/OverflowStylesManager.js @@ -0,0 +1,32 @@ +export class OverflowStylesManager +{ + constructor(container) + { + this.container = container; + + this.refreshStatus(); + + this.container.addEventListener('scroll', this.refreshStatus.bind(this)); + window.addEventListener('resize', this.refreshStatus.bind(this)); + } + + refreshStatus() + { + const MIN_SCROLL_MARGIN = 3; + + let container = this.container; + let isScrollbarVisible = (container.scrollWidth !== container.clientWidth); + + container.classList.remove('overflow-left'); + container.classList.remove('overflow-right'); + + if (isScrollbarVisible) { + if(container.scrollLeft > MIN_SCROLL_MARGIN) { + container.classList.add('overflow-left'); + } + if((container.scrollWidth - container.clientWidth - container.scrollLeft) > MIN_SCROLL_MARGIN) { + container.classList.add('overflow-right'); + } + } + } +} diff --git a/public/assets/radio-table.js b/assets/js/src/RDSPopupManager.js similarity index 60% rename from public/assets/radio-table.js rename to assets/js/src/RDSPopupManager.js index 7a75163d..5479f6b2 100644 --- a/public/assets/radio-table.js +++ b/assets/js/src/RDSPopupManager.js @@ -1,4 +1,4 @@ -class RDSPopupManager +export class RDSPopupManager { constructor(container) { @@ -150,106 +150,3 @@ class RDSPopupManager this.hidePopup(); } } - -class NumberIndentManager -{ - constructor(container) - { - this.container = container; - this.itemsGroups = new Map; - - this.setupItemsGroups(); - this.applyNumberIndent(); - } - - setupItemsGroups() - { - this.container.querySelectorAll('[data-number-indent]').forEach(item => { - let indentType = item.dataset.numberIndent; - - if (!this.itemsGroups.has(indentType)) { - this.itemsGroups.set(indentType, []); - } - - this.itemsGroups.get(indentType).push(item); - }); - } - - applyNumberIndent() - { - this.itemsGroups.forEach(items => { - let maxNumber = this.getMaxNumberFromItems(items); - let maxDigits = this.getDigitsCountOfInteger(maxNumber); - - items.forEach(item => { - let number = this.getIntegerFromItem(item); - let digits = this.getDigitsCountOfInteger(number); - let digitsDifference = maxDigits - digits; - - if (digitsDifference) { - item.classList.add(`number-indent-${digitsDifference}`); - } - }); - }); - } - - getIntegerFromItem(item) - { - return Number.parseInt(item.textContent); - } - - getDigitsCountOfInteger(number) - { - return number.toString().length; - } - - getMaxNumberFromItems(items) - { - let numbers = items.map(item => this.getIntegerFromItem(item)); - - return numbers.reduce((prev, value) => prev > value ? prev : value); - } -} - -class OverflowStylesManager -{ - constructor(container) - { - this.container = container; - - this.refreshStatus(); - - this.container.addEventListener('scroll', this.refreshStatus.bind(this)); - window.addEventListener('resize', this.refreshStatus.bind(this)); - } - - refreshStatus() - { - const MIN_SCROLL_MARGIN = 3; - - let container = this.container; - let isScrollbarVisible = (container.scrollWidth !== container.clientWidth); - - container.classList.remove('overflow-left'); - container.classList.remove('overflow-right'); - - if (isScrollbarVisible) { - if(container.scrollLeft > MIN_SCROLL_MARGIN) { - container.classList.add('overflow-left'); - } - if((container.scrollWidth - container.clientWidth - container.scrollLeft) > MIN_SCROLL_MARGIN) { - container.classList.add('overflow-right'); - } - } - } -} - -document.addEventListener('DOMContentLoaded', function(){ - let container = document.querySelector('.radio-table-container'); - - if (container) { - new NumberIndentManager(container); - new OverflowStylesManager(container); - new RDSPopupManager(container); - } -}); diff --git a/public/assets/radio-table-settings.js b/assets/js/src/RadioTableColumnsUI.js similarity index 95% rename from public/assets/radio-table-settings.js rename to assets/js/src/RadioTableColumnsUI.js index 5287e3b2..bb17f235 100644 --- a/public/assets/radio-table-settings.js +++ b/assets/js/src/RadioTableColumnsUI.js @@ -1,4 +1,4 @@ -class RadioTableColumnsUI +export class RadioTableColumnsUI { constructor(container) { @@ -154,11 +154,3 @@ class RadioTableColumnsUI button.focus(); } } - -document.addEventListener('DOMContentLoaded', function(){ - let container = document.querySelector('.radio-table-columns'); - - if (container) { - new RadioTableColumnsUI(container); - } -}); diff --git a/assets/js/src/SortableTable.js b/assets/js/src/SortableTable.js new file mode 100644 index 00000000..9e88f152 --- /dev/null +++ b/assets/js/src/SortableTable.js @@ -0,0 +1,104 @@ +export class SortableTable +{ + constructor(table) + { + this.headers = table.querySelectorAll('thead th'); + this.body = table.querySelector('tbody'); + this.rows = [...this.body.querySelectorAll('tr')]; + + this.order = 1; // 1 = ascending, -1 = descending + this.lastColumnIndex = -1; + + this.setupMarkup(); + } + + setupMarkup() + { + this.rows.forEach(function(row){ + // It's needed for comparing equal values. + row.dataset.index = row.rowIndex; + }); + + this.headers.forEach(header => { + if (header.dataset.sort) { + header.addEventListener('click', this.onHeaderClick.bind(this)); + header.addEventListener('keydown', this.onHeaderKeydown.bind(this)); + header.role = 'button'; + header.tabIndex = 0; + } + }); + } + + onHeaderClick(event) + { + let header = event.target; + let columnIndex = header.cellIndex; + let sortingType = header.dataset.sort; + + header.blur(); + + this.order = (columnIndex === this.lastColumnIndex) ? (this.order * -1) : 1; + this.lastColumnIndex = columnIndex; + + this.rows.sort(this.compareRows.bind(this, columnIndex, sortingType)); + this.rows.forEach(row => { this.body.appendChild(row); }); + + this.headers.forEach(header => { + header.classList.remove('sorted-asc'); + header.classList.remove('sorted-desc'); + }); + header.classList.add(this.order == 1 ? 'sorted-asc' : 'sorted-desc'); + } + + onHeaderKeydown(event) + { + const ENTER_KEY = 13; + const SPACE_KEY = 32; + + if (event.keyCode != ENTER_KEY && event.keyCode != SPACE_KEY) { + return; + } + + let header = event.target; + + event.preventDefault(); + header.dispatchEvent(new Event('click')); + header.focus(); + } + + compareRows(columnIndex, sortingType, row1, row2) + { + // Use textContent to ignore HTML in row content. Trim HTML indentation. + let value1 = row1.children[columnIndex].textContent.trim(); + let value2 = row2.children[columnIndex].textContent.trim(); + + if ('N' === sortingType) { + if('' !== value1) { + value1 = Number.parseFloat(value1.replace(',', '.')); + } + if('' !== value2) { + value2 = Number.parseFloat(value2.replace(',','.')); + } + } + + if (value1 === value2) { + return row1.dataset.index - row2.dataset.index; + } + if ('' === value1) { + return 1; + } + if ('' === value2) { + return -1; + } + + let result; + if ('T' === sortingType) { + result = value1.localeCompare(value2, [], {usage: 'sort'}); + } + else { + result = (value1 > value2 ? 1 : -1); + } + + return result * this.order; + } +} diff --git a/assets/js/src/TabbedUI.js b/assets/js/src/TabbedUI.js new file mode 100644 index 00000000..ccca60c0 --- /dev/null +++ b/assets/js/src/TabbedUI.js @@ -0,0 +1,99 @@ +export class TabbedUI +{ + constructor(container) + { + this.container = container; + this.panels = [...container.children]; + this.navigator = null; + this.buttons = []; + + this.setupNavigator(); + this.setupPanels(); + } + + get LAST_PANEL_SESSION_KEY() + { + return 'tabbed-ui__' + location.pathname; + } + + setupNavigator() + { + this.navigator = document.createElement('ul'); + this.navigator.classList.add('tabbed-ui-navigator'); + this.navigator.setAttribute('role', 'tablist'); + + this.panels.forEach((panel, i) => { + let title = panel.querySelector('h2').innerHTML; + let item = document.createElement('li'); + let button = document.createElement('button'); + + button.innerHTML = title; + button.dataset.panelNumber = i; + button.type = 'button'; + button.addEventListener('click', this.onNavigatorButtonClick.bind(this)); + + this.buttons[i] = button; + + item.appendChild(button); + this.navigator.appendChild(item); + }); + + this.container.insertBefore(this.navigator, this.panels[0]); + } + + setupPanels() + { + let defaultPanelNumber = 0; + + let currentHash = location.hash.replace('#', ''); + let savedPanelNumber = sessionStorage.getItem(this.LAST_PANEL_SESSION_KEY); + + if (null != savedPanelNumber) { + defaultPanelNumber = savedPanelNumber; + } + + this.panels.forEach((panel, i) => { + panel.setAttribute('role', 'tab'); + + if (currentHash && panel.id == currentHash) { + defaultPanelNumber = i; + + // If default panel was determined by URL hash, scroll to + // the top of the page to prevent default browser behavior. + let scrollToTop = () => { + window.scroll(0, 0); + window.removeEventListener('scroll', scrollToTop); + }; + window.addEventListener('scroll', scrollToTop); + } + }); + + this.changePanel(defaultPanelNumber); + } + + changePanel(panelNumber) + { + this.panels.forEach((panel, i) => { + panel.classList.remove('tabbed-ui-current'); + this.buttons[i].classList.remove('tabbed-ui-current'); + + if (i == panelNumber) { + panel.classList.add('tabbed-ui-current'); + this.buttons[i].classList.add('tabbed-ui-current'); + } + }); + } + + onNavigatorButtonClick(event) + { + event.preventDefault(); + + let button = event.target; + let panelNumber = button.dataset.panelNumber; + + button.blur(); + + this.changePanel(panelNumber); + sessionStorage.setItem(this.LAST_PANEL_SESSION_KEY, panelNumber); + } +} diff --git a/public/assets/common.js b/public/assets/common.js deleted file mode 100644 index a0af9197..00000000 --- a/public/assets/common.js +++ /dev/null @@ -1,222 +0,0 @@ -document.documentElement.classList.add('JS'); - -class SortableTable -{ - constructor(table) - { - this.headers = table.querySelectorAll('thead th'); - this.body = table.querySelector('tbody'); - this.rows = [...this.body.querySelectorAll('tr')]; - - this.order = 1; // 1 = ascending, -1 = descending - this.lastColumnIndex = -1; - - this.setupMarkup(); - } - - setupMarkup() - { - this.rows.forEach(function(row){ - // It's needed for comparing equal values. - row.dataset.index = row.rowIndex; - }); - - this.headers.forEach(header => { - if (header.dataset.sort) { - header.addEventListener('click', this.onHeaderClick.bind(this)); - header.addEventListener('keydown', this.onHeaderKeydown.bind(this)); - header.role = 'button'; - header.tabIndex = 0; - } - }); - } - - onHeaderClick(event) - { - let header = event.target; - let columnIndex = header.cellIndex; - let sortingType = header.dataset.sort; - - header.blur(); - - this.order = (columnIndex === this.lastColumnIndex) ? (this.order * -1) : 1; - this.lastColumnIndex = columnIndex; - - this.rows.sort(this.compareRows.bind(this, columnIndex, sortingType)); - this.rows.forEach(row => { this.body.appendChild(row); }); - - this.headers.forEach(header => { - header.classList.remove('sorted-asc'); - header.classList.remove('sorted-desc'); - }); - header.classList.add(this.order == 1 ? 'sorted-asc' : 'sorted-desc'); - } - - onHeaderKeydown(event) - { - const ENTER_KEY = 13; - const SPACE_KEY = 32; - - if (event.keyCode != ENTER_KEY && event.keyCode != SPACE_KEY) { - return; - } - - let header = event.target; - - event.preventDefault(); - header.dispatchEvent(new Event('click')); - header.focus(); - } - - compareRows(columnIndex, sortingType, row1, row2) - { - // Use textContent to ignore HTML in row content. Trim HTML indentation. - let value1 = row1.children[columnIndex].textContent.trim(); - let value2 = row2.children[columnIndex].textContent.trim(); - - if ('N' === sortingType) { - if('' !== value1) { - value1 = Number.parseFloat(value1.replace(',', '.')); - } - if('' !== value2) { - value2 = Number.parseFloat(value2.replace(',','.')); - } - } - - if (value1 === value2) { - return row1.dataset.index - row2.dataset.index; - } - if ('' === value1) { - return 1; - } - if ('' === value2) { - return -1; - } - - let result; - if ('T' === sortingType) { - result = value1.localeCompare(value2, [], {usage: 'sort'}); - } - else { - result = (value1 > value2 ? 1 : -1); - } - - return result * this.order; - } -} - -class TabbedUI -{ - constructor(container) - { - this.container = container; - this.panels = [...container.children]; - this.navigator = null; - this.buttons = []; - - this.setupNavigator(); - this.setupPanels(); - } - - get LAST_PANEL_SESSION_KEY() - { - return 'tabbed-ui__' + location.pathname; - } - - setupNavigator() - { - this.navigator = document.createElement('ul'); - this.navigator.classList.add('tabbed-ui-navigator'); - this.navigator.setAttribute('role', 'tablist'); - - this.panels.forEach((panel, i) => { - let title = panel.querySelector('h2').innerHTML; - let item = document.createElement('li'); - let button = document.createElement('button'); - - button.innerHTML = title; - button.dataset.panelNumber = i; - button.type = 'button'; - button.addEventListener('click', this.onNavigatorButtonClick.bind(this)); - - this.buttons[i] = button; - - item.appendChild(button); - this.navigator.appendChild(item); - }); - - this.container.insertBefore(this.navigator, this.panels[0]); - } - - setupPanels() - { - let defaultPanelNumber = 0; - - let currentHash = location.hash.replace('#', ''); - let savedPanelNumber = sessionStorage.getItem(this.LAST_PANEL_SESSION_KEY); - - if (null != savedPanelNumber) { - defaultPanelNumber = savedPanelNumber; - } - - this.panels.forEach((panel, i) => { - panel.setAttribute('role', 'tab'); - - if (currentHash && panel.id == currentHash) { - defaultPanelNumber = i; - - // If default panel was determined by URL hash, scroll to - // the top of the page to prevent default browser behavior. - let scrollToTop = () => { - window.scroll(0, 0); - window.removeEventListener('scroll', scrollToTop); - }; - window.addEventListener('scroll', scrollToTop); - } - }); - - this.changePanel(defaultPanelNumber); - } - - changePanel(panelNumber) - { - this.panels.forEach((panel, i) => { - panel.classList.remove('tabbed-ui-current'); - this.buttons[i].classList.remove('tabbed-ui-current'); - - if (i == panelNumber) { - panel.classList.add('tabbed-ui-current'); - this.buttons[i].classList.add('tabbed-ui-current'); - } - }); - } - - onNavigatorButtonClick(event) - { - event.preventDefault(); - - let button = event.target; - let panelNumber = button.dataset.panelNumber; - - button.blur(); - - this.changePanel(panelNumber); - sessionStorage.setItem(this.LAST_PANEL_SESSION_KEY, panelNumber); - } -} - -document.addEventListener('DOMContentLoaded', function(){ - let sortableTable = document.querySelector('table.sortable'); - let tabbedUI = document.querySelector('.tabbed-ui'); - let notification = document.querySelector('.notification-wrapper'); - - if (sortableTable) { - new SortableTable(sortableTable); - } - if (tabbedUI) { - new TabbedUI(tabbedUI); - } - if (notification) { - setTimeout(() => { notification.classList.add('hidden'); }, 10000); - } -}); diff --git a/templates/admin/_admin_layout.html.twig b/templates/admin/_admin_layout.html.twig index 4608aeb7..711739ed 100644 --- a/templates/admin/_admin_layout.html.twig +++ b/templates/admin/_admin_layout.html.twig @@ -1,7 +1,7 @@ {% extends 'layout.html.twig' %} {% block head_closing %} - + {{ encore_entry_link_tags('admin') }} {% endblock %} {% block page_title %}Panel administracyjny{% endblock %} diff --git a/templates/dark-error.html.twig b/templates/dark-error.html.twig index 05ff4bc1..7d0b07f6 100644 --- a/templates/dark-error.html.twig +++ b/templates/dark-error.html.twig @@ -3,7 +3,7 @@ - + {{ encore_entry_link_tags('error') }} RadioLista — {{ 'layout.slogan'|trans }} diff --git a/templates/layout.html.twig b/templates/layout.html.twig index abc165c4..ae735668 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -18,11 +18,10 @@ {% endfor %} {% endif %} - - - - - + + + {{ encore_entry_link_tags('common') }} + {{ encore_entry_script_tags('common') }} {% if block('page_title') %} {% block page_title %}{% endblock %} — RadioLista diff --git a/templates/radio_table/settings.html.twig b/templates/radio_table/settings.html.twig index 27752158..c736129a 100644 --- a/templates/radio_table/settings.html.twig +++ b/templates/radio_table/settings.html.twig @@ -3,7 +3,8 @@ {% block page_title %}{{ 'radio_table.settings.title'|trans }}{% endblock %} {% block head_closing %} - + {{ encore_entry_link_tags('radio-table-settings') }} + {{ encore_entry_script_tags('radio-table-settings') }} {% endblock %} {% block context_menu %} diff --git a/templates/radio_table/show.html.twig b/templates/radio_table/show.html.twig index ccf97e24..098956e7 100644 --- a/templates/radio_table/show.html.twig +++ b/templates/radio_table/show.html.twig @@ -39,12 +39,10 @@ {% endif %} - {% if custom_css %}{% endif %} - {% if appearance.theme %} - - {% endif %} + {{ encore_entry_link_tags('radio-table') }} + {{ encore_entry_script_tags('radio-table') }} - + {% if custom_css %}{% endif %} {% endblock %} {% block context_menu %} diff --git a/webpack.config.js b/webpack.config.js index e8b42835..12dd000f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,9 +23,11 @@ Encore * Each entry will result in one JavaScript file (e.g. app.js) * and one CSS file (e.g. app.css) if your JavaScript imports CSS. */ - .addEntry('app', './assets/js/app.js') - //.addEntry('page1', './assets/js/page1.js') - //.addEntry('page2', './assets/js/page2.js') + .addEntry('common', './assets/js/common.js') + .addEntry('radio-table', './assets/js/radio-table.js') + .addEntry('radio-table-settings', './assets/js/radio-table-settings.js') + .addStyleEntry('admin', './assets/css/admin.css') + .addStyleEntry('error', './assets/css/error.css') // When enabled, Webpack "splits" your files into smaller pieces for greater optimization. .splitEntryChunks() @@ -69,6 +71,10 @@ Encore // uncomment if you use API Platform Admin (composer req api-admin) //.enableReactPreset() //.addEntry('admin', './assets/js/admin.js') + + .configureBabel(config => { + config.presets = []; + }) ; module.exports = Encore.getWebpackConfig();