This commit is contained in:
Arsen Mirzaev Tatyano-Muradovich 2025-04-15 16:31:04 +03:00
parent 80d3b430ef
commit 86ad3f5bfc
25 changed files with 660 additions and 84 deletions

View File

@ -226,9 +226,9 @@ You can copy a clean settings document without comments from here: `/examples/ar
```json ```json
{ {
"status": "active", "status": "active", // Values: "active", "inactive" (string) Status of the settings document?
"project": { "project": {
"name": "PROJECT" "name": "PROJECT" // Name of the projext (string)
}, },
"language": "en", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\language` "language": "en", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\language`
"currency": "usd", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\currency` "currency": "usd", // Will be converted to an instance of enumeration `mirzaev\arming_bot\models\enumerations\currency`
@ -244,13 +244,94 @@ You can copy a clean settings document without comments from here: `/examples/ar
"enabled": true, // Enable the search input field? "enabled": true, // Enable the search input field?
"position": "fixed" // Values: "fixed", "relative" "position": "fixed" // Values: "fixed", "relative"
}, },
"catalog": {
"categories": {
"display": "column", // Values: "row" (flex wrap), "column" (rows) Type of the catalog display
"structure": "lists", // Values: "pages" (pages with categories and products), "lists" (all categories as tree lists on the main page)
"buttons": {
"height": "120px", // Examples: "80px", "120px", "180px" (string|null) Height of buttons
"background": "#fafafa", // Examples: "#fafafa", "yellow" (string|null) Color of buttons background
"separator": {
"enabled": true, // Enable separators?
"width": "60%" // Exaples: "100%", "80%", "60%" (string|null) Width of separators over images (relative to image width from the left)
},
"lists": {
"height": "800px", // Examples: "500px", "100%" (string|null) Maximum height of lists (`max-height` for animations working)
"background": null, // Examples: "#fafafa", "yellow" (string|null) Color of lists
"separator": null, // Examples: "#fafafa", "yellow" (string|null) Color of separators between rows
"separated": true, // Separate lists from its buttons?
"blocks": true, // Blocks instead of plain text?
"arrow": true // Add arrow at the right?
},
"texts": {
"position": {
"vertical": "center" // Values: "top", "center", "bottom" (string|null) Position of texts of ascendants categories
},
"width": "max(8rem, 20%)", // Examples: "60%", "5rem", "max(8rem, 20%)" (string|null) Width of the text section (left side of buttons)
"background": false, // Enable wrapping element for texts?
"title": {
"color": "#020202" // Examples: "#fafafa", "yellow" (string|null) Color of titles
},
"description": {
"color": "#121212" // Examples: "#fafafa", "yellow" (string|null) Color of descriptions
}
},
"images": {
"filter": "contrast(1.2)" // Example: "contrast(1.2)" (string|null) Filter for images
}
}
}
},
"cart": { "cart": {
"enabled": true // Enable the cart button? "enabled": true // Enable the cart button?
}, },
"css": { "css": {
"catalog-button-cart-background": "#40a7e3", "catalog-button-cart-background": "#40a7e3",
"product-button-cart-background": "#40a7e3" "product-button-cart-background": "#40a7e3"
"catalog-button-cart-added-background": "#90be36",
"product-button-cart-added-background": "#90be36"
},
"account": {
"enabled": false // Enable the account section? (works only when opened from telegram `inline-button`)
},
"menu": {
"enabled": true, // Enable the main menu?
"position": "fixed" // Values: "fixed" (fixed to the bottom as a solid line), "relative" (at the top as separated buttons) (stirng) Position of the main menu
},
"header": {
"enabled": true, // Enable the header?
"position": "fixed" // Values: "fixed" (fixed to the bottom), "relative" (at the top) (stirng) Position of the header
},
"acquirings": {
"robokassa": {
"enabled": true, // Enable the Robokassa acquiring?
"mode": "test" // Values: "work", "test" (string) Mode of the Robokassa acquiring
} }
},
"input": {
"deliveries": {
"site": [], // Values: "sim", "name", "destination", "address", "commentary"
"chat": [
"sim",
"name",
"destination",
"address",
"commentary"
] // Values: "sim", "name", "destination", "address", "commentary"
}
},
"deliveries": {
"cdek": {
"enabled": true, // Enable CDEK delivery?
"label": "CDEK" // Name of the CDEK delivery
}
},
"chats": [
{
"id": null, // Example: -1002599391893 (int) (negative number) The telegram chat identifier
"orders": true // Send orders? (for moderators)
}
]
} }
``` ```
@ -284,3 +365,4 @@ You can copy a clean suspension document without comments from here: `/examples/

