diff --git a/README.md b/README.md
index eef5143..22a902e 100755
--- a/README.md
+++ b/README.md
@@ -226,31 +226,112 @@ You can copy a clean settings document without comments from here: `/examples/ar
```json
{
- "status": "active",
- "project": {
- "name": "PROJECT"
- },
- "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`
- "company": {
- "identifier": null, // Example: "000000000000000" (string|null) (if `null` it will not be displayed)
- "tax": null, // Example: "000000000000" (string|null) (if `null` it will not be displayed)
- "name": null, // Example: "COMPANY" (string|null) (if `null` it will not be displayed)
- "offer": false, // Display the data of a public offer in the footer? (bool) (does not affect the `/offer` page generation)
- "sim": null, // Examples: "+7 000 000-00-00", "70000000000" (string|null) (if `null` it will not be displayed)
- "mail": null // Example: "name@domain.zone" (string|null) (if `null` it will not be displayed)
- },
- "search": {
- "enabled": true, // Enable the search input field?
- "position": "fixed" // Values: "fixed", "relative"
- },
- "cart": {
- "enabled": true // Enable the cart button?
- },
- "css": {
- "catalog-button-cart-background": "#40a7e3",
- "product-button-cart-background": "#40a7e3"
+ "status": "active", // Values: "active", "inactive" (string) Status of the settings document?
+ "project": {
+ "name": "PROJECT" // Name of the projext (string)
+ },
+ "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`
+ "company": {
+ "identifier": null, // Example: "000000000000000" (string|null) (if `null` it will not be displayed)
+ "tax": null, // Example: "000000000000" (string|null) (if `null` it will not be displayed)
+ "name": null, // Example: "COMPANY" (string|null) (if `null` it will not be displayed)
+ "offer": false, // Display the data of a public offer in the footer? (bool) (does not affect the `/offer` page generation)
+ "sim": null, // Examples: "+7 000 000-00-00", "70000000000" (string|null) (if `null` it will not be displayed)
+ "mail": null // Example: "name@domain.zone" (string|null) (if `null` it will not be displayed)
+ },
+ "search": {
+ "enabled": true, // Enable the search input field?
+ "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": {
+ "enabled": true // Enable the cart button?
+ },
+ "css": {
+ "catalog-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/
+
diff --git a/mirzaev/huesos/system/controllers/catalog.php b/mirzaev/huesos/system/controllers/catalog.php
index 3091975..0815ad3 100755
--- a/mirzaev/huesos/system/controllers/catalog.php
+++ b/mirzaev/huesos/system/controllers/catalog.php
@@ -7,6 +7,7 @@ namespace mirzaev\huesos\controllers;
// Files of the project
use mirzaev\huesos\controllers\core,
mirzaev\huesos\models\enumerations\language,
+ mirzaev\huesos\models\catalog as model,
mirzaev\huesos\models\entry,
mirzaev\huesos\models\category,
mirzaev\huesos\models\product,
@@ -195,31 +196,26 @@ final class catalog extends core
// Write to the response buffer
$response['category'] = ['name' => $category->name ?? null];
- // Search for categories that are descendants of $category
- $entries = entry::search(
+ // Searching for entries that are descendants of $category
+ $search = model::search(
document: $category,
- amount: 50,
+ amount: 200,
+ depth: 100,
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})', */
parameters: ['language' => $this->language->name],
errors: $this->errors['catalog']
);
- // Initialize buffers of entries (in singular, by parameter from ArangoDB)
- $category = $product = [];
+ if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
+ // Pages
- foreach ($entries as $entry) {
- // Iterate over entries (descendands)
-
- // 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 = $search['categories'];
}
// 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;
+ $this->view->products = $search['products'];
if (isset($this->view->products) && count($this->view->products) > 0) {
// Amount of rendered products is more than 0
@@ -234,13 +230,51 @@ final class catalog extends core
} else if (!isset($category)) {
// Not received identifier of the category
- // search for root ascendants categories
- $this->view->categories = entry::ascendants(
- descendant: new category,
- return: 'DISTINCT MERGE(d, { name: d.name.@language})',
- parameters: ['language' => $this->language->name],
- errors: $this->errors['catalog']
- ) ?? null;
+ if (isset($this->settings->catalog['categories']['structure']) && $this->settings->catalog['categories']['structure'] === 'pages') {
+ // Pages
+
+ // Searching for root ascendants categories
+ $this->view->categories = entry::ascendants(
+ descendant: new category,
+ return: 'DISTINCT MERGE(d, { name: d.name.@language})',
+ parameters: ['language' => $this->language->name],
+ errors: $this->errors['catalog']
+ ) ?? 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
@@ -393,4 +427,56 @@ final class catalog extends core
// Exit (success/fail)
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
+ );
+ }
+ }
+ }
+ }
}
diff --git a/mirzaev/huesos/system/models/catalog.php b/mirzaev/huesos/system/models/catalog.php
index 77edf52..163fdda 100755
--- a/mirzaev/huesos/system/models/catalog.php
+++ b/mirzaev/huesos/system/models/catalog.php
@@ -45,6 +45,62 @@ final class catalog extends core
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
*
diff --git a/mirzaev/huesos/system/models/entry.php b/mirzaev/huesos/system/models/entry.php
index 3b04561..9a97ff8 100755
--- a/mirzaev/huesos/system/models/entry.php
+++ b/mirzaev/huesos/system/models/entry.php
@@ -223,8 +223,9 @@ final class entry extends core implements document_interface, collection_interfa
* @param category|product $document Ascendant document
* @param string|null $filter Expression for filtering (AQL)
* @param string|null $sort Expression for sorting (AQL)
- * @param int $page Страница
- * @param int $amount Количество товаров на странице
+ * @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]
@@ -236,6 +237,7 @@ final class entry extends core implements document_interface, collection_interfa
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,
@@ -252,12 +254,13 @@ final class entry extends core implements document_interface, collection_interfa
return is_array($result = collection::execute(
sprintf(
<<<'AQL'
- FOR v IN 1..1 INBOUND @document GRAPH @graph
+ FOR v IN 1..%u INBOUND @document GRAPH @graph
%s
%s
LIMIT @offset, @amount
RETURN DISTINCT IS_SAME_COLLECTION(@category, v._id) ? MERGE(v, {_type: @category%s}) : MERGE(v, {_type: @product%s})
AQL,
+ $depth,
empty($filter) ? '' : "FILTER $filter",
empty($sort) ? '' : "SORT $sort",
empty($categories_merge) ? '' : ", $categories_merge",
diff --git a/mirzaev/huesos/system/public/themes/default/css/account.css b/mirzaev/huesos/system/public/themes/default/css/account.css
index 26f0b44..5f9310c 100755
--- a/mirzaev/huesos/system/public/themes/default/css/account.css
+++ b/mirzaev/huesos/system/public/themes/default/css/account.css
@@ -15,5 +15,5 @@ nav#menu>a#account>img {
height: inherit;
object-fit: contain;
border-radius: 100%;
- border: 2px solid var(--tg-theme-bottom-bar-bg-color);
+ /* border: 2px solid var(--tg-theme-bottom-bar-bg-color); */
}
diff --git a/mirzaev/huesos/system/public/themes/default/css/catalog.css b/mirzaev/huesos/system/public/themes/default/css/catalog.css
index ffc42b9..d52a9d7 100755
--- a/mirzaev/huesos/system/public/themes/default/css/catalog.css
+++ b/mirzaev/huesos/system/public/themes/default/css/catalog.css
@@ -1,14 +1,66 @@
@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);
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);
}
-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;
+ z-index: unset;
position: relative;
height: 23px;
padding: var(--padding);
@@ -19,10 +71,11 @@ main>section#categories>a.category[type="button"] {
overflow: hidden;
border-radius: 0.75rem;
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;
position: absoulute;
left: 0;
@@ -44,13 +97,28 @@ main>section#categories:last-child {
padding: unset;
} */
-main>section#categories>a.category[type="button"] {
- width: calc(50% - var(--padding) * 2);
- height: 180px;
+main>section#categories>a.category[type="button"],
+main>section#categories>ul>li.category[type="button"] {
+ width: var(--catalog-categories-buttons-width, calc(50% - var(--padding) * 2));
+ height: var(--catalog-categories-buttons-height, 180px);
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;
left: -5%;
top: -5%;
@@ -58,26 +126,71 @@ main>section#categories>a.category[type="button"]>img {
height: 110%;
object-fit: cover;
/* 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;
}
/* 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;
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);
margin: unset;
- width: min-content;
+ width: calc(var(--catalog-categories-buttons-texts-width, 10rem) - 0.7rem * 2);
border-radius: 0.75rem;
+ color: var(--catalog-categories-buttons-texts-title-color, var(--tg-theme-text-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;
position: absolute;
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);
}
-main>section#categories>a.category[type="button"]>p {
- z-index: 100;
- padding: 8px 16px;
-}
-
main>section#filters {
--filters-height: 2rem;
width: var(--width);
diff --git a/mirzaev/huesos/system/public/themes/default/css/catalog/lists/blocks.css b/mirzaev/huesos/system/public/themes/default/css/catalog/lists/blocks.css
new file mode 100755
index 0000000..f444d76
--- /dev/null
+++ b/mirzaev/huesos/system/public/themes/default/css/catalog/lists/blocks.css
@@ -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;
+}
diff --git a/mirzaev/huesos/system/public/themes/default/css/catalog/lists/separated.css b/mirzaev/huesos/system/public/themes/default/css/catalog/lists/separated.css
new file mode 100755
index 0000000..58d7ec7
--- /dev/null
+++ b/mirzaev/huesos/system/public/themes/default/css/catalog/lists/separated.css
@@ -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));
+}
diff --git a/mirzaev/huesos/system/public/themes/default/css/fonts/chalet.css b/mirzaev/huesos/system/public/themes/default/css/fonts/chalet.css
new file mode 100755
index 0000000..72f6e6f
--- /dev/null
+++ b/mirzaev/huesos/system/public/themes/default/css/fonts/chalet.css
@@ -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;
+}
diff --git a/mirzaev/huesos/system/public/themes/default/css/fonts/cygre.css b/mirzaev/huesos/system/public/themes/default/css/fonts/cygre.css
new file mode 100755
index 0000000..4d2850e
--- /dev/null
+++ b/mirzaev/huesos/system/public/themes/default/css/fonts/cygre.css
@@ -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;
+}
diff --git a/mirzaev/huesos/system/public/themes/default/css/icons/arrow.css b/mirzaev/huesos/system/public/themes/default/css/icons/arrow.css
index acecc5c..e025d2d 100755
--- a/mirzaev/huesos/system/public/themes/default/css/icons/arrow.css
+++ b/mirzaev/huesos/system/public/themes/default/css/icons/arrow.css
@@ -26,7 +26,7 @@ i.icon.arrow:not(.circle, .square)::after {
bottom: 7px;
}
-i.icon.arrow:not(.circle, .square)::before {
+i.icon.arrow:not(.circle, .square, .short)::before {
width: 16px;
height: 2px;
bottom: 10px;
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/chalet/chalet.otf b/mirzaev/huesos/system/public/themes/default/fonts/chalet/chalet.otf
new file mode 100644
index 0000000..a0ab7fa
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/chalet/chalet.otf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Black.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Black.ttf
new file mode 100644
index 0000000..5373650
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Black.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Bold.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Bold.ttf
new file mode 100644
index 0000000..bde9fa4
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Bold.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Book.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Book.ttf
new file mode 100644
index 0000000..9161766
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Book.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-ExtraBold.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-ExtraBold.ttf
new file mode 100644
index 0000000..68094e5
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-ExtraBold.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Light.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Light.ttf
new file mode 100644
index 0000000..6f6b293
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Light.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Medium.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Medium.ttf
new file mode 100644
index 0000000..b93ff61
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Medium.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Regular.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Regular.ttf
new file mode 100644
index 0000000..4539c5c
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Regular.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-SemiBold.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-SemiBold.ttf
new file mode 100644
index 0000000..403f1d1
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-SemiBold.ttf differ
diff --git a/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Thin.ttf b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Thin.ttf
new file mode 100644
index 0000000..584c4cd
Binary files /dev/null and b/mirzaev/huesos/system/public/themes/default/fonts/cygre/Cygre-Thin.ttf differ
diff --git a/mirzaev/huesos/system/views/themes/default/catalog/elements/categories.html b/mirzaev/huesos/system/views/themes/default/catalog/elements/categories.html
index ed335a5..a077c91 100755
--- a/mirzaev/huesos/system/views/themes/default/catalog/elements/categories.html
+++ b/mirzaev/huesos/system/views/themes/default/catalog/elements/categories.html
@@ -1,16 +1,70 @@
{% if categories is not empty %}
- {{ category.name }}
- {% endif %}
-
+ {% endif %}
+
+
+ {% endfor %}
+