diff --git a/composer.json b/composer.json index 688c6d7..0a15589 100755 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "twig/extra-bundle": "^3.7", "twig/intl-extra": "^3.10", "svoboda/time": "^1.0", + "imagine/imagine": "^1.5", "badfarm/zanzara": "^0.9.1", "nyholm/psr7": "^1.8", "react/filesystem": "^0.1.2" diff --git a/svoboda/pechatalka/system/controllers/constructor.php b/svoboda/pechatalka/system/controllers/constructor.php deleted file mode 100755 index a0af9e9..0000000 --- a/svoboda/pechatalka/system/controllers/constructor.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ -final class constructor extends core -{ - /** - * Errors - * - * @var array $errors Registry of errors - */ - protected array $errors = [ - 'system' => [] - ]; - - /** - * Pin - * - * @return null - */ - public function pin(): null - { - if (str_contains($this->request->headers['accept'], content::any->value)) { - // Request for any response - - // Render page - $page = $this->view->render('/constructor/pin/page.html'); - - // Sending response - $this->response - ->start() - ->clean() - ->sse() - ->write($page) - ->validate($this->request) - ?->body() - ->end(); - - // Deinitializing rendered page - unset($page); - - // Exit (success) - return null; - } - - // Exit (fail) - return null; - } -} diff --git a/svoboda/pechatalka/system/controllers/generator.php b/svoboda/pechatalka/system/controllers/generator.php new file mode 100755 index 0000000..567502f --- /dev/null +++ b/svoboda/pechatalka/system/controllers/generator.php @@ -0,0 +1,140 @@ + + */ +final class generator extends core +{ + /** + * Errors + * + * @var array $errors Registry of errors + */ + protected array $errors = [ + 'system' => [] + ]; + + /** + * Pin + * + * @return null + */ + public function pin(): null + { + if (str_contains($this->request->headers['accept'] ?? '', content::any->value)) { + // Request for any response + + // Render page + $page = $this->view->render('/generator/pin/page.html'); + + // Sending response + $this->response + ->start() + ->clean() + ->sse() + ->write($page) + ->validate($this->request) + ?->body() + ->end(); + + // Deinitializing rendered page + unset($page); + + // Exit (success) + return null; + } + + // Exit (fail) + return null; + } + + /** + * Print + * + * Assemble and generate the blank from layers and prepare to printing + * + * @return null + */ + public function print( + string $type, + string|int $amount = 1, + ?string $display + ): null { + // Type + $type = type::{$type} ?? null; + + // Amount + preg_match('/[\d]+/', (string) $amount, $matches); + $amount = (int) $matches[0] ?? 1; + unset($matches); + + // Display + $display = json_decode($display, true, 10); + + if ($type instanceof type && $amount > 0) { + // Initialized all required arguments + + // Universalizing received images + $files_images = array_filter($this->request->files, fn(string $key) => preg_match('/^layer_\d{1,2}_image/', $key), ARRAY_FILTER_USE_KEY); + + // Initializing the combined images buffer + $images_combined = []; + + for ($i = 0; $i < count($files_images); ++$i) { + // Iterating over images + + // Combining images with sorting + if (empty($display[$i]['image'])) $images_combined[] = $display[$i]['image']; + if (!empty($files_images["image_$i"])) $images_combined[] = $files_images["image_$i"]; + if (empty($display[$i]['image'])) $display[$i]['image'] = $files_images[$i]; + } + + if (str_contains($this->request->headers['accept'], content::any->value)) { + // Request for any response + + // Render page + $page = $this->view->render('/generator/pin/page.html'); + + // Sending response + $this->response + ->start() + ->clean() + ->sse() + ->write($page) + ->validate($this->request) + ?->body() + ->end(); + + // Deinitializing rendered page + unset($page); + + // Exit (success) + return null; + } + } + + // Exit (fail) + return null; + } +} diff --git a/svoboda/pechatalka/system/localizations/english.php b/svoboda/pechatalka/system/localizations/english.php index a5f9c81..57a719c 100644 --- a/svoboda/pechatalka/system/localizations/english.php +++ b/svoboda/pechatalka/system/localizations/english.php @@ -14,7 +14,7 @@ return [ // Main menu 'menu_title' => 'Main menu', 'menu_accounts' => 'Accounts', - 'menu_button_constructor_pin' => 'Pin', + 'menu_button_generator_pin' => 'Pin', 'menu_not_syncronized' => 'The database does not synchronize with the blockchain network', // Account diff --git a/svoboda/pechatalka/system/localizations/russian.php b/svoboda/pechatalka/system/localizations/russian.php index 23e67c3..0bf3ba1 100644 --- a/svoboda/pechatalka/system/localizations/russian.php +++ b/svoboda/pechatalka/system/localizations/russian.php @@ -14,7 +14,7 @@ return [ // Main menu 'menu_title' => 'Главное меню', 'menu_accounts' => 'Аккаунты', - 'menu_button_constructor_pin' => 'Значок', + 'menu_button_generator_pin' => 'Значок', 'menu_not_syncronized' => 'База данных не синхронизируется с блокчейн сетью', // Аккаунт diff --git a/svoboda/pechatalka/system/models/account.php b/svoboda/pechatalka/system/models/account.php index cd467e7..cc1ace5 100644 --- a/svoboda/pechatalka/system/models/account.php +++ b/svoboda/pechatalka/system/models/account.php @@ -79,7 +79,7 @@ final class account extends core /** * Initialize * - * Searches for the account record in the database, and if it does not find it, it creates it + * Searches for the account record in the database, and if it does not find it, then creates it * * @param telegram $telegram The telegram account * diff --git a/svoboda/pechatalka/system/models/enumerations/currency.php b/svoboda/pechatalka/system/models/enumerations/currency.php new file mode 100644 index 0000000..f5cdae3 --- /dev/null +++ b/svoboda/pechatalka/system/models/enumerations/currency.php @@ -0,0 +1,70 @@ + + */ +enum currency +{ + case usd; + case rub; + + /** + * Label + * + * Initialize label of the currency + * + * @param language|null $language Language into which to translate + * + * @return string Translated label of the currency + * + * @todo + * 1. More currencies + * 2. Cases??? + */ + public function label(?language $language = language::en): string + { + // Exit (success) + return match ($this) { + currency::usd => match ($language) { + language::en => 'Dollar', + language::ru => 'Доллар' + }, + currency::rub => match ($language) { + language::en => 'Ruble', + language::ru => 'Рубль' + } + }; + } + + /** + * Symbol + * + * Initialize symbol of the currency + * + * @return string Symbol of the currency + * + * @todo + * 1. More currencies + */ + public function symbol(): string + { + // Exit (success) + return match ($this) { + currency::usd => '$', + currency::rub => '₽' + }; + } +} + diff --git a/svoboda/pechatalka/system/models/enumerations/products/type.php b/svoboda/pechatalka/system/models/enumerations/products/type.php new file mode 100644 index 0000000..7f5d7f5 --- /dev/null +++ b/svoboda/pechatalka/system/models/enumerations/products/type.php @@ -0,0 +1,19 @@ + + */ +enum type +{ + case pin_37; +} + diff --git a/svoboda/pechatalka/system/models/telegram/commands.php b/svoboda/pechatalka/system/models/telegram/commands.php index cc3cd2e..dab05bd 100644 --- a/svoboda/pechatalka/system/models/telegram/commands.php +++ b/svoboda/pechatalka/system/models/telegram/commands.php @@ -76,9 +76,9 @@ final class commands extends core 'inline_keyboard' => [ [ [ - 'text' => '🔘 ' . $localization['menu_button_constructor_pin'], + 'text' => '🔘 ' . $localization['menu_button_generator_pin'], 'web_app' => [ - 'url' => 'https://pechatalka.svoboda.works/constructor/pin' + 'url' => 'https://pechatalka.svoboda.works/generator/pin' ] ] ] diff --git a/svoboda/pechatalka/system/public/css/fonts/cascadia_code.css b/svoboda/pechatalka/system/public/css/fonts/cascadia_code.css index fa4da19..c42ed21 100755 --- a/svoboda/pechatalka/system/public/css/fonts/cascadia_code.css +++ b/svoboda/pechatalka/system/public/css/fonts/cascadia_code.css @@ -2,7 +2,7 @@ @font-face { font-family: 'Cascadia Code'; - src: url("/fonts/geologica/CascadiaCode-Regular.woff2"); - font-weight: 400; + src: url("/fonts/cascadia_code/CascadiaCode.woff2"); + font-weight: 600; font-style: normal; } diff --git a/svoboda/pechatalka/system/public/index.php b/svoboda/pechatalka/system/public/index.php index 767417d..66c590c 100755 --- a/svoboda/pechatalka/system/public/index.php +++ b/svoboda/pechatalka/system/public/index.php @@ -50,7 +50,8 @@ $core = new core(namespace: __NAMESPACE__); // Initializing routes $core->router ->write('/', new route('index', 'index'), 'GET') - ->write('/constructor/pin', new route('constructor', 'pin'), 'GET') + ->write('/generator/print', new route('generator', 'print'), 'POST') + ->write('/generator/pin', new route('generator', 'pin'), 'GET') ; // Handling request diff --git a/svoboda/pechatalka/system/public/js/core.js b/svoboda/pechatalka/system/public/js/core.js index dca8cc7..269b08f 100644 --- a/svoboda/pechatalka/system/public/js/core.js +++ b/svoboda/pechatalka/system/public/js/core.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; /** * @name Core @@ -14,10 +14,13 @@ class core { static domain = window.location.hostname; // Language - static language = "ru"; + static language = "english"; + + // Currency + static currency = { name: "usd", symbol: "$" }; // Theme - static theme = window.getComputedStyle(document.getElementById('theme')); + static theme = window.getComputedStyle(document.getElementById("theme")); // Window static window; diff --git a/svoboda/pechatalka/system/public/js/modules/generator.mjs b/svoboda/pechatalka/system/public/js/modules/generator.mjs new file mode 100644 index 0000000..8461433 --- /dev/null +++ b/svoboda/pechatalka/system/public/js/modules/generator.mjs @@ -0,0 +1,231 @@ +"use strict"; + +/** + * @name Generator + * + * @description + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich + */ +export default class generator { + /** + * @name Print (interface) + * + * @description + * Request to generate image and receive image for printing + * + * @param {string} type Type (37mm) + * @param {number} [amount=1] Amount + * @param {HTMLElement} canvas The canvas with images + * @param {HTMLElement} button Button + * @param {boolean} [force=false] Ignore the damper? + * + * @return {boolean} Did the processing complete without errors? + */ + static print(type, amount = 1, canvas, button, force = false) { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Processing under damper + generator.print.damper(type, amount, canvas, button, force); + }, + () => { + // Not imported the damper module + + // Processing + generator.print.system(type, amount, canvas, button); + }, + ); + + // Exit (success) + return true; + } +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + generator.print, + { + /** + * @name Print (damper) + * + * @description + * Request to generate image and receive image for printing + * + * @memberof generator.print + * + * @param {string} type Type (37mm) + * @param {number} [amount=1] Amount + * @param {HTMLElement} canvas The canvas with images + * @param {HTMLElement} button Button + * @param {boolean} [force=false] Ignore the damper? + * + * @return {void} + */ + damper: core.damper( + (...variables) => generator.print.system(...variables), + 300, + 4, + ), + }, + ); +}); + +Object.assign( + generator.print, + { + /** + * @name Print (system) + * + * @description + * Request to generate image and receive image for printing + * + * @memberof generator.print + * + * @param {string} type Type (37mm) + * @param {number} [amount=1] Amount + * @param {HTMLElement} canvas The canvas with images + * @param {HTMLElement} button Button + * @param {function} resolve Resolve + * @param {function} reject Reject + * + * @return {Promise} Request to the server + */ + async system( + type, + amount = 1, + canvas, + button, + resolve = () => {}, + reject = () => {}, + ) { + try { + if ( + typeof type === "string" && + type.length > 0 && + typeof amount === "number" && + canvas instanceof HTMLElement + ) { + // Validated all required arguments + + // Initializing the body buffer + const body = new FormData(); + + // Writing parameters into the body buffer + body.append("type", type); + body.append("amount", amount); + + // Initializing the display JSON buffer + const display = {}; + + for (const layer of canvas.querySelectorAll("div.layer")) { + // Iterating over layers + + // Initializing the layer index + const index = +layer.getAttribute("id")?.split( + "pechatalka_layer_", + )[1]; + + if (typeof index === "number") { + // Initialized the layer index + + // Initializing the layer + display[index] ??= {}; + + // Writing the layer parameters + display[index].x = + parseInt(layer.style.getPropertyValue("left")) || 0; + display[index].y = + parseInt(layer.style.getPropertyValue("top")) || 0; + display[index].scale = + (parseInt(layer.style.getPropertyValue("scale")) || 1) * 100; + + // Initializing the layer type + const type = layer.getAttribute("data-layer-type"); + + if (type === "image") { + // Image + + // Initializing the layer image + const image = layer.getElementsByTagName("img")[0]; + + if (image instanceof HTMLImageElement) { + // Initialized the layer image + + // Initializing the image `src` attribute value + const src = image.getAttribute("src")?.split("?")[0]; + + if (src.length > 0) { + // Initialized the image `src` attribute value + + if (new URL(src).protocol === "blob:") { + // Blob + + await fetch(src).then((r) => r.blob()).then((value) => { + // Converted "blob:..." string to the Blob object + + // Writing into the body buffer + body.append("layer_" + index + "_image", value, index); + }); + } else { + // URL (expected) + + // Writing the layer parameter + display[index].image = src; + } + } + } + } else if (type === "film") { + // Film (laminating) + } + } + } + + // Writing into the body buffer + body.append("display", JSON.stringify(display)); + + return await core.request( + "/generator/print", + body, + "POST", + {}, + null, + ).then( + async (json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + + // Exit (fail) + reject(json); + } else { + // Success (not received errors) + + // Exit (success) + resolve(); + } + } + }, + () => reject(), + ); + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +// Connecting to the core +if (!core.generator) core.generator = generator; diff --git a/svoboda/pechatalka/system/public/js/pages/constructor/pin.js b/svoboda/pechatalka/system/public/js/pages/constructor/pin.js deleted file mode 100644 index 4627a7a..0000000 --- a/svoboda/pechatalka/system/public/js/pages/constructor/pin.js +++ /dev/null @@ -1,13 +0,0 @@ -core.modules.connect(["pechatalka"]) - .then(() => { - // Imported the pechatalka module - - // Initializing the instance - const instance = new core.pechatalka( - document.getElementById("pechatalka"), - document.getElementById("pechatalka")?.querySelector(".canvas"), - document.getElementById("pechatalka")?.querySelector(".result"), - true, - ); - }); - diff --git a/svoboda/pechatalka/system/public/js/pages/generator/pin.js b/svoboda/pechatalka/system/public/js/pages/generator/pin.js new file mode 100644 index 0000000..fcae9a2 --- /dev/null +++ b/svoboda/pechatalka/system/public/js/pages/generator/pin.js @@ -0,0 +1,110 @@ +core.modules.connect(["pechatalka"]) + .then(() => { + // Imported the pechatalka module + + // Initializing the instance + const instance = new core.pechatalka( + document.getElementById("pechatalka"), + document.getElementById("pechatalka")?.querySelector(".canvas"), + document.getElementById("pechatalka")?.querySelector(".result"), + new Map([ + ["interface", true], + ["round", true], + ]), + true, + ); + + // Reinitializing the core instance of pechatalka + core.pechatalka = instance; + + // instence.layers.set('interface', true) + + // Reinitializing the `layer create` event + instance.events + .get("layers") + .set("create", (layer) => { + for (const button of layer.buttons.values()) { + // Iterating over the layer buttons + + // Writing the class + button.classList.add("rounded"); + } + + // Initializing the interface changing function + const interface_change = (value) => { + if (value) { + // Interface are enabled + + for (const button of layer.buttons.values()) { + // Iterating over the layer buttons + + // Writing the class + button.classList.remove("disabled"); + } + } else { + // Interface are disabled + + for (const button of layer.buttons.values()) { + // Iterating over the layer buttons + + // Writing the class + button.classList.add("disabled"); + } + } + }; + + // Reinitializing `interface` events functions + layer.events.set( + "interface", + new Map([ + ["toggle", interface_change], + ["set", interface_change], + ]), + ); + + // Initial processing of the interface function + interface_change(instance.preset.get("interface") ?? false); + + // Initializing the round changing function + const round_change = (value) => { + if (value) { + // Round are enabled + + // Writing the class + layer.content.classList.add("rounded"); + } else { + // Round are disabled + + // Writing the class + layer.content.classList.remove("rounded"); + } + }; + + // Reinitializing `round` events functions + layer.events.set( + "round", + new Map([ + ["toggle", round_change], + ["set", round_change], + ]), + ); + + // Initial processing of the round function + round_change(instance.preset.get("round") ?? false); + + if (layer.type === "image") { + // Image + + // Writing the layer type + layer.wrap.setAttribute("data-layer-type", "image"); + } + }); + + // Reinitializing the `layer create` event + instance.events + .get("cost") + .set("changed", (to, from) => { + // Writing the total cost into the document + instance.result.querySelector(".cost").innerText = to; + }); + }); diff --git a/svoboda/pechatalka/system/public/robot.php b/svoboda/pechatalka/system/public/telegram.php similarity index 100% rename from svoboda/pechatalka/system/public/robot.php rename to svoboda/pechatalka/system/public/telegram.php diff --git a/svoboda/pechatalka/system/public/themes/default/css/icons/notification.css b/svoboda/pechatalka/system/public/themes/default/css/icons/notification.css new file mode 100644 index 0000000..76afa98 --- /dev/null +++ b/svoboda/pechatalka/system/public/themes/default/css/icons/notification.css @@ -0,0 +1,46 @@ +@charset "UTF-8"; + +i.icon.notification { + position: relative; + width: 14px; + height: 14px; + box-sizing: border-box; + display: block; + background: + linear-gradient(to left, currentColor 10px, transparent 0) no-repeat right bottom/2px 8px, + linear-gradient(to left, currentColor 10px, transparent 0) no-repeat left top/8px 2px; + + &:after, + &:before { + content: ""; + position: absolute; + box-sizing: border-box; + display: block; + } + + &:before { + width: 14px; + height: 14px; + border-bottom: 2px solid; + border-left: 2px solid; + } + + &:after { + top: -2px; + right: -2px; + width: 6px; + height: 6px; + border-radius: 4px; + background-color: currentColor; + } + + &:is(:disabled, [disabled="true"], .disabled) { + background: + linear-gradient(to left, currentColor 10px, transparent 0) no-repeat right bottom/2px 14px, + linear-gradient(to left, currentColor 10px, transparent 0) no-repeat left top/12px 2px; + + &:after { + display: none; + } + } +} diff --git a/svoboda/pechatalka/system/public/themes/default/css/icons/round.css b/svoboda/pechatalka/system/public/themes/default/css/icons/round.css new file mode 100644 index 0000000..d236b23 --- /dev/null +++ b/svoboda/pechatalka/system/public/themes/default/css/icons/round.css @@ -0,0 +1,17 @@ +@charset "UTF-8"; + +i.icon.round { + --border-radius-top-left: 0.7rem; + width: var(--square, 10px); + height: var(--square, 10px); + border: var(--border-width, 2px) solid var(--border-color, #fff); + border-radius: + var(--border-radius-top-left, 0rem) + var(--border-radius-top-right, 0rem) + var(--border-radius-bottom-right, 0rem) + var(--border-radius-bottom-left, 0rem); + + &:is(:disabled, [disabled="true"], .disabled) { + border-radius: 0; + } +} diff --git a/svoboda/pechatalka/system/public/themes/default/css/icons/share.css b/svoboda/pechatalka/system/public/themes/default/css/icons/share.css new file mode 100644 index 0000000..49c617f --- /dev/null +++ b/svoboda/pechatalka/system/public/themes/default/css/icons/share.css @@ -0,0 +1,37 @@ +@charset "UTF-8"; + +i.icon.share { + position: relative; + width: 6px; + height: 6px; + display: block; + box-sizing: border-box; + border-radius: 100px; + background-color: currentColor; + box-shadow: + 10px -6px 0, + 10px 6px 0; + + &:after, + &:before { + content: ""; + position: absolute; + left: 2px; + width: 10px; + height: 2px; + display: block; + box-sizing: border-box; + border-radius: 3px; + background-color: currentColor; + } + + &:before { + top: 0; + transform: rotate(-35deg); + } + + &:after { + bottom: 0; + transform: rotate(35deg); + } +} diff --git a/svoboda/pechatalka/system/public/themes/default/css/interface/select.css b/svoboda/pechatalka/system/public/themes/default/css/interface/select.css new file mode 100644 index 0000000..11c0113 --- /dev/null +++ b/svoboda/pechatalka/system/public/themes/default/css/interface/select.css @@ -0,0 +1,172 @@ +@charset "UTF-8"; + +div[data-type="select"] { + --filter-width: max(14rem, 30vw); + --filter-button-width: 2.5rem; + --filter-height-element: var(--filters-height, 2rem); + --filter-height-close: var(--filter-height-element); + --filter-height-open: max-content; + --filter-row-padding-x: 1rem; + --filter-row-gap: 10px; + position: relative; + width: var(--filter-width); + min-width: var(--width); + height: var(--height-close); + display: flex; + cursor: pointer; + border-radius: 0.75rem; + overflow-x: hidden; + background-color: var(--select-background-color, var(--button-background-color, var(--tg-theme-button-color))); + transition: 0s; +} + +div[data-type="select"] :is(button, :is(a, label)[type="button"]) { + background-color: var(--select-background-color, var(--button-background-color, var(--tg-theme-button-color))); +} + +div[data-type="select"]>section { + position: relative; + height: var(--filter-height-close); + display: flex; + flex-direction: column; + flex-grow: 1; + cursor: pointer; + overflow: hidden; + border-radius: 0 0 0.75rem 0.75rem; + background-color: var(--select-background-color, var(--button-background-color, var(--tg-theme-button-color))); + transition: 0s; +} + + +div[data-type="select"]:has(>section:is([data-select="open"], :focus)) { + >button:has(>i.icon.close) { + border-radius: 0 0.75rem 0 0; + } +} + +div[data-type="select"]>button:has(>i.icon.close) { + z-index: 100; + /* position: fixed; */ + position: absolute; + /* right: calc((var(--filter-button-width) + 0.4rem * 2) / 2); */ + right: 0; + /* align-self: start; */ + width: var(--filter-button-width); + height: var(--filter-height-close); + padding: 0 0.4rem; + border-radius: 0 0.75rem 0.75rem 0; +} + + +div[data-type="select"]:has(>section>input[id$="title"]:checked)>button:has(>i.icon.close), +div[data-type="select"]:focus>button:has(>i.icon.close) { + display: none; +} + +div[data-type="select"]:not(>section:focus, >section:has(>button>i.icon.close)):after { + z-index: 30; + content: ''; + top: calc(50% - 2.5px); + right: 1rem; + position: absolute; + width: 0; + height: 0; + pointer-events: none; + border-left: 5px solid transparent; + border-top: 5px solid var(--select-text-color, var(--button-text-color, var(--tg-theme-button-text-color, black))); + border-right: 5px solid transparent; +} + +div[data-type="select"]>section>input { + left: -99999px; + position: absolute; + opacity: 0; +} + +div[data-type="select"]>section>label { + z-index: 10; + order: 2; + top: 0; + position: absolute; + width: calc(100% + var(--filter-button-width)); + height: var(--filter-height-element); + padding: 0 var(--filter-row-padding-x, 1rem); + display: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + box-sizing: border-box; + pointer-events: none; + color: var(--select-text-color, var(--button-text-color, var(--tg-theme-button-text-color))); + transition: 0s; +} + +div[data-type="select"]>section>a { + height: var(--filter-height-element); + box-sizing: border-box; +} + +div[data-type="select"]>section:not(:focus)>a { + display: none; + pointer-events: none; +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input:not(:checked)+label[for$='title'] { + display: none; +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input:not(:checked)+label[for$='all']:not(:hover, :active, :focus) { + filter: brightness(90%); +} + +div[data-type="select"]>section>input:not(:checked)+label { + cursor: pointer; + filter: brightness(80%); +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input+label:hover { + filter: brightness(110%); +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label:hover { + filter: brightness(120%); +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input+label:is(:active, :focus) { + filter: brightness(60%); +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label:is(:active, :focus) { + filter: brightness(70%); +} + +div[data-type="select"]>section>input:checked+label { + z-index: 20; + order: 1; + max-width: calc(var(--filter-width) - var(--filter-row-padding-x, 1rem) * 2 - var(--filter-row-gap, 10px)); + display: inline; + line-height: var(--filter-height-element); + padding-right: 0; +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>input:checked+label { + max-width: initial; + padding-right: initial; +} + +div[data-type="select"]>section:is([data-select="open"], :focus) { + height: var(--filter-height-open, max-content); +} + +div[data-type="select"]>section:is([data-select="open"], :focus)>label { + position: relative; + display: inline; + line-height: var(--filter-height-element); + pointer-events: all; +} + +@media only screen and (max-width: 500px) { + div[data-type="select"]>section:only-child { + --width: 100% + } +} diff --git a/svoboda/pechatalka/system/public/themes/default/css/localizations/english.css b/svoboda/pechatalka/system/public/themes/default/css/localizations/english.css index 8945004..21acebb 100644 --- a/svoboda/pechatalka/system/public/themes/default/css/localizations/english.css +++ b/svoboda/pechatalka/system/public/themes/default/css/localizations/english.css @@ -8,7 +8,6 @@ --localization-button-pins: "Pins"; --localization-cost: "Cost"; - --localization-currency: "$"; --localization-buy: "Buy"; } } diff --git a/svoboda/pechatalka/system/public/themes/default/css/localizations/russian.css b/svoboda/pechatalka/system/public/themes/default/css/localizations/russian.css index a916e9a..a0bec79 100644 --- a/svoboda/pechatalka/system/public/themes/default/css/localizations/russian.css +++ b/svoboda/pechatalka/system/public/themes/default/css/localizations/russian.css @@ -8,7 +8,6 @@ --localization-button-pins: "Значки"; --localization-cost: "Стоимость"; - --localization-currency: "₽"; --localization-buy: "Купить"; } } diff --git a/svoboda/pechatalka/system/public/themes/default/css/main.css b/svoboda/pechatalka/system/public/themes/default/css/main.css index 6b7f002..383ab8c 100755 --- a/svoboda/pechatalka/system/public/themes/default/css/main.css +++ b/svoboda/pechatalka/system/public/themes/default/css/main.css @@ -8,7 +8,7 @@ body { flex-direction: column; align-items: center; gap: 1rem; - overflow-x: clip; + overflow: clip; background-color: var(--background-color, var(--tg-theme-bg-color)); } @@ -20,3 +20,46 @@ main { gap: var(--gap); transition: 0s; } + +div#language { + --filter-width: 2.3rem; + --filter-height-element: calc(var(--filter-width) - var(--filter-width) / 10); + --filter-height-close: var(--filter-height-element); + --filter-button-width: 0rem; + --filter-row-padding-x: 0rem; + --filter-row-gap: 0px; + position: absolute; + top: 20px; + left: 20px; + border: 2px solid var(--section-background-color-inverted); + filter: brightness(0.2); + + &:hover { + filter: unset; + } + + >section>label { + text-align: center; + } +} + +div#currency { + --filter-width: 2.3rem; + --filter-height-element: calc(var(--filter-width) - var(--filter-width) / 10); + --filter-height-close: var(--filter-height-element); + --filter-button-width: 0rem; + --filter-row-padding-x: 0rem; + --filter-row-gap: 0px; + position: absolute; + top: 20px; + right: 20px; + border: 2px solid var(--section-background-color-inverted); + filter: brightness(0.2); + + &:hover { + filter: unset; + } + + >section>label { + text-align: center; + } diff --git a/svoboda/pechatalka/system/public/themes/default/css/pages/constructor/pin.css b/svoboda/pechatalka/system/public/themes/default/css/pages/generator/pin.css similarity index 84% rename from svoboda/pechatalka/system/public/themes/default/css/pages/constructor/pin.css rename to svoboda/pechatalka/system/public/themes/default/css/pages/generator/pin.css index 83c84b6..d01066e 100644 --- a/svoboda/pechatalka/system/public/themes/default/css/pages/constructor/pin.css +++ b/svoboda/pechatalka/system/public/themes/default/css/pages/generator/pin.css @@ -47,7 +47,7 @@ section#pechatalka { overflow: clip; border: 2px solid var(--section-background-color-inverted); - >.button { + >:is(button, .button) { --padding-x: 0.8rem; height: 100%; box-sizing: border-box; @@ -62,14 +62,17 @@ section#pechatalka { } &:last-of-type:last-child:not(:only-of-type) { - margin-left: auto; padding-right: calc(var(--padding-x, 0.6rem) + var(--menu-padding-x, 0.4rem)) !important; } + &:is([for="pechatalka_add_image"])+ :is(button, .button) { + margin-left: auto; + } + >div.color { - --size: 1rem; - width: var(--size, 1rem); - height: var(--size, 1rem); + --size: 14px; + width: var(--size, 14px); + height: var(--size, 14px); border-radius: 3px; background-color: var(--color, var(--paper, var(--white, #fff))); @@ -84,7 +87,7 @@ section#pechatalka { } >section.canvas.pin { - --diameter-cut: var(--width, 300px); + --diameter-cut: 250px; --offset: calc(37 / 48 * 100); --diameter-display: calc(var(--diameter-cut) * var(--offset) / 100); position: relative; @@ -164,22 +167,29 @@ section#pechatalka { animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1); } - >button.delete { - --offset: 5px; - --size: 40px; - z-index: 1500; - position: fixed; - margin-top: var(--offset); - margin-left: calc((var(--size, 40px) + var(--offset)) * -1); - width: var(--size, 40px); - height: var(--size, 40px); - display: inline-flex; - justify-content: center; - align-items: center; - mix-blend-mode: overlay; + >button { + &:is(:disabled, [disabled="true"], .disabled) { + display: none !important; + pointer-events: none !important; + } - &:hover { - mix-blend-mode: normal; + &:is(.delete) { + --offset: 5px; + --size: 40px; + z-index: 1500; + position: fixed; + margin-top: var(--offset); + margin-left: calc((var(--size, 40px) + var(--offset)) * -1); + width: var(--size, 40px); + height: var(--size, 40px); + display: inline-flex; + justify-content: center; + align-items: center; + mix-blend-mode: overlay; + + &:hover { + mix-blend-mode: normal; + } } } } @@ -218,7 +228,7 @@ section#pechatalka { } */ &:after { - content: var(--localization-currency, "$"); + content: var(--currency, "?"); margin-left: 0.1rem; } } diff --git a/svoboda/pechatalka/system/views/templater.php b/svoboda/pechatalka/system/views/templater.php index b2aaede..8d67f0c 100755 --- a/svoboda/pechatalka/system/views/templater.php +++ b/svoboda/pechatalka/system/views/templater.php @@ -5,7 +5,8 @@ declare(strict_types=1); namespace svoboda\pechatalka\views; // Files of the project -use svoboda\pechatalka\models\enumerations\language; +use svoboda\pechatalka\models\enumerations\language, + svoboda\pechatalka\models\enumerations\currency; // Framework for PHP use mirzaev\minimal\controller; @@ -67,6 +68,9 @@ final class templater extends controller implements array_access $this->twig->addGlobal('server', $_SERVER); $this->twig->addGlobal('cookies', $_COOKIE); $this->twig->addGlobal('language', $language = $session?->buffer['language'] ?? language::en); + $this->twig->addGlobal('languages', language::cases()); + $this->twig->addGlobal('currency', $currency = $session?->buffer['currency'] ?? currency::usd); + $this->twig->addGlobal('currencies', currency::cases()); } /** @@ -83,7 +87,6 @@ final class templater extends controller implements array_access { // Generation and exit (success) return $this->twig->render('themes' . DIRECTORY_SEPARATOR . $this->twig->getGlobals()['theme'] . DIRECTORY_SEPARATOR . $file, $variables + $this->variables); - } /** @@ -113,7 +116,7 @@ final class templater extends controller implements array_access */ public function __get(string $name): mixed { - // Read the variable and exit (success) + // Read the variable and exit (success) return $this->variables[$name]; } @@ -128,7 +131,7 @@ final class templater extends controller implements array_access */ public function __unset(string $name): void { - // Delete the variable and exit (success) + // Delete the variable and exit (success) unset($this->variables[$name]); } @@ -174,7 +177,7 @@ final class templater extends controller implements array_access */ public function offsetGet(mixed $name): mixed { - // Read the variable and exit (success) + // Read the variable and exit (success) return $this->variables[$name]; } @@ -189,7 +192,7 @@ final class templater extends controller implements array_access */ public function offsetUnset(mixed $name): void { - // Delete the variable and exit (success) + // Delete the variable and exit (success) unset($this->variables[$name]); } @@ -208,4 +211,3 @@ final class templater extends controller implements array_access return isset($this->variables[$name]); } } - diff --git a/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/pechatalka.html b/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/pechatalka.html deleted file mode 100644 index 2fe1b1e..0000000 --- a/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/pechatalka.html +++ /dev/null @@ -1,30 +0,0 @@ -
-
- - -
- - - -
- - -
-
- -
- - 0 - -
-
diff --git a/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/title.html b/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/title.html deleted file mode 100644 index 2b47cf4..0000000 --- a/svoboda/pechatalka/system/views/themes/default/constructor/pin/elements/title.html +++ /dev/null @@ -1,4 +0,0 @@ -
-

Pechatalka

- Svoboda Work Union -
diff --git a/svoboda/pechatalka/system/views/themes/default/footer.html b/svoboda/pechatalka/system/views/themes/default/footer.html index 8322f33..eaab9af 100755 --- a/svoboda/pechatalka/system/views/themes/default/footer.html +++ b/svoboda/pechatalka/system/views/themes/default/footer.html @@ -3,6 +3,7 @@ {% block body %} {% endblock %} diff --git a/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/pechatalka.html b/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/pechatalka.html new file mode 100644 index 0000000..b737861 --- /dev/null +++ b/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/pechatalka.html @@ -0,0 +1,29 @@ +
+
+ + +
+ + + +
+ + +
+
+ +
+ + 0 + +
+
diff --git a/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/title.html b/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/title.html new file mode 100644 index 0000000..cf43563 --- /dev/null +++ b/svoboda/pechatalka/system/views/themes/default/generator/pin/elements/title.html @@ -0,0 +1,4 @@ +
+

{{ language.name == 'ru' ? 'Печаталка' : 'Pechatalka' }}

+ {{ language.name == 'ru' ? 'Рабочий Союз Свободы' : 'Svoboda Work Union' }} +
diff --git a/svoboda/pechatalka/system/views/themes/default/constructor/pin/page.html b/svoboda/pechatalka/system/views/themes/default/generator/pin/page.html similarity index 52% rename from svoboda/pechatalka/system/views/themes/default/constructor/pin/page.html rename to svoboda/pechatalka/system/views/themes/default/generator/pin/page.html index 54cdd26..ffc6759 100644 --- a/svoboda/pechatalka/system/views/themes/default/constructor/pin/page.html +++ b/svoboda/pechatalka/system/views/themes/default/generator/pin/page.html @@ -2,21 +2,24 @@ {% block css %} {{ parent() }} - + + + {% endblock %} {% block main %} - {% include "/themes/default/constructor/pin/elements/title.html" %} - {% include "/themes/default/constructor/pin/elements/pechatalka.html" %} + {% include "/themes/default/generator/pin/elements/title.html" %} + {% include "/themes/default/generator/pin/elements/pechatalka.html" %} {% endblock %} {% block js %} {{ parent() }} - - + + + {% endblock %} diff --git a/svoboda/pechatalka/system/views/themes/default/head.html b/svoboda/pechatalka/system/views/themes/default/head.html index c6b3a38..efb6a2d 100755 --- a/svoboda/pechatalka/system/views/themes/default/head.html +++ b/svoboda/pechatalka/system/views/themes/default/head.html @@ -1,28 +1,29 @@ {% block title %} -{% if head.title != empty %}{{ head.title }}{% else %}pechatalka by mirzaev{% endif %} + {% if head.title != empty %}{{ head.title }}{% else %}pechatalka by mirzaev{% endif %} {% endblock %} {% block meta %} - - -{% for meta in head.metas %} - -{% endfor %} + + + {% for meta in head.metas %} + + {% endfor %} {% endblock %} {% block css %} -{% for element in css %} - -{% endfor %} - - - - - - - - - + {% for element in css %} + + {% endfor %} + + + + + + + + + + {% endblock %} diff --git a/svoboda/pechatalka/system/views/themes/default/header.html b/svoboda/pechatalka/system/views/themes/default/header.html index 430650a..bda79a6 100755 --- a/svoboda/pechatalka/system/views/themes/default/header.html +++ b/svoboda/pechatalka/system/views/themes/default/header.html @@ -2,8 +2,8 @@ {% endblock %} {% block body %} -
-
+
+
{% endblock %} {% block js %} diff --git a/svoboda/pechatalka/system/views/themes/default/index.html b/svoboda/pechatalka/system/views/themes/default/index.html index ae92aea..24194ce 100755 --- a/svoboda/pechatalka/system/views/themes/default/index.html +++ b/svoboda/pechatalka/system/views/themes/default/index.html @@ -11,13 +11,16 @@ {% endblock %} {% block body %} + {% include '/themes/default/interface/select/language.html' %} + {% include '/themes/default/interface/select/currency.html' %} + {{ block('header') }} {{ block('aside') }}
{% block main %} - {{ main|raw }} + {{ main|raw }} {% endblock %}
diff --git a/svoboda/pechatalka/system/views/themes/default/interface/select/currency.html b/svoboda/pechatalka/system/views/themes/default/interface/select/currency.html new file mode 100644 index 0000000..c701d67 --- /dev/null +++ b/svoboda/pechatalka/system/views/themes/default/interface/select/currency.html @@ -0,0 +1,12 @@ +
+
+ {% for _currency in currencies %} + + + {% endfor %} +
+
diff --git a/svoboda/pechatalka/system/views/themes/default/interface/select/language.html b/svoboda/pechatalka/system/views/themes/default/interface/select/language.html new file mode 100644 index 0000000..8067983 --- /dev/null +++ b/svoboda/pechatalka/system/views/themes/default/interface/select/language.html @@ -0,0 +1,12 @@ +
+
+ {% for _language in languages %} + + + {% endfor %} +
+
diff --git a/svoboda/pechatalka/system/views/themes/default/js.html b/svoboda/pechatalka/system/views/themes/default/js.html index 13dd9f8..82a303b 100755 --- a/svoboda/pechatalka/system/views/themes/default/js.html +++ b/svoboda/pechatalka/system/views/themes/default/js.html @@ -6,9 +6,7 @@ - - {% if javascript is not empty %} @@ -22,4 +20,11 @@ }); {% endif %} + + {% endblock %}