View File

@ -7,6 +7,7 @@ namespace mirzaev\huesos\controllers;
// Files of the project // Files of the project
use mirzaev\huesos\controllers\core, use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\enumerations\language, mirzaev\huesos\models\enumerations\language,
mirzaev\huesos\models\catalog as model,
mirzaev\huesos\models\entry, mirzaev\huesos\models\entry,
mirzaev\huesos\models\category, mirzaev\huesos\models\category,
mirzaev\huesos\models\product, mirzaev\huesos\models\product,
@ -195,31 +196,26 @@ final class catalog extends core
// Write to the response buffer // Write to the response buffer
$response['category'] = ['name' => $category->name ?? null]; $response['category'] = ['name' => $category->name ?? null];
// Search for categories that are descendants of $category // Searching for entries that are descendants of $category
$entries = entry::search( $search = model::search(
document: $category, document: $category,
amount: 50, amount: 200,
depth: 100,
categories_merge: 'name: v.name.@language', categories_merge: 'name: v.name.@language',
/* products_merge: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})', */ /* products_merge: 'DISTINCT MERGE(d, {name: d.name.@language, description: d.description.@language, compatibility: d.compatibility.@language, brand: d.brand.@language, cost: d.cost.@currency})', */
parameters: ['language' => $this->language->name], parameters: ['language' => $this->language->name],
errors: $this->errors['catalog'] errors: $this->errors['catalog']
); );
// Initialize buffers of entries (in singular, by parameter from ArangoDB) if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
$category = $product = []; // Pages
foreach ($entries as $entry) { // Write to the buffer of global variables of view templater
// Iterate over entries (descendands) $this->view->categories = $search['categories'];
// 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 // Write to the buffer of global variables of view templater
$this->view->categories = $category; $this->view->products = $search['products'];
// Write to the buffer of global variables of view templater
$this->view->products = $product;
if (isset($this->view->products) && count($this->view->products) > 0) { if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0 // Amount of rendered products is more than 0
@ -234,13 +230,51 @@ final class catalog extends core
} else if (!isset($category)) { } else if (!isset($category)) {
// Not received identifier of the category // Not received identifier of the category
// search for root ascendants categories if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
// Pages
// Searching for root ascendants categories
$this->view->categories = entry::ascendants( $this->view->categories = entry::ascendants(
descendant: new category, descendant: new category,
return: 'DISTINCT MERGE(d, { name: d.name.@language})', return: 'DISTINCT MERGE(d, { name: d.name.@language})',
parameters: ['language' => $this->language->name], parameters: ['language' => $this->language->name],
errors: $this->errors['catalog'] errors: $this->errors['catalog']
) ?? null; ) ?? null;
} else if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'lists') {
// Lists
// Initializing the list of all categories
$categories = [];
// Initializing the structure of the list of all categories
$structure = [];
foreach (
entry::ascendants(
descendant: new category,
return: 'DISTINCT MERGE(d, { name: d.name.@language})',
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
) ?? null as $document
) {
// Iterating over found root ascendants categories
// Generating the list (entering into recursion)
static::list(
document: $document,
language: $this->language,
categories: $categories,
structure: $structure,
errors: $this->errors['catalog']
);
}
// Writing the list of all categories into the templater variable
$this->view->categories = $categories;
// Writing the structure of the list of all categories into the templater variable
$this->view->structure = $structure;
}
} }
// Validating @todo add throwing errors // Validating @todo add throwing errors
@ -393,4 +427,56 @@ final class catalog extends core
// Exit (success/fail) // Exit (success/fail)
return null; return null;
} }
/**
* List
*
*
*
* @return void
*/
private static function list(_document $document, language $language, array &$categories = [], array &$structure = [], array &$errors = []): void
{
// Initializing the object
$category = new category;
if (method_exists($category, '__document')) {
// Object can implement a document from ArangoDB
// Writing the instance of product document from ArangoDB to the implement object
$category->__document($document);
// Writing the category into the list of categories
$categories[$category->getId()] = $category;
// Writing the category into the structure of the list categories
$structure[$category->getId()] = [];
// Searching for entries that are descendants of $category
$search = model::search(
document: $category,
amount: 200,
categories_merge: 'name: v.name.@language',
parameters: ['language' => $language->name],
errors: $errors
);
if (count($search['categories']) > 0) {
// Found descendants categories
foreach ($search['categories'] as $document) {
// Iterating over descendants categories
// Entering into descendant level of the recursion for the list generation
static::list(
document: $document,
language: $language,
categories: $categories,
structure: $structure[$category->getId()],
errors: $errors
);
}
}
}
}
} }

View File

@ -45,6 +45,62 @@ final class catalog extends core
yandex::list as folder; yandex::list as folder;
} }
/**
* Search
*
* @param category|product $document Ascendant document
* @param string|null $filter Expression for filtering (AQL)
* @param string|null $sort Expression for sorting (AQL)
* @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
* @param int $page Page
* @param int $amount Amount of documents per page
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
* @param string|null $products_merge Expression with paremeters to return for products (AQL)
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
* @param array &$errors Registry of errors
*
* @return array [categories => [document::class...], $products => [document::class...]]
*/
public static function search(
category|product $document,
?string $filter = 'v.deleted != true && v.hidden != true',
?string $sort = 'v.position ASC, v.created DESC',
int $depth = 1,
int $page = 1,
int $amount = 100,
?string $categories_merge = null,
?string $products_merge = null,
array $parameters = [],
array &$errors = []
): array {
// Search for entries that are descendants of $category
$entries = entry::search(
document: $document,
filter: $filter,
sort: $sort,
depth: $depth,
page: $page,
amount: $amount,
categories_merge: $categories_merge,
products_merge: $products_merge,
parameters: $parameters,
errors: $errors
);
// Initialize buffers of entries (in singular, by parameter from ArangoDB)
$category = $product = [];
foreach ($entries as $entry) {
// Iterate over entries (descendands)
// Write entry to the buffer of entries (sort by $category and $product)
${$entry->_type}[] = $entry;
}
// Exit (success)
return ['categories' => $category, 'products' => $product];
}
/** /**
* Collect parameter from all products * Collect parameter from all products
* *

View File

@ -223,8 +223,9 @@ final class entry extends core implements document_interface, collection_interfa
* @param category|product $document Ascendant document * @param category|product $document Ascendant document
* @param string|null $filter Expression for filtering (AQL) * @param string|null $filter Expression for filtering (AQL)
* @param string|null $sort Expression for sorting (AQL) * @param string|null $sort Expression for sorting (AQL)
* @param int $page Страница * @param int $depth Amount of nodes for traversal search (subcategories/products inside subcategories...)
* @param int $amount Количество товаров на странице * @param int $page Page
* @param int $amount Amount of documents per page
* @param string|null $categories_merge Expression with paremeters to return for categories (AQL) * @param string|null $categories_merge Expression with paremeters to return for categories (AQL)
* @param string|null $products_merge Expression with paremeters to return for products (AQL) * @param string|null $products_merge Expression with paremeters to return for products (AQL)
* @param array $parameters Binded parameters for placeholders ['placeholder' => parameter] * @param array $parameters Binded parameters for placeholders ['placeholder' => parameter]
@ -236,6 +237,7 @@ final class entry extends core implements document_interface, collection_interfa
category|product $document, category|product $document,
?string $filter = 'v.deleted != true && v.hidden != true', ?string $filter = 'v.deleted != true && v.hidden != true',
?string $sort = 'v.position ASC, v.created DESC', ?string $sort = 'v.position ASC, v.created DESC',
int $depth = 1,
int $page = 1, int $page = 1,
int $amount = 100, int $amount = 100,
?string $categories_merge = null, ?string $categories_merge = null,
@ -252,12 +254,13 @@ final class entry extends core implements document_interface, collection_interfa
return is_array($result = collection::execute( return is_array($result = collection::execute(
sprintf( sprintf(
<<<'AQL' <<<'AQL'
FOR v IN 1..1 INBOUND @document GRAPH @graph FOR v IN 1..%u INBOUND @document GRAPH @graph
%s %s
%s %s
LIMIT @offset, @amount LIMIT @offset, @amount
RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s}) RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s})
AQL, AQL,
$depth,
empty($filter) ? '' : "FILTER $filter", empty($filter) ? '' : "FILTER $filter",
empty($sort) ? '' : "SORT $sort", empty($sort) ? '' : "SORT $sort",
empty($categories_merge) ? '' : ", $categories_merge", empty($categories_merge) ? '' : ", $categories_merge",

View File

@ -15,5 +15,5 @@ nav#menu>a#account>img {
height: inherit; height: inherit;
object-fit: contain; object-fit: contain;
border-radius: 100%; border-radius: 100%;
border: 2px solid var(--tg-theme-bottom-bar-bg-color); /* border: 2px solid var(--tg-theme-bottom-bar-bg-color); */
} }

View File

@ -1,14 +1,66 @@
@charset "UTF-8"; @charset "UTF-8";
main>section#categories { main>section#categories:has(a),
main>section#categories:not(:has(a))>ul:has(li.category[type="button"]) {
width: var(--width); width: var(--width);
display: flex; display: flex;
flex-flow: row wrap; flex-flow: var(--catalog-categories-wrap-flex-wrap, row wrap);
flex-direction: var(--catalog-categories-wrap-flex-direction, 'row');
}
main>section#categories:has(a) {
gap: var(--gap, 5px); gap: var(--gap, 5px);
} }
main>section#categories>a.category[type="button"] { main>section#categories:not(:has(a))>ul:has(li.category[type="button"])>ul {
margin-bottom: var(--gap, 5px);
}
main>section#categories ul {
padding-left: 1.3rem;
list-style-type: none;
overflow: hidden;
width: 100%;
}
main>section#categories>li {
line-height: 1rem;
}
main>section#categories li {
/* font-family: "Chalet"; */
}
main>section#categories li.openable {
font-weight: 400;
}
main>section#categories ul>li:has(+ ul) {
z-index: 100;
}
main>section#categories ul>ul {
--offset: 1rem;
--padding: 1rem;
margin-top: calc(var(--offset, 1rem) * -1);
padding-top: calc(var(--offset, 1rem) + var(--padding, 1rem));
padding-bottom: var(--padding, 1rem);
border-radius: 0 0 0.75rem 0.75rem;
display: flex;
flex-direction: column;
gap: 0.3rem;
background: var(--catalog-categories-buttons-lists-background-color, var(--tg-theme-secondary-bg-color));
}
main>section#categories:not(:has(a))>ul:has(li.category[type="button"]) {
margin: unset;
padding: unset;
}
main>section#categories>a.category[type="button"],
main>section#categories>ul>li.category[type="button"] {
--padding: 0.7rem; --padding: 0.7rem;
z-index: unset;
position: relative; position: relative;
height: 23px; height: 23px;
padding: var(--padding); padding: var(--padding);
@ -19,10 +71,11 @@ main>section#categories>a.category[type="button"] {
overflow: hidden; overflow: hidden;
border-radius: 0.75rem; border-radius: 0.75rem;
color: var(--tg-theme-button-text-color); color: var(--tg-theme-button-text-color);
background: unset; background: var(--catalog-categories-buttons-background);
} }
main>section#categories>a.category[type="button"]:after { main>section#categories>a.category[type="button"]:not(.column):after,
main>section#categories>ul>li.category[type="button"]:not(.column):after {
z-index: -100; z-index: -100;
position: absoulute; position: absoulute;
left: 0; left: 0;
@ -44,13 +97,28 @@ main>section#categories:last-child {
padding: unset; padding: unset;
} */ } */
main>section#categories>a.category[type="button"] { main>section#categories>a.category[type="button"],
width: calc(50% - var(--padding) * 2); main>section#categories>ul>li.category[type="button"] {
height: 180px; width: var(--catalog-categories-buttons-width, calc(50% - var(--padding) * 2));
height: var(--catalog-categories-buttons-height, 180px);
padding: unset; padding: unset;
} }
main>section#categories>a.category[type="button"]>img { main>section#categories ul>li[type="button"].opened+ul {
max-height: var(--catalog-categories-buttons-lists-height, 500px);
transition: max-height 0.15s ease-in 0.1s, margin 0s ease-out 0.1s, padding 0s ease-out 0.1s;
}
main>section#categories ul>li[type="button"]:not(.opened)+ul {
max-height: 0;
padding-top: 0;
padding-bottom: 0;
margin-top: 0;
transition: max-height 0.25s ease-out, margin 0s ease-out 0.25s, padding 0s ease-out 0.25s;
}
main>section#categories>a.category[type="button"]>img,
main>section#categories>ul>li.category[type="button"]>img {
position: absolute; position: absolute;
left: -5%; left: -5%;
top: -5%; top: -5%;
@ -58,26 +126,71 @@ main>section#categories>a.category[type="button"]>img {
height: 110%; height: 110%;
object-fit: cover; object-fit: cover;
/* filter: blur(1px); */ /* filter: blur(1px); */
filter: contrast(1.2) brightness(60%); filter: var(--catalog-categories-buttons-images-filter, contrast(1.2) brightness(60%));
} }
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img { main>section#categories>a.category[type="button"]>img.right,
main>section#categories>ul>li.category[type="button"]>img.right {
/* position: absolute; */
left: unset;
top: unset;
right: 0;
width: calc(100% - var(--catalog-categories-buttons-texts-width, 10rem));
}
main>section#categories>a.category[type="button"]:is(:hover, :focus)>img,
main>section#categories>ul>li.category[type="button"]:is(:hover, :focus)>img {
filter: unset; filter: unset;
} }
/* main>section#categories>a.category[type="button"]:has(>img)>p { */ /* main>section#categories>a.category[type="button"]:has(>img)>p { */
main>section#categories>a.category[type="button"]>p { main>section#categories>a.category[type="button"]>p,
main>section#categories>ul>li.category[type="button"]>p {
position: absolute; position: absolute;
left: var(--padding); left: var(--padding);
bottom: var(--padding); top: var(--catalog-categories-buttons-texts-top, var(--padding));
bottom: var(--catalog-categories-buttons-texts-bottom, var(--padding));
right: var(--padding); right: var(--padding);
margin: unset; margin: unset;
width: min-content; width: calc(var(--catalog-categories-buttons-texts-width, 10rem) - 0.7rem * 2);
border-radius: 0.75rem; border-radius: 0.75rem;
color: var(--catalog-categories-buttons-texts-title-color, var(--tg-theme-text-color));
/* background: var(--tg-theme-hint-color); */ /* background: var(--tg-theme-hint-color); */
} }
main>section#categories>a.category[type="button"]>p:after { main>section#categories>a.category[type="button"]>div.separator.gradient:has(+img.right),
main>section#categories>ul>li.category[type="button"]>div.separator.gradient:has(+img.right) {
--background: var(--catalog-categories-buttons-background, var(--tg-theme-button-color));
z-index: 100;
content: '';
position: absolute;
right: 0;
width: calc(100% - var(--catalog-categories-buttons-texts-width, 10rem));
height: 100%;
background: var(--background);
background: linear-gradient(90deg, var(--background) 0%, transparent var(--catalog-categories-buttons-separator-width, 100%));
}
main>section#categories>a.category[type="button"]>p,
main>section#categories>ul>li.category[type="button"]>p {
z-index: 100;
padding: 0 calc(var(--padding) / 2);
}
main>section#categories>a.category[type="button"]>p:not(.background),
main>section#categories>ul>li.category[type="button"]>p:not(.background) {
font-family: "Cygre";
font-weight: 600;
}
main>section#categories>a.category[type="button"]>p.background,
main>section#categories>ul>li.category[type="button"]>p.background {
padding: 8px calc(var(--padding, 0.7rem) * 1.3);
width: min-content;
}
main>section#categories>a.category[type="button"]>p.background:after,
main>section#categories>ul>li.category[type="button"]>p.background:after {
z-index: -100; z-index: -100;
position: absolute; position: absolute;
left: 0; left: 0;
@ -92,11 +205,6 @@ main>section#categories>a.category[type="button"]>p:after {
-moz-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0px 0px 8px 4px rgba(0, 0, 0, 0.1);
} }
main>section#categories>a.category[type="button"]>p {
z-index: 100;
padding: 8px 16px;
}
main>section#filters { main>section#filters {
--filters-height: 2rem; --filters-height: 2rem;
width: var(--width); width: var(--width);

View File

@ -0,0 +1,51 @@
@charset "UTF-8";
main>section#categories ul>ul {
--padding: 1rem;
padding: 0;
}
main>section#categories>ul ul>li {
--padding-row: calc(var(--gap, 5px) * 1);
position: relative;
padding: var(--padding-row);
display: flex;
flex-flow: row;
align-items: center;
}
main>section#categories>ul ul>li>:is(small, i.icon):last-child {
margin-left: auto;
margin-right: 0.1rem;
rotate: 0deg;
transition: rotate 0.1s ease-out;
font-family: monospace;
font-weight: bold;
}
main>section#categories>ul ul>li.opened>:is(small, i.icon):last-child {
rotate: 90deg;
transition: rotate 0.1s ease-in;
}
main>section#categories>ul ul>li:after,
main>section#categories>ul ul>li[type="button"].opened+ul:has(+ li)>li:last-of-type:after {
position: absolute;
content: '';
bottom: 0px;
width: calc(100% - var(--padding-row) * 2);
display: block;
align-self: center;
border-bottom: 1px solid var(--catalog-categories-buttons-lists-separatpr-color, var(--tg-theme-hint-color));
transition: bottom 0s linear 0s;
}
main>section#categories>ul ul>li:last-of-type:not(.opened):after {
bottom: -1rem;
transition: bottom 0.1s ease-out 0.25s;
}
main>section#categories>ul ul>li:last-of-type.opened:after {
bottom: -1px;
transition: bottom 0.1s ease-in;
}

View File

@ -0,0 +1,21 @@
@charset "UTF-8";
main>section#categories ul>li:not(.opened):has(+ ul) {
margin-bottom: 0;
transition: margin 0.1s ease-out 0.25s;
}
main>section#categories>ul>li.opened:has(+ ul) {
margin-bottom: var(--gap, 5px);
transition: margin 0.1s ease-in;
}
main>section#categories ul>ul {
--offset: 0rem;
border-radius: 0.75rem;
}
main>section#categories>ul ul>ul {
margin-left: var(--gap, 5px);
width: calc(100% - var(--gap, 5px));
}

View File

@ -0,0 +1,8 @@
@charset "UTF-8";
@font-face {
font-family: 'Chalet';
src: url("/themes/default/fonts/chalet/chalet.otf") format('opentype');
font-weight: 600;
font-style: bold;
}

View File

@ -0,0 +1,8 @@
@charset "UTF-8";
@font-face {
font-family: 'Cygre';
src: url("/themes/default/fonts/cygre/Cygre-Bold.ttf");
font-weight: 600;
font-style: bold;
}

View File

@ -26,7 +26,7 @@ i.icon.arrow:not(.circle, .square)::after {
bottom: 7px; bottom: 7px;
} }
i.icon.arrow:not(.circle, .square)::before { i.icon.arrow:not(.circle, .square, .short)::before {
width: 16px; width: 16px;
height: 2px; height: 2px;
bottom: 10px; bottom: 10px;

View File

@ -1,16 +1,70 @@
{% if categories is not empty %} {% if categories is not empty %}
<section id="categories" class="unselectable"> {% if settings.catalog.categories.structure == 'pages' %}
<section id="categories" class="unselectable">
{% for category in categories %} {% for category in categories %}
<a id="category_{{ category.identifier }}" data-category-identifier="{{ category.identifier }}" class="category" <a id="category_{{ category.identifier }}" data-category-identifier="{{ category.identifier }}"
class="category{% if settings.catalog.categories.display == 'column' %} column{% endif %}"
type="button" href="?category={{ category.identifier }}" type="button" href="?category={{ category.identifier }}"
onclick="core.catalog.parameters.set('category', {{ category.identifier }}); return !core.catalog.search(event);" onclick="core.catalog.parameters.set('category', {{ category.identifier }}); return !core.catalog.search(event);"
onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))" onkeydown="event.keyCode === 13 && (core.catalog.parameters.set('category', {{ category.identifier }}), core.catalog.search(event))"
tabindex="3"> tabindex="3"
>
{% if category.images %} {% if category.images %}
<img src="{{ category.images.0.storage.400 }}" alt="{{ category.name }}" ondragstart="return false;"> {% if settings.catalog.categories.display == 'column' and settings.catalog.categories.buttons.separator.enabled %}
<div class="separator gradient"></div>
{% endif %} {% endif %}
<p>{{ category.name }}</p> <img src="{{ category.images.0.storage.400 }}"
class="{% if settings.catalog.categories.display == 'column' %}right{% endif %}" alt="{{ category.name }}" ondragstart="return false;">
{% endif %}
<p class="{% if settings.catalog.categories.buttons.texts.background %}background{% endif %}">{{ category.name }}{% if category.description %} <small>{{ category.description }}</small>{% endif %}</p>
</a> </a>
{% endfor %} {% endfor %}
</section> </section>
{% elseif settings.catalog.categories.structure == 'lists' %}
{% macro generate(structure, categories) %}
{% for category, descendants in structure %}
{% if descendants is empty %}
<li id="category_{{ categories[category].identifier }}" type="button"
onclick="core.catalog.parameters.set('category', {{ categories[category].identifier }}); return !core.catalog.search(event);">
{{ categories[category].name }}</li>
{% else %}
<li id="category_{{ categories[category].identifier }}" type="button" class="openable" onclick="this.classList.toggle('opened'); for (const sibling of [...this.parentElement.children].filter(children => children !== this)) { sibling.classList.remove('opened'); }">
{{ categories[category].name }}
{% if settings.catalog.categories.buttons.lists.arrow %}
<i class="icon arrow short"></i>
{% endif %}
</li>
<ul>
<li id="category_{{ categories[category].identifier }}" type="button"
onclick="core.catalog.parameters.set('category', {{ categories[category].identifier }}); return !core.catalog.search(event);">
Все товары
</li>
{{ _self.generate(descendants, categories) }}
</ul>
{% endif %}
{% endfor %}
{% endmacro %}
<section id="categories" class="unselectable">
{% if structure is not empty %}
<ul>
{% for category, descendants in structure %}
<li id="category_{{ categories[category].identifier }}" class="category openable" type="button" tabindex="3" onclick="this.classList.toggle('opened'); for (const sibling of [...this.parentElement.children].filter(children => children !== this)) { sibling.classList.remove('opened'); }">
{% if categories[category].images %}
{% if settings.catalog.categories.display == 'column' and settings.catalog.categories.buttons.separator.enabled %}
<div class="separator gradient"></div>
{% endif %}
<img src="{{ categories[category].images.0.storage.400 }}"
class="{% if settings.catalog.categories.display == 'column' %}right{% endif %}" alt="{{ categories[category].name }}" ondragstart="return false;">
{% endif %}
<p class="{% if settings.catalog.categories.buttons.texts.background %}background{% endif %}">{{ categories[category].name }}{% if category.description %} <small>{{ categories[category].description }}</small>{% endif %}</p>
</li>
<ul>
{{ _self.generate(descendants, categories) }}
</ul>
{% endfor %}
</ul>
{% endif %}
</section>
{% endif %}
{% endif %} {% endif %}

View File

@ -0,0 +1,23 @@
{% macro generate(categories, registry) %}
{% for category, descendants in categories %}
{% if descendants is empty %}
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
{% else %}
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
<ul>
{{ _self.generate(descendants, registry) }}
</ul>
{% endif %}
{% endfor %}
{% endmacro %}
{% if registry is not empty and structure is not empty %}
<ul>
{% for category, descendants in structure %}
<li id="{{ registry[category].getId() }}">{{ registry[category].name }}</li>
<ul>
{{ _self.generate(descendants, registry) }}
</ul>
{% endfor %}
</ul>
{% endif %}

View File

@ -3,6 +3,19 @@
{% block css %} {% block css %}
{{ parent() }} {{ parent() }}
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog.css" />
{% if settings.catalog.categories.display == 'column' and settings.catalog.categories.structure == 'lists' %}
{% if settings.catalog.categories.buttons.lists.separated %}
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/lists/separated.css" />
{% endif %}
{% if settings.catalog.categories.buttons.lists.blocks %}
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/catalog/lists/blocks.css" />
{% endif %}
{% if settings.catalog.categories.buttons.lists.arrow %}
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/arrow.css" />
{% endif %}
{% endif %}
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/interface/select.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/interface/select.css" />
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/search.css" />
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" />

View File

