menu + catelog REBUILD + new icons
This commit is contained in:
parent
5509b97148
commit
16be453b07
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\controllers;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\controllers\core,
|
||||
mirzaev\arming_bot\models\session,
|
||||
mirzaev\arming_bot\models\account as model;
|
||||
|
||||
// Framework for ArangoDB
|
||||
use mirzaev\arangodb\document;
|
||||
|
||||
/**
|
||||
* Controller of account
|
||||
*
|
||||
* @package mirzaev\arming_bot\controllers
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class account extends core
|
||||
{
|
||||
/**
|
||||
* Registry of errors
|
||||
*/
|
||||
protected array $errors = [
|
||||
'session' => [],
|
||||
'account' => []
|
||||
];
|
||||
|
||||
/**
|
||||
* Write to the buffer
|
||||
*
|
||||
* @param array $parameters Parameters of the request (POST + GET)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write(array $parameters = []): void
|
||||
{
|
||||
if (!empty($parameters) && $this->account instanceof model) {
|
||||
// Found data of the program and active account
|
||||
|
||||
foreach ($parameters as $name => $value) {
|
||||
// Iterate over parameters
|
||||
|
||||
// Validation of the parameter
|
||||
if (mb_strlen($value) > 4096) continue;
|
||||
|
||||
// Convert name to multidimensional array
|
||||
foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value];
|
||||
|
||||
// Write data of to the buffer parameter in the implement object of account document from ArangoDB
|
||||
$this->account->buffer = $parameter + $this->account->buffer ?? [];
|
||||
}
|
||||
|
||||
// Write from implement object to account document from ArangoDB
|
||||
document::update($this->account->__document(), $this->errors['account']);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,11 @@ namespace mirzaev\arming_bot\controllers;
|
|||
use mirzaev\arming_bot\controllers\core,
|
||||
mirzaev\arming_bot\models\entry,
|
||||
mirzaev\arming_bot\models\category,
|
||||
mirzaev\arming_bot\models\product;
|
||||
mirzaev\arming_bot\models\product,
|
||||
mirzaev\arming_bot\models\menu;
|
||||
|
||||
// Library for ArangoDB
|
||||
use ArangoDBClient\Document as _document;
|
||||
|
||||
/**
|
||||
* Controller of catalog
|
||||
|
@ -24,6 +28,7 @@ final class catalog extends core
|
|||
protected array $errors = [
|
||||
'session' => [],
|
||||
'account' => [],
|
||||
'menu' => [],
|
||||
'catalog' => []
|
||||
];
|
||||
|
||||
|
@ -34,64 +39,160 @@ final class catalog extends core
|
|||
*/
|
||||
public function index(array $parameters = []): ?string
|
||||
{
|
||||
// Initializing identifier of a category
|
||||
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
||||
$identifier = $matches[0] ?? null;
|
||||
// Validating
|
||||
if (!empty($parameters['category']) && preg_match('/[\d]+/', $parameters['category'], $matches)) $category = (int) $matches[0];
|
||||
|
||||
// Initializint the buffer of respnse
|
||||
$html = [];
|
||||
if (isset($category)) {
|
||||
// Received and validated identifier of the category
|
||||
|
||||
if (!empty($parameters['identifier'])) {
|
||||
// Передана категория (идентификатор)
|
||||
|
||||
// Инициализация актуальной категории
|
||||
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => (int) $identifier], errors: $this->errors['catalog']);
|
||||
// Initialize of category
|
||||
$category = category::_read('d.identifier == @identifier', parameters: ['identifier' => $category], errors: $this->errors['catalog']);
|
||||
|
||||
if ($category instanceof category) {
|
||||
// Found the category
|
||||
|
||||
// Поиск категорий или товаров входящих в актуальную категорию
|
||||
// Write to the response buffer
|
||||
$response['category'] = ['name' => $category->name ?? null];
|
||||
|
||||
// Search for categories that are descendants of $category
|
||||
$entries = entry::search(
|
||||
document: $category,
|
||||
amount: 30,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
|
||||
// Объявление буферов категорий и товаров (важно - в единственном числе, по параметру из базы данных)
|
||||
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
|
||||
$category = $product = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// Перебор вхождений
|
||||
// Iterate over entries (descendands)
|
||||
|
||||
// Запись массивов категорий и товаров ($category и $product) в буфер глобальной переменной шаблонизатора
|
||||
// Write entry to the buffer of entries (sort by $category and $product)
|
||||
${$entry->_type}[] = $entry;
|
||||
}
|
||||
|
||||
// Запись категорий из буфера в глобальную переменную шаблонизатора
|
||||
// Write to the buffer of global variables of view templater
|
||||
$this->view->categories = $category;
|
||||
|
||||
// Запись товаров из буфера в глобальную переменную шаблонизатора
|
||||
// Write to the buffer of global variables of view templater
|
||||
$this->view->products = $product;
|
||||
|
||||
// Generating filters
|
||||
// Delete buffers
|
||||
unset($category, $product);
|
||||
}
|
||||
} else if (!isset($parameters['category'])) {
|
||||
// Not received identifie of the category
|
||||
|
||||
// Search for root ascendants categories
|
||||
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']);
|
||||
}
|
||||
|
||||
// Validating
|
||||
if (!empty($parameters['product']) && preg_match('/[\d]+/', $parameters['product'], $matches)) $product = (int) $matches[0];
|
||||
|
||||
if (isset($product)) {
|
||||
// Received and validated identifier of the product
|
||||
|
||||
// Search for product and write to the buffer of global variables of view templater
|
||||
$this->view->product = product::read(
|
||||
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||
sort: 'd.created DESC',
|
||||
amount: 1,
|
||||
return: '{name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}',
|
||||
parameters: ['identifier' => $product],
|
||||
language: $this->language,
|
||||
errors: $this->errors['catalog']
|
||||
)[0]?->getAll() ?? null;
|
||||
}
|
||||
|
||||
// Validating
|
||||
if (!empty($parameters['text']) && preg_match('/[\w\s]+/u', $parameters['text'], $matches)) $text = $matches[0];
|
||||
|
||||
if (isset($text)) {
|
||||
// Received and validated text for search
|
||||
|
||||
// Intialize buffer of query parameters
|
||||
$_parameters = [];
|
||||
|
||||
// Initialize buffer of filters query (AQL)
|
||||
$_filters = 'd.deleted != true && d.hidden != true';
|
||||
|
||||
if (isset($this->view->products) && count($this->view->products) > 0) {
|
||||
// Amount of rendered products is more than 0
|
||||
|
||||
// Write to the buffer of filters query (AQL)
|
||||
$_filters .= ' && POSITION(["' . implode('", "', array_map(fn(_document $document): string => $document->getId(), $this->view->products)) . '"], d._id)';
|
||||
}
|
||||
|
||||
// Validating
|
||||
if (!empty($parameters['brand']) && preg_match('/[\w]+/', $parameters['brand'], $matches)) $brand = $matches[0];
|
||||
|
||||
if (isset($brand)) {
|
||||
// Received and validated filter by brand
|
||||
|
||||
// Write to the buffer of filters query (AQL)
|
||||
$_filters .= ' && d.brand.@language == @brand';
|
||||
|
||||
// Write to the buffer of query parameters
|
||||
$_parameters['brand'] = $brand;
|
||||
}
|
||||
|
||||
// Initialize buffer of filters query (AQL)
|
||||
$_sort = 'd.position ASC, d.name ASC, d.created DESC';
|
||||
|
||||
// Validating
|
||||
if (!empty($parameters['sort']) && preg_match('/[\w]+/', $parameters['sort'], $matches)) $sort = $matches[0];
|
||||
|
||||
if (isset($sort)) {
|
||||
// Received and validated sort
|
||||
|
||||
// Write to the buffer of sort query (AQL)
|
||||
$_sort = "d.@sort DESC, $_sort";
|
||||
|
||||
// Write to the buffer of query parameters
|
||||
$_parameters['sort'] = $sort;
|
||||
}
|
||||
|
||||
// Search for products and write to the buffer of global variables of view templater
|
||||
$this->view->products = product::read(
|
||||
search: $text,
|
||||
filter: $_filters,
|
||||
sort: $_sort,
|
||||
amount: 30,
|
||||
language: $this->language,
|
||||
parameters: $_parameters,
|
||||
errors: $this->errors['catalog']
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->view->products) && count($this->view->products) > 0) {
|
||||
// Amount of rendered products is more than 0
|
||||
|
||||
// Search for filters and write to the buffer of global variables of view templater
|
||||
$this->view->filters = [
|
||||
'brands' => product::collect(
|
||||
'd.brand.' . $this->language->name,
|
||||
return: 'd.brand.@language',
|
||||
products: array_map(fn(_document $document): string => $document->getId(), $this->view->products),
|
||||
language: $this->language,
|
||||
errors: $this->errors['catalog']
|
||||
)
|
||||
];
|
||||
|
||||
// Generating HTML and writing to the buffer of response
|
||||
$html = [
|
||||
'filters' => count($this->view->products) > 0 ? $this->view->render('catalog/elements/filters.html') : null,
|
||||
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// Не передана категория
|
||||
|
||||
// Поиск категорий: "categories" (самый верхний уровень)
|
||||
$this->view->categories = entry::ascendants(descendant: new category, errors: $this->errors['catalog']);
|
||||
if (isset($menu)) {
|
||||
//
|
||||
|
||||
} else {
|
||||
// Not received ... menu
|
||||
|
||||
// Search for filters and write to the buffer of global variables of view templater
|
||||
$this->view->menu = menu::_read(
|
||||
return: 'MERGE(d, { name: d.name.@language })',
|
||||
sort: 'd.position ASC, d.created DESC, d._key DESC',
|
||||
amount: 4,
|
||||
parameters: ['language' => $this->language->name],
|
||||
errors: $this->errors['menu']
|
||||
);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
|
@ -102,6 +203,40 @@ final class catalog extends core
|
|||
} else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// POST request
|
||||
|
||||
// Initializing the buffer of response
|
||||
$response = [
|
||||
'title' => $title ?? null
|
||||
];
|
||||
|
||||
if (isset($this->view->categories)) {
|
||||
// Initialized categories
|
||||
|
||||
// Render HTML-code of categories and write to the response buffer
|
||||
$response['html'] ??= [];
|
||||
$response['html']['categories'] = $this->view->render('catalog/elements/categories.html');
|
||||
}
|
||||
|
||||
if (isset($this->view->product)) {
|
||||
// Initialized product
|
||||
|
||||
}
|
||||
|
||||
if (isset($this->view->products)) {
|
||||
// Initialized products
|
||||
|
||||
// Render HTML-code of products and write to the response buffer
|
||||
$response['html'] ??= [];
|
||||
$response['html']['products'] = $this->view->render('catalog/elements/products/2columns.html');
|
||||
}
|
||||
|
||||
if (isset($this->view->filters)) {
|
||||
// Initialized filters
|
||||
|
||||
// Render HTML-code of filters and write to the response buffer
|
||||
$response['html'] ??= [];
|
||||
$response['html']['filters'] = $this->view->render('catalog/elements/filters.html');
|
||||
}
|
||||
|
||||
// Initializing a response headers
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
|
@ -112,11 +247,7 @@ final class catalog extends core
|
|||
|
||||
// Generating the reponse
|
||||
echo json_encode(
|
||||
[
|
||||
'title' => $title ?? '',
|
||||
'html' => $html + [
|
||||
'categories' => $this->view->render('catalog/elements/categories.html'),
|
||||
],
|
||||
$response + [
|
||||
'errors' => $this->errors
|
||||
]
|
||||
);
|
||||
|
@ -135,122 +266,4 @@ final class catalog extends core
|
|||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search
|
||||
*
|
||||
* @param array $parameters Parameters of the request (POST + GET)
|
||||
*/
|
||||
public function search(array $parameters = []): ?string
|
||||
{
|
||||
// Initializing of text fore search
|
||||
preg_match('/[\w\s]+/u', $parameters['text'] ?? '', $matches);
|
||||
$text = $matches[0] ?? null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// POST request
|
||||
|
||||
// Search for products
|
||||
$this->view->products = isset($text) ? product::read(
|
||||
search: $text,
|
||||
filter: 'd.deleted != true && d.hidden != true',
|
||||
sort: 'd.position ASC, d.name ASC, d.created DESC',
|
||||
amount: 30,
|
||||
language: $this->language,
|
||||
errors: $this->errors['catalog']
|
||||
) : [];
|
||||
|
||||
// Initializing a response headers
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
// Initializing of the output buffer
|
||||
ob_start();
|
||||
|
||||
// Generating the reponse
|
||||
echo json_encode(
|
||||
[
|
||||
'title' => $title ?? '',
|
||||
'html' => [
|
||||
'products' => $this->view->render('catalog/elements/products/2columns.html')
|
||||
],
|
||||
'errors' => $this->errors
|
||||
]
|
||||
);
|
||||
|
||||
// Initializing a response headers
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
|
||||
// Sending and deinitializing of the output buffer
|
||||
ob_end_flush();
|
||||
flush();
|
||||
|
||||
// Exit (success)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Product
|
||||
*
|
||||
* @param array $parameters Parameters of the request (POST + GET)
|
||||
*/
|
||||
public function product(array $parameters = []): ?string
|
||||
{
|
||||
// Initializing identifier of a product
|
||||
preg_match('/[\d]+/', $parameters['identifier'] ?? '', $matches);
|
||||
$identifier = $matches[0] ?? null;
|
||||
|
||||
if (!empty($identifier)) {
|
||||
// Received identifier of the product
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// POST request
|
||||
|
||||
// Search for products
|
||||
$product = product::read(
|
||||
filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true",
|
||||
sort: 'd.created DESC',
|
||||
amount: 1,
|
||||
return: '{identifier: d.identifier, name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, images: d.images[*].storage}',
|
||||
parameters: ['identifier' => (int) $identifier],
|
||||
language: $this->language,
|
||||
errors: $this->errors['catalog']
|
||||
)[0]?->getAll();
|
||||
|
||||
// Initializing a response headers
|
||||
header('Content-Type: application/json');
|
||||
header('Content-Encoding: none');
|
||||
header('X-Accel-Buffering: no');
|
||||
|
||||
// Initializing of the output buffer
|
||||
ob_start();
|
||||
|
||||
// Generating the reponse
|
||||
echo json_encode(
|
||||
[
|
||||
'product' => $product,
|
||||
'errors' => $this->errors
|
||||
]
|
||||
);
|
||||
|
||||
// Initializing a response headers
|
||||
header('Content-Length: ' . ob_get_length());
|
||||
|
||||
// Sending and deinitializing of the output buffer
|
||||
ob_end_flush();
|
||||
flush();
|
||||
|
||||
// Exit (success)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,14 +153,14 @@ final class session extends core
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Connect session to the telegram account
|
||||
* Write to the buffer
|
||||
*
|
||||
* @param array $parameters Parameters of the request (POST + GET)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write(array $parameters = []): ?string
|
||||
public function write(array $parameters = []): void
|
||||
{
|
||||
if (!empty($parameters) && $this->session instanceof model) {
|
||||
// Found data of the program and active session
|
||||
|
@ -171,18 +171,15 @@ final class session extends core
|
|||
// Validation of the parameter
|
||||
if (mb_strlen($value) > 4096) continue;
|
||||
|
||||
// Write data of the program to the implement object of session document from ArangoDB
|
||||
$this->session->{$name} = json_decode($value, true, 10);
|
||||
// Convert name to multidimensional array
|
||||
foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value];
|
||||
|
||||
// Write data of to the buffer parameter in the implement object of session document from ArangoDB
|
||||
$this->session->buffer = $parameter + ($this->session->buffer ?? []);
|
||||
}
|
||||
|
||||
// Write from implement object to session document from ArangoDB
|
||||
document::update($this->session->__document(), $this->errors['session']);
|
||||
|
||||
// Exit (success)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exit (fail)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\arming_bot\models;
|
||||
|
||||
// Files of the project
|
||||
use mirzaev\arming_bot\models\core,
|
||||
mirzaev\arming_bot\models\traits\document as arangodb_document_trait,
|
||||
mirzaev\arming_bot\models\interfaces\document as arangodb_document_interface;
|
||||
|
||||
|
||||
/**
|
||||
* Model of menu
|
||||
*
|
||||
* @package mirzaev\arming_bot\models
|
||||
*
|
||||
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class menu extends core implements arangodb_document_interface
|
||||
{
|
||||
use arangodb_document_trait;
|
||||
|
||||
/**
|
||||
* Name of the collection in ArangoDB
|
||||
*/
|
||||
final public const string COLLECTION = 'menu';
|
||||
}
|
|
@ -214,12 +214,16 @@ final class product extends core
|
|||
* Collect parameter from all products
|
||||
*
|
||||
* @param string $return Return (AQL path)
|
||||
* @param array $products Array with products system identifiers ["_id", "_id", "_id"...]
|
||||
* @param array &$errors Registry of errors
|
||||
*
|
||||
* @return array Array with found unique parameter values from all products (can be empty)
|
||||
*/
|
||||
public static function collect(
|
||||
string $return = 'd._key',
|
||||
array $products = [],
|
||||
language $language = language::en,
|
||||
array $parameters = [],
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
|
@ -230,13 +234,16 @@ final class product extends core
|
|||
sprintf(
|
||||
<<<'AQL'
|
||||
FOR d IN @@collection
|
||||
%s
|
||||
RETURN DISTINCT %s
|
||||
AQL,
|
||||
empty($products) ? '' : 'FILTER POSITION(["' . implode('", "', $products) . '"], d._id)',
|
||||
empty($return) ? 'd._key' : $return
|
||||
),
|
||||
[
|
||||
'@collection' => static::COLLECTION,
|
||||
],
|
||||
'language' => $language->name,
|
||||
] + $parameters,
|
||||
errors: $errors
|
||||
)) {
|
||||
// Found parameters
|
||||
|
|
|
@ -39,12 +39,14 @@ $router = new router;
|
|||
// Initialize routes
|
||||
$router
|
||||
->write('/', 'catalog', 'index', 'GET')
|
||||
->write('/search', 'catalog', 'search', 'POST')
|
||||
->write('/', 'catalog', 'index', 'POST')
|
||||
->write('/account/write', 'account', 'write', 'POST')
|
||||
->write('/session/write', 'session', 'write', 'POST')
|
||||
->write('/session/connect/telegram', 'session', 'telegram', 'POST')
|
||||
->write('/category/$identifier', 'catalog', 'index', 'POST')
|
||||
->write('/category', 'catalog', 'index', 'POST')
|
||||
->write('/product/$identifier', 'catalog', 'product', 'POST');
|
||||
/* ->write('/category/$identifier', 'catalog', 'index', 'POST') */
|
||||
/* ->write('/category', 'catalog', 'index', 'POST') */
|
||||
/* ->write('/product/$identifier', 'catalog', 'product', 'POST') */
|
||||
;
|
||||
|
||||
/*
|
||||
|
||||
|
|
|
@ -72,9 +72,10 @@ import("/js/core.js").then(() =>
|
|||
}, 3000);
|
||||
|
||||
if (core.telegram.api.initData.length > 0) {
|
||||
core.request(
|
||||
core
|
||||
.request(
|
||||
"/session/connect/telegram",
|
||||
core.telegram.api.initData,
|
||||
core.telegram.api.initData
|
||||
)
|
||||
.then((json) => {
|
||||
if (
|
||||
|
@ -93,9 +94,8 @@ import("/js/core.js").then(() =>
|
|||
const a =
|
||||
core.status_account.getElementsByTagName("a")[0];
|
||||
a.setAttribute("onclick", "core.account.profile()");
|
||||
a.innerText = json.domain.length > 0
|
||||
? "@" + json.domain
|
||||
: "ERROR";
|
||||
a.innerText =
|
||||
json.domain.length > 0 ? "@" + json.domain : "ERROR";
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -109,6 +109,34 @@ import("/js/core.js").then(() =>
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer
|
||||
*/
|
||||
static buffer = class buffer {
|
||||
/**
|
||||
* Write to the account buffer
|
||||
*
|
||||
* @param {string} name Name of the parameter
|
||||
* @param {string} value Value of the parameter (it can be JSON)
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static write(name, value) {
|
||||
if (typeof name === "string" && typeof value === "string") {
|
||||
//
|
||||
|
||||
// Send request to the server
|
||||
core.request(
|
||||
"/account/write",
|
||||
`${name}=${value}`,
|
||||
"POST",
|
||||
{},
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,20 @@ import("/js/core.js").then(() =>
|
|||
}, 5000);
|
||||
|
||||
function initialization() {
|
||||
core.session.telegram();
|
||||
//
|
||||
const { initData, initDataUnsafe, ...data } = core.telegram.api;
|
||||
|
||||
//
|
||||
core.session.buffer.write("telegram_program", JSON.stringify(data));
|
||||
|
||||
if (core.telegram.api.initData.length > 0) {
|
||||
//
|
||||
|
||||
//
|
||||
core.account.authentication();
|
||||
}
|
||||
|
||||
//
|
||||
core.telegram.api.ready();
|
||||
}
|
||||
})
|
||||
|
|
|
@ -29,81 +29,113 @@ import("/js/core.js").then(() =>
|
|||
// Write to the core
|
||||
core.catalog = class catalog {
|
||||
/**
|
||||
* Registry of filters (instead of cookies)
|
||||
* Parameters of search
|
||||
*
|
||||
* Will be converted to the body of the GET request ("?name=value&name2=value2")
|
||||
*/
|
||||
static filters = new Map([
|
||||
static parameters = new Map([
|
||||
["text", null],
|
||||
["category", null],
|
||||
["sort", null],
|
||||
["brand", null],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Select a category (interface)
|
||||
* Search (interface)
|
||||
*
|
||||
* @param {HTMLElement} button Button of a category <a>
|
||||
* @param {bool} clean Clear search bar?
|
||||
* @param {Event} event Event (keyup)
|
||||
* @param {HTMLElement} element Search bar <input>
|
||||
* @param {bool} force Ignore the damper?
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static category(button, clean = true, force = false) {
|
||||
// Initializing identifier of the category
|
||||
const identifier = button.getAttribute(
|
||||
"data-category-identifier",
|
||||
);
|
||||
static search(event, element, force = false) {
|
||||
if (typeof element === "undefined") {
|
||||
element = document.getElementById("search");
|
||||
}
|
||||
|
||||
this._category(identifier, clean, force);
|
||||
element.classList.remove("error");
|
||||
|
||||
if (element.value.length === 1) {
|
||||
return;
|
||||
} else {
|
||||
if (typeof event === "undefined") {
|
||||
//
|
||||
|
||||
this._search(element, true);
|
||||
} else if (event.keyCode === 13) {
|
||||
// Button: "enter"
|
||||
|
||||
element.setAttribute("disabled", true);
|
||||
|
||||
this._search(element, force);
|
||||
} else {
|
||||
// Button: any
|
||||
|
||||
this._search(element, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a category (damper)
|
||||
* Search (damper)
|
||||
*
|
||||
* @param {HTMLElement} button Button of category <a>
|
||||
* @param {bool} clean Clear search bar?
|
||||
* @param {HTMLElement} element Search bar <input>
|
||||
* @param {bool} force Ignore the damper?
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static _category = core.damper(
|
||||
(...variables) => this.__category(...variables),
|
||||
400,
|
||||
static _search = core.damper(
|
||||
(...variables) => this.__search(...variables),
|
||||
1400,
|
||||
2,
|
||||
);
|
||||
|
||||
/**
|
||||
* Select a category (system)
|
||||
* Search (system)
|
||||
*
|
||||
* @param {string} identifier Identifier of the category
|
||||
* @param {bool} clean Clear search bar?
|
||||
* @param {HTMLElement} element Search bar <input>
|
||||
*
|
||||
* @return {Promise} Request to the server
|
||||
*
|
||||
* @todo add animations of errors
|
||||
*/
|
||||
static __category(identifier, clean = true) {
|
||||
if (typeof identifier === "string") {
|
||||
// Received required parameters
|
||||
static __search(element) {
|
||||
if (typeof element === "undefined") {
|
||||
element = document.getElementById("search");
|
||||
}
|
||||
|
||||
const urn = Array.from(this.parameters)
|
||||
.filter(([k, v]) =>
|
||||
typeof v === "string" || typeof v === "number"
|
||||
)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join("&");
|
||||
|
||||
return core.request(
|
||||
"/category" +
|
||||
("/" + identifier).replace(/^\/*/, "/").trim().replace(
|
||||
/\/*$/,
|
||||
"",
|
||||
),
|
||||
"/",
|
||||
urn,
|
||||
)
|
||||
.then((json) => {
|
||||
element.removeAttribute("disabled");
|
||||
element.focus();
|
||||
|
||||
if (
|
||||
json.errors !== null &&
|
||||
typeof json.errors === "object" &&
|
||||
json.errors.length > 0
|
||||
) {
|
||||
// Errors received
|
||||
|
||||
element.classList.add("error");
|
||||
} else {
|
||||
// Errors not received
|
||||
|
||||
if (clean) {
|
||||
// Clearing the search bar
|
||||
const search = core.main.querySelector(
|
||||
'search[data-section="search"]>input',
|
||||
history.pushState(
|
||||
{},
|
||||
urn,
|
||||
urn,
|
||||
);
|
||||
if (search instanceof HTMLElement) search.value = "";
|
||||
}
|
||||
|
||||
if (
|
||||
typeof json.title === "string" &&
|
||||
|
@ -118,6 +150,12 @@ import("/js/core.js").then(() =>
|
|||
title.innerText = json.title;
|
||||
}
|
||||
|
||||
// Deinitialization of the categories
|
||||
const categories = core.main.querySelector(
|
||||
'section[data-catalog-type="categories"]',
|
||||
);
|
||||
// if (categories instanceof HTMLElement) categories.remove();
|
||||
|
||||
if (
|
||||
typeof json.html.categories === "string" &&
|
||||
json.html.categories.length > 0
|
||||
|
@ -164,179 +202,6 @@ import("/js/core.js").then(() =>
|
|||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof json.html.filters === "string" &&
|
||||
json.html.filters.length > 0
|
||||
) {
|
||||
// Received filters (reinitialization of the filters)
|
||||
|
||||
const filters = core.main.querySelector(
|
||||
'section[data-section="filters"]',
|
||||
);
|
||||
|
||||
if (filters instanceof HTMLElement) {
|
||||
// Found list of filters
|
||||
|
||||
filters.outerHTML = json.html.filters;
|
||||
} else {
|
||||
// Not found list of categories
|
||||
|
||||
const element = document.createElement("section");
|
||||
|
||||
const categories = core.main.querySelector(
|
||||
'section[data-catalog-type="categories"]',
|
||||
);
|
||||
|
||||
if (categories instanceof HTMLElement) {
|
||||
core.main.insertBefore(
|
||||
element,
|
||||
categories.nextSibling,
|
||||
);
|
||||
} else {
|
||||
core.main.append(element);
|
||||
}
|
||||
|
||||
element.outerHTML = json.html.filters;
|
||||
}
|
||||
} else {
|
||||
// Not received categories (deinitialization of the categories)
|
||||
|
||||
const filters = core.main.querySelector(
|
||||
'section[data-section="filters"',
|
||||
);
|
||||
|
||||
if (filters instanceof HTMLElement) {
|
||||
filters.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
typeof json.html.products === "string" &&
|
||||
json.html.products.length > 0
|
||||
) {
|
||||
// Received products (reinitialization of the products)
|
||||
|
||||
const products = core.main.querySelector(
|
||||
'section[data-catalog-type="products"]',
|
||||
);
|
||||
|
||||
if (products instanceof HTMLElement) {
|
||||
// Found list of products
|
||||
|
||||
products.outerHTML = json.html.products;
|
||||
} else {
|
||||
// Not found list of products
|
||||
|
||||
const element = document.createElement("section");
|
||||
core.main.append(element);
|
||||
element.outerHTML = json.html.products;
|
||||
}
|
||||
} else {
|
||||
// Not received products (deinitialization of the products)
|
||||
|
||||
const products = core.main.querySelector(
|
||||
'section[data-catalog-type="products"',
|
||||
);
|
||||
if (products instanceof HTMLElement) {
|
||||
products
|
||||
.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a category (interface)
|
||||
*
|
||||
* @param {Event} event Event (keyup)
|
||||
* @param {HTMLElement} element Search bar <input>
|
||||
* @param {bool} force Ignore the damper?
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static search(event, element, force = false) {
|
||||
element.classList.remove("error");
|
||||
|
||||
if (element.value.length === 1) {
|
||||
return;
|
||||
} else if (event.keyCode === 13) {
|
||||
// Button: "enter"
|
||||
|
||||
element.setAttribute("disabled", true);
|
||||
|
||||
this._search(element, force);
|
||||
} else {
|
||||
// Button: any
|
||||
|
||||
this._search(element, force);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search in the catalog (damper)
|
||||
*
|
||||
* @param {HTMLElement} button Button of category <a>
|
||||
* @param {bool} clean Clear search bar?
|
||||
* @param {bool} force Ignore the damper?
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static _search = core.damper(
|
||||
(...variables) => this.__search(...variables),
|
||||
1400,
|
||||
2,
|
||||
);
|
||||
|
||||
/**
|
||||
* Search in the catalog (system)
|
||||
*
|
||||
* @param {HTMLElement} element Search bar <input>
|
||||
*
|
||||
* @return {Promise} Request to the server
|
||||
*
|
||||
* @todo add animations of errors
|
||||
*/
|
||||
static __search(element) {
|
||||
return this.__category("/", false)
|
||||
.then(function () {
|
||||
core.request("/search", `text=${element.value}`)
|
||||
.then((json) => {
|
||||
element.removeAttribute("disabled");
|
||||
element.focus();
|
||||
|
||||
if (
|
||||
json.errors !== null &&
|
||||
typeof json.errors === "object" &&
|
||||
json.errors.length > 0
|
||||
) {
|
||||
// Errors received
|
||||
|
||||
element.classList.add("error");
|
||||
} else {
|
||||
// Errors not received
|
||||
|
||||
if (
|
||||
typeof json.title === "string" &&
|
||||
json.title.length > 0
|
||||
) {
|
||||
// Received the page title
|
||||
|
||||
// Initialize a link to the categories list
|
||||
const title =
|
||||
core.main.getElementsByTagName("h2")[0];
|
||||
|
||||
// Write the title
|
||||
title.innerText = json.title;
|
||||
}
|
||||
|
||||
// Deinitialization of the categories
|
||||
const categories = core.main.querySelector(
|
||||
'section[data-catalog-type="categories"]',
|
||||
);
|
||||
// if (categories instanceof HTMLElement) categories.remove();
|
||||
|
||||
if (
|
||||
typeof json.html.products === "string" &&
|
||||
json.html.products.length > 0
|
||||
|
@ -361,6 +226,9 @@ import("/js/core.js").then(() =>
|
|||
);
|
||||
|
||||
if (categories instanceof HTMLElement) {
|
||||
//
|
||||
|
||||
//
|
||||
core.main.insertBefore(
|
||||
element,
|
||||
categories.nextSibling,
|
||||
|
@ -368,6 +236,9 @@ import("/js/core.js").then(() =>
|
|||
|
||||
element.outerHTML = json.html.products;
|
||||
} else {
|
||||
//
|
||||
|
||||
//
|
||||
const search = core.main.querySelector(
|
||||
'search[data-section="search"]',
|
||||
);
|
||||
|
@ -388,13 +259,13 @@ import("/js/core.js").then(() =>
|
|||
const products = core.main.querySelector(
|
||||
'section[data-catalog-type="products"]',
|
||||
);
|
||||
|
||||
if (products instanceof HTMLElement) {
|
||||
products.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -439,7 +310,10 @@ import("/js/core.js").then(() =>
|
|||
if (typeof identifier === "string") {
|
||||
//
|
||||
|
||||
return core.request(`/product/${identifier}`)
|
||||
//
|
||||
const urn = `?product=${identifier}`;
|
||||
|
||||
return core.request(urn)
|
||||
.then((json) => {
|
||||
if (
|
||||
json.errors !== null &&
|
||||
|
@ -471,7 +345,7 @@ import("/js/core.js").then(() =>
|
|||
|
||||
const name = document.createElement("span");
|
||||
name.classList.add("name");
|
||||
name.setAttribute("title", json.product.identifier);
|
||||
name.setAttribute("title", identifier);
|
||||
name.innerText = json.product.name;
|
||||
|
||||
const exit = document.createElement("a");
|
||||
|
@ -671,8 +545,9 @@ import("/js/core.js").then(() =>
|
|||
);
|
||||
|
||||
history.pushState(
|
||||
{ product_card: json.product.identifier },
|
||||
{ identifier },
|
||||
json.product.name,
|
||||
urn,
|
||||
);
|
||||
|
||||
// блокировка закрытия карточки
|
||||
|
@ -681,7 +556,12 @@ import("/js/core.js").then(() =>
|
|||
wrap.addEventListener("mousedown", _from);
|
||||
wrap.addEventListener("touchstart", _from);
|
||||
|
||||
const remove = () => {
|
||||
const remove = (event) => {
|
||||
if (
|
||||
typeof event === "undefined" ||
|
||||
event.type !== "popstate"
|
||||
) history.back();
|
||||
|
||||
wrap.remove();
|
||||
images.removeEventListener(
|
||||
"mousedown",
|
||||
|
|
|
@ -46,13 +46,43 @@ const core = class core {
|
|||
method = "POST",
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
type = "json",
|
||||
) {
|
||||
return await fetch(encodeURI(address), { method, headers, body })
|
||||
.then((response) => response[type]());
|
||||
.then((response) => type === null || response[type]());
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer
|
||||
*/
|
||||
static buffer = class buffer {
|
||||
/**
|
||||
* Write to buffers
|
||||
*
|
||||
* @param {string} name Name of the parameter
|
||||
* @param {string} value Value of the parameter (it can be JSON)
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static write(name, value) {
|
||||
if (typeof this.session === "function") {
|
||||
// Initialized the session implement object
|
||||
|
||||
// Write to the session buffer
|
||||
core.session.buffer.write(name, value);
|
||||
}
|
||||
|
||||
if (typeof this.account === "function") {
|
||||
// Initialized the account implement object
|
||||
|
||||
// Write to the account buffer
|
||||
core.account.buffer.write(name, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Сгенерировать окно выбора действия
|
||||
*
|
||||
|
|
|
@ -27,19 +27,32 @@ import("/js/core.js").then(() =>
|
|||
// Write to the core
|
||||
core.session = class session {
|
||||
/**
|
||||
* Send data of Telegram program settings to the session
|
||||
* Buffer
|
||||
*/
|
||||
static buffer = class buffer {
|
||||
/**
|
||||
* Write to the session buffer
|
||||
*
|
||||
* @param {string} name Name of the parameter
|
||||
* @param {string} value Value of the parameter (it can be JSON)
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
static telegram() {
|
||||
static write(name, value) {
|
||||
if (typeof name === "string" && typeof value === "string") {
|
||||
//
|
||||
const { initData, initDataUnsafe, ...data } = core.telegram.api;
|
||||
|
||||
// Send request to the server
|
||||
core.request(
|
||||
"/session/write",
|
||||
"telegram=" + JSON.stringify(data),
|
||||
`${name}=${value}`,
|
||||
"POST",
|
||||
{},
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
i.icon.arrow.top.left {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
i.icon.arrow.top.left::after,
|
||||
i.icon.arrow.top.left::before {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
i.icon.arrow.top.left::after {
|
||||
width: 10px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
transform: rotate(45deg);
|
||||
bottom: 8px;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
i.icon.arrow.top.left::before {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
left: 4px;
|
||||
top: 4px;
|
||||
border-top: 2px solid;
|
||||
border-left: 2px solid;
|
||||
}
|
|
@ -4,7 +4,6 @@ i.icon.shopping.cart {
|
|||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
transform: scale(var(--ggs, 1));
|
||||
width: 20px;
|
||||
height: 21px;
|
||||
background:
|
||||
|
|
|
@ -25,6 +25,9 @@ a {
|
|||
}
|
||||
|
||||
body {
|
||||
--gap: 16px;
|
||||
--width: calc(100% - var(--gap) * 2);
|
||||
--offset-x: 2%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
@ -39,10 +42,20 @@ body {
|
|||
|
||||
aside {}
|
||||
|
||||
header {}
|
||||
header {
|
||||
container-type: inline-size;
|
||||
container-name: header;
|
||||
margin: 2rem 0 1rem;
|
||||
padding: 0 var(--offset-x);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 26px;
|
||||
}
|
||||
|
||||
main {
|
||||
--offset-x: 2%;
|
||||
container-type: inline-size;
|
||||
container-name: main;
|
||||
padding: 0 var(--offset-x);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -56,8 +69,6 @@ main>section:last-child {
|
|||
}
|
||||
|
||||
main>*[data-section] {
|
||||
--gap: 16px;
|
||||
--width: calc(100% - var(--gap) * 2);
|
||||
width: var(--width);
|
||||
}
|
||||
|
||||
|
@ -150,7 +161,7 @@ a[type="button"] {
|
|||
|
||||
h1,
|
||||
h2 {
|
||||
margin: 28px 0 0;
|
||||
margin: 1rem 0 0;
|
||||
}
|
||||
|
||||
footer {}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
header>nav#menu {
|
||||
container-type: inline-size;
|
||||
container-name: menu;
|
||||
width: var(--width);
|
||||
min-height: 3rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 1rem;
|
||||
border-radius: 1.375rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
header>nav#menu>a[type="button"] {
|
||||
height: 3rem;
|
||||
padding: unset;
|
||||
border-radius: 1.375rem;
|
||||
color: var(--unsafe-color, var(--tg-theme-button-text-color));
|
||||
background-color: var(--unsafe-background-color, var(--tg-theme-button-color));
|
||||
}
|
||||
|
||||
header>nav#menu>a[type=button]>:first-child {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
header>nav#menu>a[type="button"]>* {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
@container header (max-width: 450px) {
|
||||
header>nav#menu > a[type="button"]:nth-child(1)> i.icon+span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container header (max-width: 350px) {
|
||||
header>nav#menu > a[type="button"]:nth-child(2)> i.icon+span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container header (max-width: 250px) {
|
||||
header>nav#menu > a[type="button"]:nth-child(3)> i.icon+span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container header (max-width: 150px) {
|
||||
header>nav#menu > a[type="button"]> i.icon+span {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -2,19 +2,15 @@
|
|||
<section class="unselectable" data-section="catalog" data-catalog-type="categories"
|
||||
data-catalog-level="{{ level ?? 0 }}">
|
||||
{% for category in categories %}
|
||||
<a id="{{ category.getId() }}" class="category" type="button"
|
||||
onclick="core.catalog.parameters.set('category', {{ category.identifier }}); core.catalog.search()"
|
||||
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search())"
|
||||
tabindex="3">
|
||||
{% if category.images %}
|
||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="core.catalog.category(this)" onkeydown="event.keyCode === 13 && core.catalog.category(this)"
|
||||
data-category-identifier="{{ category.identifier }}" tabindex="3">
|
||||
<img src="{{ category.images.0.storage }}"
|
||||
alt="{{ category.name[language] }}" ondrugstart="return false;">
|
||||
<p>{{ category.name[language] }}</p>
|
||||
</a>
|
||||
{% else %}
|
||||
<a id="{{ category.getId() }}" class="category" type="button" onclick="core.catalog.category(this)" onkeydown="event.keyCode === 13 && core.catalog.category(this)"
|
||||
data-category-identifier="{{ category.identifier }}" tabindex="3">
|
||||
<p>{{ category.name[language] }}</p>
|
||||
</a>
|
||||
<img src="{{ category.images.0.storage }}" alt="{{ category.name[language] }}" ondrugstart="return false;">
|
||||
{% endif %}
|
||||
<p>{{ category.name[language] }}</p>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
{% if filters is not empty %}
|
||||
<section class="unselectble" data-section="filters">
|
||||
<section class="unselectable" data-type="select" data-filter="brand" tabindex="4">
|
||||
<input name="brand" type="radio" id="brand_title"{% if session.filters.brand is empty %} checked{% endif %}>
|
||||
{% set cheked_brand = account.buffer.filters.brand ?? session.buffer.filters.brand %}
|
||||
<input name="brand" type="radio" id="brand_title" onclick="core.catalog.parameters.set('brand', null); core.catalog.search()" {% if checked_brand
|
||||
is empty %} checked{% endif %}>
|
||||
<label for="brand_title" type="button">{{ language == 'ru' ? 'Бренд' : 'Brand' }}</label>
|
||||
<input name="brand" type="radio" id="brand_all">
|
||||
<input name="brand" type="radio" id="brand_all" onclick="core.catalog.parameters.set('brand', null); core.catalog.search()">
|
||||
<label for="brand_all" type="button">{{ language == 'ru' ? 'Все бренды' : 'All brands' }}</label>
|
||||
{% for brand in filters.brands %}
|
||||
<input name="brand" type="radio" id="brand_{{ loop.index }}"{% if session.filters.brand == brand %} checked{% endif %}>
|
||||
<input name="brand" type="radio" id="brand_{{ loop.index }}" onclick="core.catalog.parameters.set('brand', '{{ brand }}'); core.catalog.search()" {%
|
||||
if brand==checked_brand %} checked{% endif %}>
|
||||
<label for="brand_{{ loop.index }}" type="button">{{ brand }}</label>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<search data-section="search">
|
||||
<label><i class="icon search"></i></label>
|
||||
<input id="search" placeholder="Поиск по каталогу" type="search" onkeyup="event.keyCode !== 9 && core.catalog.search(event, this)" tabindex="1"/>
|
||||
{% set search = account.buffer.search ?? session.buffer.search %}
|
||||
<input id="search" placeholder="Поиск по каталогу" type="search" tabindex="1"
|
||||
onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); core.catalog.search(event, this)"{% if search is not empty %} value="{{ search }}"{% endif %} />
|
||||
</search>
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
{% use "/themes/default/menu.html" with css as menu_css, body as menu_body, js as menu_js %}
|
||||
|
||||
{% block css %}
|
||||
{{ block('menu_css') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<header>
|
||||
{{ block('menu_body') }}
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ block('menu_js') }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
{{ block('header_css') }}
|
||||
{{ block('connection_css') }}
|
||||
{{ block('account_css') }}
|
||||
{{ block('header_css') }}
|
||||
{{ block('footer_css') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{{ block('header') }}
|
||||
{{ block('connection_body') }}
|
||||
{{ block('account_body') }}
|
||||
{{ block('header') }}
|
||||
<main>
|
||||
{% block main %}
|
||||
{{ main|raw }}
|
||||
|
@ -27,8 +27,8 @@
|
|||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
{{ block('header_js') }}
|
||||
{{ block('connection_js') }}
|
||||
{{ block('account_js') }}
|
||||
{{ block('header_js') }}
|
||||
{{ block('footer_js') }}
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{% block css %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/default/css/menu.css">
|
||||
{% for button in menu %}
|
||||
{% if button.icon %}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/default/css/icons/{{ button.icon|replace({' ': '-'}) }}.css">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<nav id="menu">
|
||||
{% for button in menu %}
|
||||
<a href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button" class="unselectable"
|
||||
title="{{ button.name }}"
|
||||
style="order: {{ button.position }};{% for target, color in button.color %} --unsafe-{{ target }}: {{ color|e }};{% endfor %}">
|
||||
{% if button.icon %}
|
||||
<i class="icon {{ button.icon }}"></i>
|
||||
{% endif %}
|
||||
<span>{{ button.name }}</span>
|
||||
{% if button.image.storage %}
|
||||
<img src="{{ button.image.storage }}" alt="{{ button.name }}" ondrugstart="return false;">
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script type="text/javascript" src="/js/menu.js"></script>
|
||||
{% endblock %}
|
|
@ -1,14 +0,0 @@
|
|||
{% extends "/themes/default/index.html" %}
|
||||
|
||||
{% block css %}
|
||||
{{ parent() }}
|
||||
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ parent() }}
|
||||
<script src="/js/catalog.js" defer></script>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue