promo! before react

This commit is contained in:
Zakhar 2026-08-24 06:45:21 +03:00
parent 9baa29bd68
commit dec37e92c8
4 changed files with 196 additions and 21 deletions

130
package-lock.json generated
View File

@ -5,7 +5,10 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"http-server": "^14.1.1" "http-server": "^14.1.1",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"styled-components": "^6.5.3"
}, },
"devDependencies": { "devDependencies": {
"@minify-html/node": "^0.18.1", "@minify-html/node": "^0.18.1",
@ -14,6 +17,21 @@
"ws": "^8.20.1" "ws": "^8.20.1"
} }
}, },
"node_modules/@emotion/is-prop-valid": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
"license": "MIT",
"dependencies": {
"@emotion/memoize": "^0.9.0"
}
},
"node_modules/@emotion/memoize": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
"license": "MIT"
},
"node_modules/@inversifyjs/common": { "node_modules/@inversifyjs/common": {
"version": "1.5.2", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/@inversifyjs/common/-/common-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@inversifyjs/common/-/common-1.5.2.tgz",
@ -453,6 +471,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/camelize": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@ -577,6 +604,32 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/css-color-keywords": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
"license": "ISC",
"engines": {
"node": ">=4"
}
},
"node_modules/css-to-react-native": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
"license": "MIT",
"dependencies": {
"camelize": "^1.0.0",
"css-color-keywords": "^1.0.0",
"postcss-value-parser": "^4.0.2"
}
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -1679,6 +1732,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@ -1713,6 +1772,27 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/react": {
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
"react": "^19.2.8"
}
},
"node_modules/reflect-metadata": { "node_modules/reflect-metadata": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
@ -1783,6 +1863,12 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/secure-compare": { "node_modules/secure-compare": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
@ -1947,6 +2033,48 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/styled-components": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.5.3.tgz",
"integrity": "sha512-vAX79sfpmUerP9fsTTxoTrBDE0RuO4ahjInyWYoohNgqrdg63Ms4q6FJ/o2Fyity82NU3cujOT8Ewl9TThBdwg==",
"license": "MIT",
"dependencies": {
"@emotion/is-prop-valid": "1.4.0",
"css-to-react-native": "3.2.0",
"csstype": "3.2.3",
"stylis": "4.3.6"
},
"engines": {
"node": ">= 16"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/styled-components"
},
"peerDependencies": {
"css-to-react-native": ">= 3.2.0",
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0",
"react-native": ">= 0.68.0"
},
"peerDependenciesMeta": {
"css-to-react-native": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/stylis": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
"integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",

View File

@ -4,7 +4,10 @@
"build": "./build.js" "build": "./build.js"
}, },
"dependencies": { "dependencies": {
"http-server": "^14.1.1" "http-server": "^14.1.1",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"styled-components": "^6.5.3"
}, },
"devDependencies": { "devDependencies": {
"@minify-html/node": "^0.18.1", "@minify-html/node": "^0.18.1",

View File

@ -75,7 +75,7 @@
} }
.loader:after, .loader:after,
.loader:before { .loader:before {
content: 'Собираем...'; content: 'Брок бегает по супермаркету...';
display: block; display: block;
position: absolute; position: absolute;
top: 0; bottom: 0; left: 0; right: 0; top: 0; bottom: 0; left: 0; right: 0;
@ -166,6 +166,7 @@
#total-price { font-size: 20px; line-height: 24px; font-weight: 500; color: #0C3B2E; margin-top: 4px;visibility: hidden;} #total-price { font-size: 20px; line-height: 24px; font-weight: 500; color: #0C3B2E; margin-top: 4px;visibility: hidden;}
.checkout-btn { background-color: #5EA12D;height: 54px; color: #ffffff; border: none; padding: 14px 32px; border-radius: 12px; font-size: 16px; font-weight: 370; line-height: 20px; cursor: pointer; transition: background 0.2s; } .checkout-btn { background-color: #5EA12D;height: 54px; color: #ffffff; border: none; padding: 14px 32px; border-radius: 12px; font-size: 16px; font-weight: 370; line-height: 20px; cursor: pointer; transition: background 0.2s; }
.total-wrap { display: flex; flex-direction: column; align-items: flex-start; } .total-wrap { display: flex; flex-direction: column; align-items: flex-start; }
.total-wrap > div { display: flex; align-items: center; }
.checkout-btn:hover { background-color: #72B541; } .checkout-btn:hover { background-color: #72B541; }
.checkout-btn:focus, .checkout-btn:focus-visible { background-color: #72B541;box-shadow: 0 4px 10px rgba(12, 59, 46, 0.1); } .checkout-btn:focus, .checkout-btn:focus-visible { background-color: #72B541;box-shadow: 0 4px 10px rgba(12, 59, 46, 0.1); }
.checkout-btn:active { background-color: #4A8D19;} .checkout-btn:active { background-color: #4A8D19;}
@ -215,6 +216,7 @@
.day-btn { flex: 1; height: 52px;max-width:140px;border: 2px solid #e1e1e1; border-radius: 8px; background: #ffffff; font-size: 16px; line-height:19px;font-weight: 350; color: #0C3B2E; cursor: pointer; transition: 0.2s; } .day-btn { flex: 1; height: 52px;max-width:140px;border: 2px solid #e1e1e1; border-radius: 8px; background: #ffffff; font-size: 16px; line-height:19px;font-weight: 350; color: #0C3B2E; cursor: pointer; transition: 0.2s; }
.day-btn:hover { border-color: #6ba531; } .day-btn:hover { border-color: #6ba531; }
.day-btn.active { background: #E6F696; border-color: #5EA12D; } .day-btn.active { background: #E6F696; border-color: #5EA12D; }
.day-btn:focus { outline: none; }
/* Семья (Кто будет есть?) */ /* Семья (Кто будет есть?) */
.family-list { display: flex; flex-direction: column; gap: 12px; } .family-list { display: flex; flex-direction: column; gap: 12px; }
@ -647,7 +649,7 @@
<div> <div>
<div class="summary-title">Ваша корзина на <span class="summary-days"> - </span></div> <div class="summary-title">Ваша корзина на <span class="summary-days"> - </span></div>
<div class="relative"> <div class="relative">
<div><div class="loader hidden" id="summary-loader">Собираем...</div></div> <div><div class="loader hidden" id="summary-loader">Брок бегает по супермаркету...</div></div>
<div class="summary-details" id="summary-details"> <div class="summary-details" id="summary-details">
<span>84 товара</span> <span>84 товара</span>
<b>&bull;</b> <b>&bull;</b>
@ -670,7 +672,7 @@
<div class="cart-footer"> <div class="cart-footer">
<div class="total-wrap"> <div class="total-wrap">
<div style="display: flex; align-items: center;"> <div>
<div class="icon-circle bg-green-light"> <div class="icon-circle bg-green-light">
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="44" height="44" rx="22" fill="#E6F696"/> <rect width="44" height="44" rx="22" fill="#E6F696"/>

View File

@ -1897,18 +1897,63 @@ const cartPolicy = trustedTypes.createPolicy('cartPolicy', {
}); });
function renderProducts() { function renderProducts() {
cartList.innerHTML = cartPolicy.createHTML(''); const existingNodesByKey = new Map();
Array.from(cartList.children).forEach(node => {
existingNodesByKey.set(node.dataset.productKey, node);
});
visibleProducts.forEach((item, index) => { visibleProducts.forEach((item, index) => {
let node = renderProduct(item, index) const key = item.name;
node.getElementsByClassName('item-image')[0].addEventListener('click', () => openModal( const signature = JSON.stringify({
item.image.replace('65,fit', '335,fit'), name: item.name,
item.name, n: item.n,
item.n, price: item.price,
item.total_price, total_price: item.total_price,
() => {console.log('replace')}, currency: item.currency,
() => {console.log('another')}, image: item.image,
)); unit: item.unit,
cartList.appendChild(node); });
let node = existingNodesByKey.get(key);
if (!node || node.dataset.productSignature !== signature) {
const oldNode = node;
node = renderProduct(item, index);
node.dataset.productKey = key;
node.dataset.productSignature = signature;
node.getElementsByClassName('item-image')[0].addEventListener('click', () => openModal(
item.image.replace('65,fit', '335,fit'),
item.name,
item.n,
item.total_price,
() => { console.log('replace') },
() => { console.log('another') },
));
if (oldNode) {
oldNode.replaceWith(node);
}
} else {
const menuBtn = node.querySelector('.menu-btn');
if (menuBtn) {
menuBtn.dataset.index = index;
}
}
const currentNodeAtPosition = cartList.children[index];
if (currentNodeAtPosition !== node) {
cartList.insertBefore(node, currentNodeAtPosition || null);
}
existingNodesByKey.delete(key);
});
existingNodesByKey.forEach(node => {
node.remove();
}); });
const remaining = allProducts.length - visibleProducts.length; const remaining = allProducts.length - visibleProducts.length;
@ -1918,6 +1963,7 @@ function renderProducts() {
} else { } else {
showMoreBtn.classList.add('hidden'); showMoreBtn.classList.add('hidden');
} }
updateSummaryCount(); updateSummaryCount();
calculateListHeight(); calculateListHeight();
} }
@ -2024,12 +2070,8 @@ function wholeRender(initialData) {
replaceProduct(allProducts[index].name).then(() => { replaceProduct(allProducts[index].name).then(() => {
li.remove(); li.remove();
document.querySelectorAll('.menu-dropdown.active').forEach(el => el.classList.remove('active')); document.querySelectorAll('.menu-dropdown.active').forEach(el => el.classList.remove('active'));
visibleProducts.splice(index, 1);
allProducts.splice(index, 1); allProducts.splice(index, 1);
visibleProducts = allProducts.slice(0, visibleProducts ? visibleProducts.length : 5);
if(visibleProducts.length < allProducts.length) {
visibleProducts.push(allProducts[visibleProducts.length]);
}
renderProducts(); renderProducts();
calculateListHeight(); calculateListHeight();
}); });