diff --git a/package.json b/package.json
index 8e0e193..7055d34 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,7 @@
"axios": "^0.19.2",
"bootstrap": "^5.0.0-alpha1",
"popper.js": "^1.16.1",
+ "query-string": "^6.13.1",
"uuid": "^8.2.0"
}
}
diff --git a/src/app.html b/src/app.html
index f47cd01..88f795a 100644
--- a/src/app.html
+++ b/src/app.html
@@ -8,6 +8,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/app.js b/src/app.js
index 1cc5db3..f395214 100644
--- a/src/app.js
+++ b/src/app.js
@@ -2,22 +2,16 @@ import './app.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap';
-// ! TODO: 5-14 строчки удалить, после теста компонента
-import TestModal from './components/test-modal';
-import TestButton from './components/test-button';
-import TableComponent from './components/table-component';
-import storageApi from './api/StorageServiceAPI';
+import routeService from './services/RouteService';
+import routerPagesContainer from './components/router-pages-container';
+import ApiPage from './components/api-page';
+import MainPage from './components/main-page';
-const testModal = new TestModal();
-const testButton = new TestButton();
-const initStorageListTable = async function () {
- const list = await storageApi.request();
- const storageListTable = new TableComponent();
- return storageListTable.render(list);
-};
+// Новые страницы обязательно добавляем тут
+routerPagesContainer.addRoutes([
+ {url: '/', pageComponent: MainPage},
+ {url: '/api', pageComponent: ApiPage},
+]);
-initStorageListTable();
-
-testButton.subscribe('click', () => {
- testModal.show();
-});
+// Этот метод генерит событие Route, чтобы все компоненты получили его после инициализации
+routeService.init();
diff --git a/src/components/api-page/ApiPage.js b/src/components/api-page/ApiPage.js
new file mode 100644
index 0000000..d1ce652
--- /dev/null
+++ b/src/components/api-page/ApiPage.js
@@ -0,0 +1,19 @@
+import Component from '../component/index';
+import TableComponent from '../table-component';
+import storageApi from '../../api/StorageServiceAPI';
+
+class ApiPage extends Component {
+ constructor (mainNodeSelector, parentNode) {
+ super(mainNodeSelector, parentNode);
+
+ const initStorageListTable = async () => {
+ const list = await storageApi.request();
+ const storageListTable = new TableComponent(this.mainNode);
+ return storageListTable.render(list);
+ };
+
+ initStorageListTable();
+ }
+}
+
+export default ApiPage;
diff --git a/src/components/api-page/index.js b/src/components/api-page/index.js
new file mode 100644
index 0000000..1de3045
--- /dev/null
+++ b/src/components/api-page/index.js
@@ -0,0 +1,3 @@
+import ApiPage from './ApiPage';
+
+export default ApiPage;
diff --git a/src/components/component/Component.js b/src/components/component/Component.js
index 6ee26ff..0be98e5 100644
--- a/src/components/component/Component.js
+++ b/src/components/component/Component.js
@@ -1,21 +1,17 @@
+import EmitService from '../../services/EmitService';
+
/**
* Класс для создания компонентов приложения. Необходим для наследования.
* @param {string} mainNodeSelector - селектор, с помощью которого извлекается шаблон компонента
* @param {Node} parentNode - родительский Node, в который следует положить созданный элемент
*/
-class Component {
+class Component extends EmitService {
/**
* Список слушателей компонента
* @type {{element: Node, eventName: string, listener: Function}[]}
*/
_listeners;
- /**
- * События компонента
- * @type {Object}
- */
- _events;
-
/**
* Корневой элемент компонента
* @type {Node} - корневой элемент компонента
@@ -23,6 +19,7 @@ class Component {
mainNode;
constructor (mainNodeSelector, parentNode) {
+ super();
const content = document.querySelector(mainNodeSelector).content;
if (content.children.length > 1) {
const message = ' должен содержать только один элемент children';
@@ -45,31 +42,6 @@ class Component {
this._listeners.push({element, eventName, listener});
}
- /**
- * Метод подписки на события компонента
- * @param {string} eventName - событие компонента, на которое будет реагировать обработчик
- * @param {Function} listener - обработчик события
- */
- subscribe = (eventName, listener) => {
- const listeners = this._events[eventName] || [];
- this._events[eventName] = [
- ...listeners,
- listener,
- ];
- }
-
- /**
- * Метод генерирует событие
- * @param {string} eventName - событие, которое необходимо сгенерировать
- * @param {unknown[]} args - аругемнты, который необходимо передать обработчикам события
- */
- next = (eventName, ...args) => {
- const listeners = this._events[eventName];
- listeners.forEach((listener) => {
- listener(...args);
- });
- }
-
/**
* Метод уничтожения компонента. Удаляет элемент из верстки, снимает обработчики и очищает подписки
*/
@@ -79,7 +51,7 @@ class Component {
});
this.mainNode.remove();
this._listeners = [];
- this._events = {};
+ this.clearSubscribes();
}
}
diff --git a/src/components/main-page/MainPage.js b/src/components/main-page/MainPage.js
new file mode 100644
index 0000000..f102b37
--- /dev/null
+++ b/src/components/main-page/MainPage.js
@@ -0,0 +1,11 @@
+import Component from '../component/index';
+
+class MainPage extends Component {
+ constructor (mainNodeSelector, parentNode) {
+ super(mainNodeSelector, parentNode);
+
+ this.mainNode.textContent = 'Главная страница';
+ }
+}
+
+export default MainPage;
diff --git a/src/components/main-page/index.js b/src/components/main-page/index.js
new file mode 100644
index 0000000..3634ebe
--- /dev/null
+++ b/src/components/main-page/index.js
@@ -0,0 +1,3 @@
+import MainPage from './MainPage';
+
+export default MainPage;
diff --git a/src/components/router-pages-container/RouterPagesContainer.css b/src/components/router-pages-container/RouterPagesContainer.css
new file mode 100644
index 0000000..e772840
--- /dev/null
+++ b/src/components/router-pages-container/RouterPagesContainer.css
@@ -0,0 +1,3 @@
+html, body, .PageContainer, .Page {
+ height: 100%;
+}
\ No newline at end of file
diff --git a/src/components/router-pages-container/RouterPagesContainer.js b/src/components/router-pages-container/RouterPagesContainer.js
new file mode 100644
index 0000000..3e75143
--- /dev/null
+++ b/src/components/router-pages-container/RouterPagesContainer.js
@@ -0,0 +1,35 @@
+import Component from '../component/index';
+import routeService from '../../services/RouteService';
+
+import './RouterPagesContainer.css';
+
+class RouterPagesContainer extends Component {
+ routes = [];
+
+ currentPage;
+
+ constructor () {
+ super('#page-container', document.body);
+
+ routeService.onChange(({url}) => {
+ const {pageComponent: PageComponent} = this.routes.find((route) => {
+ return route.url === url;
+ }) || {};
+
+ if (this.currentPage) {
+ this.currentPage.destroy();
+ }
+
+ if (PageComponent) {
+ this.currentPage = new PageComponent('#page', this.mainNode);
+ }
+ });
+
+ }
+
+ addRoutes = (routes) => {
+ this.routes = this.routes.concat(routes);
+ }
+}
+
+export default RouterPagesContainer;
diff --git a/src/components/router-pages-container/index.js b/src/components/router-pages-container/index.js
new file mode 100644
index 0000000..89716a1
--- /dev/null
+++ b/src/components/router-pages-container/index.js
@@ -0,0 +1,5 @@
+import RouterPagesContainer from './RouterPagesContainer';
+
+const routerPagesContainer = new RouterPagesContainer();
+
+export default routerPagesContainer;
diff --git a/src/components/table-component/TableComponent.js b/src/components/table-component/TableComponent.js
index ebb9423..c963f62 100644
--- a/src/components/table-component/TableComponent.js
+++ b/src/components/table-component/TableComponent.js
@@ -5,8 +5,8 @@ import TableColumnComponent from '../table-column-component/index';
import TableRowComponent from '../table-row-component/TableRowComponent';
class TableComponent extends Component {
- constructor () {
- super('#main-table', document.body);
+ constructor (parentNode) {
+ super('#main-table', parentNode);
this.tableHead = this.mainNode.querySelector('.Table__head');
this.tableBody = this.mainNode.querySelector('.Table__body');
diff --git a/src/services/EmitService.js b/src/services/EmitService.js
new file mode 100644
index 0000000..1b1a899
--- /dev/null
+++ b/src/services/EmitService.js
@@ -0,0 +1,48 @@
+/**
+ * Класс для создвания объектов с генерацией событий
+ */
+class EmitService {
+ /**
+ * События компонента
+ * @type {Object}
+ */
+ _events;
+
+ constructor () {
+ this._events = {};
+ }
+
+ /**
+ * Метод подписки на события компонента
+ * @param {string} eventName - событие компонента, на которое будет реагировать обработчик
+ * @param {Function} listener - обработчик события
+ */
+ subscribe = (eventName, listener) => {
+ const listeners = this._events[eventName] || [];
+ this._events[eventName] = [
+ ...listeners,
+ listener,
+ ];
+ }
+
+ /**
+ * Метод генерирует событие
+ * @param {string} eventName - событие, которое необходимо сгенерировать
+ * @param {unknown[]} args - аругемнты, который необходимо передать обработчикам события
+ */
+ next = (eventName, ...args) => {
+ const listeners = this._events[eventName];
+ listeners.forEach((listener) => {
+ listener(...args);
+ });
+ }
+
+ /**
+ * Очищает все события и слушателей
+ */
+ clearSubscribes = () => {
+ this._events = {};
+ }
+}
+
+export default EmitService;
diff --git a/src/services/RouteService.js b/src/services/RouteService.js
new file mode 100644
index 0000000..7af1818
--- /dev/null
+++ b/src/services/RouteService.js
@@ -0,0 +1,41 @@
+import EmitService from './EmitService';
+import {parse} from 'querystring';
+
+const ROUTE_CHANGE = 'routeChange';
+
+class RouteService extends EmitService {
+ constructor () {
+ super();
+
+ this.history = window.history;
+ this._events[ROUTE_CHANGE] = [];
+ }
+
+ getUrlData = () => {
+ return {
+ url: location.pathname,
+ query: parse(location.search.slice(1)),
+ };
+ }
+
+ generateNext = () => {
+ this.next(ROUTE_CHANGE, this.getUrlData());
+ }
+
+ init = () => {
+ this.generateNext();
+ }
+
+ goTo = (url) => {
+ this.history.pushState({}, '', url);
+ this.generateNext();
+ }
+
+ onChange = (listener) => {
+ this.subscribe(ROUTE_CHANGE, listener);
+ }
+}
+
+const routeService = new RouteService();
+
+export default routeService;
diff --git a/webpack.config.js b/webpack.config.js
index 8101aa7..01a5d32 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -18,6 +18,7 @@ module.exports = (env, argv) => ({
devServer: {
compress: true,
port: 9000,
+ historyApiFallback: true,
},
optimization: {
splitChunks: {