@ -18,11 +18,14 @@
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/window.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/window.css" />
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/dejavu.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/dejavu.css" />
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/kabrio.css" rel="preload" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/kabrio.css" rel="preload" />
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/cygre.css" rel="preload" />
<!-- <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/fonts/chalet.css" rel="preload" /> -->
<style> <style>
:root { :root {
--currency: "{{ currency.symbol ?? '$' }}"; --currency: "{{ currency.symbol ?? '$' }}";
--days: "{{ language.name == 'ru' ? 'дней' : 'days' }}"; --days: "{{ language.name == 'ru' ? 'дней' : 'days' }}";
--days-short: "{{ language.name == 'ru' ? 'дн' : 'd' }}"; --days-short: "{{ language.name == 'ru' ? 'дн' : 'd' }}";
{% if settings.search.enabled and settings.search.position == 'fixed' %} {% if settings.search.enabled and settings.search.position == 'fixed' %}
{% if settings.menu.enabled %} {% if settings.menu.enabled %}
{% if settings.menu.position == 'fixed' %} {% if settings.menu.position == 'fixed' %}
@ -34,16 +37,76 @@
--header-margin-top: 3rem; --header-margin-top: 3rem;
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if settings.menu.enabled and settings.menu.position == 'fixed' %} {% if settings.menu.enabled and settings.menu.position == 'fixed' %}
--menu-height: {{ settings.menu.height ?? '3rem' }}; --menu-height: {{ settings.menu.height ?? '3rem' }};
--footer-margin-bottom: var(--menu-height); --footer-margin-bottom: var(--menu-height);
{% endif %} {% endif %}
{% for parameter, value in settings.css %} {% for parameter, value in settings.css %}
--{{ parameter }}: {{ value }}; --{{ parameter }}: {{ value }};
{% endfor %} {% endfor %}
{% if cart.summary.amount > 0 %} {% if cart.summary.amount > 0 %}
--cart-amount: "{{ cart.summary.amount }}"; --cart-amount: "{{ cart.summary.amount }}";
{% endif %} {% endif %}
{% if settings.catalog.categories.buttons.height %}
--catalog-categories-buttons-height: {{ settings.catalog.categories.buttons.height }};
{% endif %}
{% if settings.catalog.categories.buttons.background %}
--catalog-categories-buttons-background: {{ settings.catalog.categories.buttons.background }};
{% endif %}
{% if settings.catalog.categories.buttons.texts.title.color %}
--catalog-categories-buttons-texts-title-color: {{ settings.catalog.categories.buttons.texts.title.color }};
{% endif %}
{% if settings.catalog.categories.buttons.texts.description.color %}
--catalog-categories-buttons-texts-description-color: {{ settings.catalog.categories.buttons.texts.description.color }};
{% endif %}
{% if settings.catalog.categories.buttons.images.filter %}
--catalog-categories-buttons-images-filter: {{ settings.catalog.categories.buttons.images.filter }};
{% endif %}
{% if settings.catalog.categories.display == 'column' %}
--catalog-categories-wrap-flex-wrap: unset;
--catalog-categories-wrap-flex-direction: 'column';
--catalog-categories-buttons-width: 100%;
{% if settings.catalog.categories.buttons.separator.width %}
--catalog-categories-buttons-separator-width: {{ settings.catalog.categories.buttons.separator.width }};
{% endif %}
{% if settings.catalog.categories.buttons.lists.background %}
--catalog-categories-buttons-lists-background-color: {{ settings.catalog.categories.buttons.lists.background }};
{% endif %}
{% if settings.catalog.categories.buttons.lists.separator %}
--catalog-categories-buttons-lists-separator-color: {{ settings.catalog.categories.buttons.lists.separator }};
{% endif %}
{% if settings.catalog.categories.buttons.lists.height %}
--catalog-categories-buttons-lists-height: {{ settings.catalog.categories.buttons.lists.height }};
{% endif %}
{% if settings.catalog.categories.buttons.texts.position.vertical == 'top' %}
--catalog-categories-buttons-texts-bottom: auto;
{% elseif settings.catalog.categories.buttons.texts.position.vertical == 'center' %}
--catalog-categories-buttons-texts-top: auto;
--catalog-categories-buttons-texts-bottom: auto;
{% elseif settings.catalog.categories.buttons.texts.position.vertical == 'bottom' %}
--catalog-categories-buttons-texts-top: auto;
{% endif %}
{% if settings.catalog.categories.buttons.texts.width %}
--catalog-categories-buttons-texts-width: {{ settings.catalog.categories.buttons.texts.width }};
{% endif %}
{% else %}
--catalog-categories-buttons-texts-top: auto;
{% endif %}
} }
</style> </style>
<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/animations.css" /> <link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/animations.css" />