Compare commits

...

2 Commits

Author SHA1 Message Date
85b18161fd kolorki 2026-01-28 15:03:41 +01:00
09c14785e7 opinie, dodaj do koszyka, labelki 2026-01-27 10:39:21 +01:00
17 changed files with 761 additions and 309 deletions

View File

@@ -34,7 +34,7 @@ function idmObserveEachOncePM(elements, callback, options = {}) {
{ {
threshold: 0.1, threshold: 0.1,
...options, ...options,
} },
); );
elements.forEach((el) => observer.observe(el)); elements.forEach((el) => observer.observe(el));
@@ -45,7 +45,7 @@ idmObserveEachOncePM(
document.querySelectorAll(".idm_picture__module"), document.querySelectorAll(".idm_picture__module"),
(entry) => { (entry) => {
idmPictureModuleProductsPM(entry.target); idmPictureModuleProductsPM(entry.target);
} },
); );
// GRAPHQL QUERY // GRAPHQL QUERY
@@ -144,6 +144,7 @@ function idmGetSingleProdGraphQLPM({
name name
link link
type type
icon
${labels ? "zones" : ""} ${labels ? "zones" : ""}
${ ${
opinions opinions
@@ -239,7 +240,7 @@ async function idmHandleAddToBasketPM(e) {
Alertek.Error( Alertek.Error(
idmPhotoModuleLiteralsPM[ idmPhotoModuleLiteralsPM[
"Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę" "Coś poszło nie tak podczas dodawania do koszyka. Spróbuj ponownie lub odśwież stronę"
] ],
); );
buttonEl.innerHTML = `<span>${buttonEl.dataset.error}</span>`; buttonEl.innerHTML = `<span>${buttonEl.dataset.error}</span>`;
buttonEl.classList.add("--error"); buttonEl.classList.add("--error");
@@ -267,20 +268,10 @@ function idmMarkupAddToBasketPM(prodData) {
return `<form class="add_to_basket" action="/basketchange.php" type="post"> return `<form class="add_to_basket" action="/basketchange.php" type="post">
<input class="product__add_mode" type="hidden" value="1" name="mode"> <input class="product__add_mode" type="hidden" value="1" name="mode">
<input class="product__add_id" type="hidden" value="${ <input class="product__add_id" type="hidden" value="${prodData.id}" name="mode">
prodData.id <input class="product__add_size" type="hidden" value="${prodData.sizes?.[0]?.id || "uniw"}" name="mode">
}" name="mode"> <input class="product__add_number" type="hidden" value="${prodData.unit?.sellBy || 1}" name="mode">
<input class="product__add_size" type="hidden" value="${ <button class="btn --solid --medium add_to_basket__button" tabindex="0" data-success="${idmPhotoModuleLiteralsPM["Dodany"]}" data-error="${idmPhotoModuleLiteralsPM["Wystąpił błąd"]}" data-text="${idmPhotoModuleLiteralsPM["Do koszyka"]}">
prodData.sizes?.[0]?.id || "uniw"
}" name="mode">
<input class="product__add_number" type="hidden" value="${
prodData.unit?.sellBy || 1
}" name="mode">
<button class="btn --solid --medium add_to_basket__button" tabindex="0" data-success="${
idmPhotoModuleLiteralsPM["Dodany"]
}" data-error="${idmPhotoModuleLiteralsPM["Wystąpił błąd"]}" data-text="${
idmPhotoModuleLiteralsPM["Do koszyka"]
}">
<span>${idmPhotoModuleLiteralsPM["Do koszyka"]}</span> <span>${idmPhotoModuleLiteralsPM["Do koszyka"]}</span>
</button> </button>
</form>`; </form>`;
@@ -291,9 +282,7 @@ function idmMarkupPricePM({ prodData, addToBasket }) {
let priceMarkup; let priceMarkup;
if (!addToBasket) if (!addToBasket)
priceMarkup = `<div class="product_prices"><span class="price --normal --main">${ priceMarkup = `<div class="product_prices"><span class="price --normal --main">${prodData.price?.price?.[app_shop.vars.priceType]?.formatted}</span></div>`;
prodData.price?.price?.[app_shop.vars.priceType]?.formatted
}</span></div>`;
else { else {
const currentSize = prodData?.sizes?.[0] || prodData; const currentSize = prodData?.sizes?.[0] || prodData;
@@ -313,9 +302,7 @@ function idmMarkupPricePM({ prodData, addToBasket }) {
const maxPercent = currentSize?.price?.youSavePercent; const maxPercent = currentSize?.price?.youSavePercent;
priceMarkup = ` priceMarkup = `
<div class="product_prices ${omnibusPrice ? `--omnibus` : ""} ${ <div class="product_prices ${omnibusPrice ? `--omnibus` : ""} ${isOmnibusHigher ? `--omnibus-higher` : ""} ${omnibusPrice === maxPrice ? "--omnibus-short" : ""}">
isOmnibusHigher ? `--omnibus-higher` : ""
} ${omnibusPrice === maxPrice ? "--omnibus-short" : ""}">
<span class="price --normal --main">${price}</span> <span class="price --normal --main">${price}</span>
${ ${
omnibusPrice && typeof omnibusPercent === "number" omnibusPrice && typeof omnibusPercent === "number"
@@ -382,7 +369,7 @@ async function idmPictureModuleProductsPM(containerEL) {
opinions: isOpinions, opinions: isOpinions,
addToBasket: isAddToBasket, addToBasket: isAddToBasket,
})}`, })}`,
"" "",
)}}`, )}}`,
}), }),
}); });
@@ -420,41 +407,32 @@ async function idmPictureModuleProductsPM(containerEL) {
// OPINIE // OPINIE
let opinionsHTML = ""; let opinionsHTML = "";
if (isOpinions) { if (
isOpinions &&
typeof prodData.opinion?.rating === "number" &&
typeof prodData.opinion?.count === "number"
) {
opinionsHTML = ` opinionsHTML = `
<div class="product_opinions__stars"> <div class="product_opinions__stars">
<i class="icon-star ${ <i class="icon-star ${prodData.opinion.rating > 0.5 ? "--active" : ""}"></i>
prodData.opinion?.rating > 0.5 ? "--active" : "" <i class="icon-star ${prodData.opinion.rating > 1.5 ? "--active" : ""}"></i>
}"></i> <i class="icon-star ${prodData.opinion.rating > 2.5 ? "--active" : ""}"></i>
<i class="icon-star ${ <i class="icon-star ${prodData.opinion.rating > 3.5 ? "--active" : ""}"></i>
prodData.opinion?.rating > 1.5 ? "--active" : "" <i class="icon-star ${prodData.opinion.rating > 4.5 ? "--active" : ""}"></i>
}"></i>
<i class="icon-star ${
prodData.opinion?.rating > 2.5 ? "--active" : ""
}"></i>
<i class="icon-star ${
prodData.opinion?.rating > 3.5 ? "--active" : ""
}"></i>
<i class="icon-star ${
prodData.opinion?.rating > 4.5 ? "--active" : ""
}"></i>
</div> </div>
<span class="product_opinions__score">${ <span class="product_opinions__score">${prodData.opinion.rating} / 5.00 </span>
prodData.opinion.rating
} / 5.00 </span>
<span class="product_opinions__count">${prodData.opinion.count}</span> <span class="product_opinions__count">${prodData.opinion.count}</span>
`; `;
} }
prodEl.innerHTML = ` prodEl.innerHTML = `
${isLabels ? `<strong class="label_icons">${labelsHTML}</strong>` : ""} <a href="${prodData.link}" class="product_icon --mobile"><img src="${prodData.icon}" alt="${prodData.name}" tabindex="-1"/></a>
${ <div class="product_info__container">
isOpinions ${isLabels ? `<strong class="label_icons">${labelsHTML}</strong>` : ""}
? `<div class="product_opinions">${opinionsHTML}</div>` ${isOpinions ? `<div class="product_opinions">${opinionsHTML}</div>` : ""}
: "" <a class="product_name" href="${prodData.link}">${prodData.name}</a>
} ${idmMarkupPricePM({ prodData, addToBasket: isAddToBasket })}
<a class="product_name" href="${prodData.link}">${prodData.name}</a> ${isAddToBasket ? idmMarkupAddToBasketPM(prodData) : ""}
${idmMarkupPricePM({ prodData, addToBasket: isAddToBasket })} </div>
${isAddToBasket ? idmMarkupAddToBasketPM(prodData) : ""}
`; `;
idmInitEventsPM({ idmInitEventsPM({
@@ -464,7 +442,7 @@ async function idmPictureModuleProductsPM(containerEL) {
}); });
} catch (err) { } catch (err) {
allProdEl?.forEach((prodEl) => allProdEl?.forEach((prodEl) =>
prodEl.closest(".idm_picture__product")?.remove() prodEl.closest(".idm_picture__product")?.remove(),
); );
console.error(err); console.error(err);
} }
@@ -474,21 +452,31 @@ async function idmPictureModuleProductsPM(containerEL) {
document.body.addEventListener("click", (e) => { document.body.addEventListener("click", (e) => {
if (app_shop.vars.view === 3 || app_shop.vars.view === 4) return; if (app_shop.vars.view === 3 || app_shop.vars.view === 4) return;
const prodContainerEl = e.target.closest(".idm_picture__product"); const moduleCotnainer = e.target.closest(".idm_picture__module");
const prodContainerEl = e.target
.closest(".idm_picture__product")
?.querySelector(".product_info");
if (!prodContainerEl || !moduleCotnainer) return;
// ClearShow // ClearShow
if (!prodContainerEl) // if(!prodContainerEl) return document.querySelector(".idm_picture__product.--show")?.classList?.remove("--show");
return document // if(prodContainerEl.classList.contains("--show")) return prodContainerEl.classList.remove("--show");
.querySelector(".idm_picture__product.--show")
?.classList?.remove("--show");
if (prodContainerEl.classList.contains("--show"))
return prodContainerEl.classList.remove("--show");
const moduleContainer = prodContainerEl.closest(".idm_picture__module"); // const moduleContainer = prodContainerEl.closest(".idm_picture__module");
if (!moduleContainer) return; // if(!moduleContainer) return;
moduleContainer // moduleContainer.querySelectorAll(".idm_picture__product.--show").forEach(prodConEl=>prodConEl.classList.remove("--show"));
.querySelectorAll(".idm_picture__product.--show") // prodContainerEl.classList.add("--show");
.forEach((prodConEl) => prodConEl.classList.remove("--show")); const computedStyles = window.getComputedStyle(moduleCotnainer);
prodContainerEl.classList.add("--show"); const backgroundColor = computedStyles.getPropertyValue("--photo-mod-col-bg");
const textColor = computedStyles.getPropertyValue("--photo-mod-col-text");
new Modal({
element: prodContainerEl,
classList: "--mobile --photo-prod-mobile",
afterShow: (modal) => {
modal.style.setProperty("--photo-prod-box-bg", backgroundColor || "#fff");
modal.style.setProperty("--photo-prod-box-text", textColor || "#111");
},
});
}); });

View File

@@ -1,7 +1,8 @@
<style> <style>
.idm_picture__module { .idm_picture__module {
--photo-prod-box-bg: #fff; --photo-prod-box-bg: var(--photo-mod-col-bg, #fff);
--photo-prod-box-text: #111; --photo-prod-box-text: var(--photo-mod-col-text, #111);
--photo-prod-point-shadow: rgba(255, 255, 255, 0.5); --photo-prod-point-shadow: rgba(255, 255, 255, 0.5);
--photo-prod-box-pad-top: 1rem; --photo-prod-box-pad-top: 1rem;
@@ -32,8 +33,7 @@
position: absolute; position: absolute;
z-index: 10; z-index: 10;
} }
.idm_picture__product:hover, .idm_picture__product:hover {
.idm_picture__product.--show {
z-index: 20; z-index: 20;
} }
.idm_picture__img { .idm_picture__img {
@@ -114,15 +114,18 @@ PULSE ANIMATION
.product_info { .product_info {
background: var(--photo-prod-box-bg); background: var(--photo-prod-box-bg);
flex-direction: column;
padding: 1rem; padding: 1rem;
gap: 1rem;
display: none; display: none;
max-width: var(--photo-prod-box-width); max-width: var(--photo-prod-box-width);
position: absolute; position: absolute;
width: max-content; width: max-content;
box-shadow: 0px 0px 10px 1px #000; box-shadow: 0px 0px 10px 1px #000;
} }
.product_info__container {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 757px) { @media (min-width: 757px) {
.product_info { .product_info {
padding: 1.5rem; padding: 1.5rem;
@@ -294,19 +297,27 @@ PULSE ANIMATION
} }
} }
.idm_picture__product .product_info .product_name { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_info
.product_name {
font-size: 1.6rem; font-size: 1.6rem;
color: var(--photo-prod-box-text); color: var(--photo-prod-box-text);
} }
.idm_picture__product .product_info .product_name:hover { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_info
.product_name:hover {
color: var(--primary-color, #000) !important; color: var(--primary-color, #000) !important;
} }
.idm_picture__product .product_info .product_prices { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_info
.product_prices {
font-size: 1.6rem; font-size: 1.6rem;
color: var(--photo-prod-box-text); color: var(--photo-prod-box-text);
} }
.idm_picture__product .product_info .price.--main { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_info
.price.--main {
margin-bottom: 0; margin-bottom: 0;
} }
@@ -320,12 +331,6 @@ PULSE ANIMATION
animation: idmShowUp 0.3s ease-in-out; animation: idmShowUp 0.3s ease-in-out;
} }
} }
@media (max-width: 978px) {
.idm_picture__product.--show .product_info {
display: flex;
animation: idmShowUp 0.3s ease-in-out;
}
}
@keyframes idmShowUp { @keyframes idmShowUp {
from { from {
@@ -356,38 +361,101 @@ PULSE ANIMATION
} }
} }
.idm_picture__module .label_icons { :is(.idm_picture__module, .modal.--photo-prod-mobile) .label_icons {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
position: static; position: static;
} }
.idm_picture__module .product_opinions { :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_opinions {
display: flex; display: flex;
color: var(--photo-prod-box-text);
} }
.idm_picture__module .icon-star:not(.--active)::before { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.icon-star:not(.--active)::before {
content: "\f006"; content: "\f006";
} }
.idm_picture__module .icon-star.--active { :is(.idm_picture__module, .modal.--photo-prod-mobile) .icon-star.--active {
color: var(--opinions-star-active-color, #fac917); color: var(--opinions-star-active-color, #fac917);
} }
.idm_picture__module .product_opinions__score { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_opinions__score {
margin-left: 1rem; margin-left: 1rem;
margin-right: 0.5rem; margin-right: 0.5rem;
} }
.idm_picture__module .product_opinions__count::before { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_opinions__count::before {
content: "("; content: "(";
} }
.idm_picture__module .product_opinions__count::after { :is(.idm_picture__module, .modal.--photo-prod-mobile)
.product_opinions__count::after {
content: ")"; content: ")";
} }
.idm_picture__module :is(.add_to_basket, .add_to_basket__link) { :is(.idm_picture__module, .modal.--photo-prod-mobile)
:is(.add_to_basket, .add_to_basket__link) {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 0; margin-top: 0;
} }
:is(.idm_picture__module, .modal.--photo-prod-mobile)
:is(.price.--omnibus.omnibus_price, .price.--max) {
color: var(--photo-prod-box-text);
}
:is(.idm_picture__module, .modal.--photo-prod-mobile)
.--omnibus-higher
:is(.price.--normal.--main, .price_percent) {
color: var(--color-promo-price, #cd2323);
}
.idm_picture__module .product_icon {
display: none !important;
}
@media (max-width: 978px) {
.modal.--photo-prod-mobile .product_info.--mod-init {
position: static;
display: flex;
width: 100%;
box-shadow: none;
flex-direction: column;
}
.modal.--photo-prod-mobile .modal__wrapper {
border-radius: 10px 10px 0 0;
background-color: var(--photo-prod-box-bg);
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile .modal__close {
position: absolute;
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile
.modal__close
:is(.product_name, .omnibus_price, .price.--max) {
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile .product_icon {
display: flex;
justify-content: center;
align-items: center;
}
}
@media (min-width: 420px) and (max-width: 978px) {
.modal.--photo-prod-mobile .product_info.--mod-init {
display: grid;
grid-template-columns: 1fr 2fr;
}
.modal.--photo-prod-mobile .product_info.--mod-init > *:not(.product_icon) {
grid-column: "2/3";
}
.modal.--photo-prod-mobile .product_info.--mod-init > .product_icon {
grid-row: "1/-1";
}
}
</style> </style>

62
package-lock.json generated
View File

@@ -13,6 +13,7 @@
"@mui/icons-material": "^7.3.6", "@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6", "@mui/material": "^7.3.6",
"react": "^19.2.0", "react": "^19.2.0",
"react-color": "^2.19.3",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-hot-toast": "^2.6.0", "react-hot-toast": "^2.6.0",
"react-router-dom": "^7.11.0", "react-router-dom": "^7.11.0",
@@ -1076,6 +1077,15 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@icons/material": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
"integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13", "version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -2999,6 +3009,18 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
"license": "MIT"
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -3028,6 +3050,12 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==",
"license": "ISC"
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3268,6 +3296,7 @@
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -3300,6 +3329,24 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-color": {
"version": "2.19.3",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
"integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
"license": "MIT",
"dependencies": {
"@icons/material": "^0.2.4",
"lodash": "^4.17.15",
"lodash-es": "^4.17.15",
"material-colors": "^1.2.1",
"prop-types": "^15.5.10",
"reactcss": "^1.2.0",
"tinycolor2": "^1.4.1"
},
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "19.2.3", "version": "19.2.3",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
@@ -3404,6 +3451,15 @@
"react-dom": ">=16.6.0" "react-dom": ">=16.6.0"
} }
}, },
"node_modules/reactcss": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
"integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
"license": "MIT",
"dependencies": {
"lodash": "^4.0.1"
}
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.11", "version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -3583,6 +3639,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/tinycolor2": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
"license": "MIT"
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.15", "version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",

View File

@@ -15,6 +15,7 @@
"@mui/icons-material": "^7.3.6", "@mui/icons-material": "^7.3.6",
"@mui/material": "^7.3.6", "@mui/material": "^7.3.6",
"react": "^19.2.0", "react": "^19.2.0",
"react-color": "^2.19.3",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-hot-toast": "^2.6.0", "react-hot-toast": "^2.6.0",
"react-router-dom": "^7.11.0", "react-router-dom": "^7.11.0",

View File

@@ -3,7 +3,6 @@ import Home from "./pages/Home";
import AppLayout from "./ui/AppLayout"; import AppLayout from "./ui/AppLayout";
import Instruction from "./pages/Instruction"; import Instruction from "./pages/Instruction";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import { Alert } from "@mui/material";
function App() { function App() {
return ( return (

View File

@@ -23,7 +23,16 @@ export const DEFAULT_POINT = {
direction: Object.keys(DIRECTIONS)[0], direction: Object.keys(DIRECTIONS)[0],
}; // Default point structure }; // Default point structure
export const URL_RADIO_DATA = [ export const DEFAULT_COLOR_PALLETE = [
{ value: "single", label: "Jedno Zdjęcie" }, "#D9E3F0",
{ value: "rwd", label: "Trzy Zdjęcia (RWD)" }, "#F47373",
"#697689",
"#37D67A",
"#2CCCE4",
"#555555",
"#dce775",
"#ff8a65",
"#ba68c8",
"#111111",
"#f2f2f2",
]; ];

View File

@@ -17,6 +17,13 @@ export default function CodeBox() {
} }
}, [state.urls, state.points, state.photoAlt, state.previewMode]); // reactive slices }, [state.urls, state.points, state.photoAlt, state.previewMode]); // reactive slices
if (
import.meta.env.MODE === "production" &&
window.location.href.includes("https://zooart6.yourtechnicaldomain.com/") &&
!window.location.search.includes("dev=test")
)
return null;
const copyCode = () => { const copyCode = () => {
navigator.clipboard.writeText(code); navigator.clipboard.writeText(code);
toast.success("Skopiowano tekst!"); toast.success("Skopiowano tekst!");

View File

@@ -1,40 +1,18 @@
import styled from "@emotion/styled"; import { Tab, Tabs } from "@mui/material";
import { Button, Tab, Tabs } from "@mui/material";
import { BREAKPOINTS } from "../../../constants/rwd"; import { BREAKPOINTS } from "../../../constants/rwd";
import { useSharedState } from "../../../store/useSharedState"; import { useSharedState } from "../../../store/useSharedState";
import { capitalizeFirstLetter } from "../../../utils/capitalizeFirstLetter"; import { capitalizeFirstLetter } from "../../../utils/capitalizeFirstLetter";
const StyledPreviewTabsContainer = styled("div")({
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: "0.1rem",
// backgroundColor: "#060606",
});
function PreviewRWDTabs() { function PreviewRWDTabs() {
const currentPreviewMode = useSharedState((state) => state.previewMode); const currentPreviewMode = useSharedState((state) => state.previewMode);
const setPreviewMode = useSharedState((state) => state.setPreviewMode); const setField = useSharedState((state) => state.setField);
if (currentPreviewMode === "single") return null; if (currentPreviewMode === "single") return null;
return ( return (
// <StyledPreviewTabsContainer>
// {currentPreviewMode === "single"
// ? null
// : Object.keys(BREAKPOINTS).map((key) => (
// <Button
// key={key}
// variant="contained"
// disabled={currentPreviewMode === key}
// onClick={() => setPreviewMode(key)}
// >
// {capitalizeFirstLetter(key)}
// </Button>
// ))}
// </StyledPreviewTabsContainer>
<Tabs <Tabs
value={currentPreviewMode} value={currentPreviewMode}
onChange={(_, newVal) => setPreviewMode(newVal)} onChange={(_, newVal) => setField("previewMode", newVal)}
variant="fullWidth" variant="fullWidth"
sx={(theme) => ({ sx={(theme) => ({
backgroundColor: theme.palette.background.header, backgroundColor: theme.palette.background.header,

View File

@@ -7,10 +7,27 @@ function GeneratePreview({ preview = true }) {
const urls = useSharedState((state) => state.urls); const urls = useSharedState((state) => state.urls);
const uniqueId = Math.ceil(Math.random() * 100000 + 1); const uniqueId = Math.ceil(Math.random() * 100000 + 1);
const labels = useSharedState((state) => state.labels);
const opinions = useSharedState((state) => state.opinions);
const addToBasket = useSharedState((state) => state.addToBasket);
const bgColor = useSharedState((state) => state.bgColor);
const textColor = useSharedState((state) => state.textColor);
if (preview && urls[previewMode] === "") return null; if (preview && urls[previewMode] === "") return null;
return ( return (
<div className="idm_picture__module" id={`idm-picture-module-${uniqueId}`}> <div
className="idm_picture__module"
id={`idm-picture-module-${uniqueId}`}
data-labels={labels && true}
data-opinions={opinions && true}
data-add-to-basket={addToBasket && true}
style={{
"--photo-mod-col-bg": bgColor,
"--photo-mod-col-text": textColor,
}}
>
{<GeneratePreviewImage preview={preview} urls={urls} />} {<GeneratePreviewImage preview={preview} urls={urls} />}
{<GeneratePreviewPoints preview={preview} uniqueId={uniqueId} />} {<GeneratePreviewPoints preview={preview} uniqueId={uniqueId} />}
</div> </div>

View File

@@ -1,13 +1,15 @@
import { useSharedState } from "../../../store/useSharedState"; import { useSharedState } from "../../../store/useSharedState";
function GeneratePreviewImage({ preview, urls }) { function GeneratePreviewImage({ preview, urls }) {
const imagePrefix =
import.meta.env.MODE === "development"
? `${import.meta.env.VITE_PUBLIC_URL}/`
: "";
const previewMode = useSharedState((state) => state.previewMode); const previewMode = useSharedState((state) => state.previewMode);
const photoAlt = useSharedState((state) => state.photoAlt); const photoAlt = useSharedState((state) => state.photoAlt);
const imagePrefix =
import.meta.env.MODE === "development" &&
!urls[previewMode]?.includes("https://")
? `${import.meta.env.VITE_PUBLIC_URL}/`
: "";
if (preview) if (preview)
return ( return (
<img <img

View File

@@ -12,6 +12,9 @@ function GeneratePreviewSinglePoint({
const { positions, id } = useSharedState((state) => state.points[index]); const { positions, id } = useSharedState((state) => state.points[index]);
const previewMode = useSharedState((state) => state.previewMode); const previewMode = useSharedState((state) => state.previewMode);
const product = useSharedState((state) => state.products[id]); const product = useSharedState((state) => state.products[id]);
const labels = useSharedState((state) => state.labels);
const opinions = useSharedState((state) => state.opinions);
const addToBasket = useSharedState((state) => state.addToBasket);
const setSinglePointPosition = useSharedState( const setSinglePointPosition = useSharedState(
(state) => state.setSinglePointPosition, (state) => state.setSinglePointPosition,
@@ -37,63 +40,46 @@ function GeneratePreviewSinglePoint({
}; };
}; };
if (preview) // WARUNKI DLA STYLE/DATA ATRYBUTY
return ( const mainContainerStyles = () => {
<div if (preview)
className="idm_picture__product" return {
style={{ top: `${positions[previewMode].y}%`,
top: `${positions[previewMode].y}%`, left: `${positions[previewMode].x}%`,
left: `${positions[previewMode].x}%`, display: positions[previewMode].hide ? "none" : "block",
display: positions[previewMode].hide ? "none" : "block", };
}}
> if (previewMode === "single")
<button return { top: `${positions.single.y}%`, left: `${positions.single.x}%` };
className="idm_picture__product_point" else
aria-describedby={prodBoxUniqueId} return Object.keys(BREAKPOINTS).reduce((acc, key) => {
aria-label="Product Info" acc[`--photo-prod-point-${key}-top`] = `${positions[key].y}%`;
onPointerDown={onPointerDown} acc[`--photo-prod-point-${key}-left`] = `${positions[key].x}%`;
> acc[`--photo-prod-point-${key}-display`] = positions[key].hide
+ ? "none"
</button> : "block";
<div return acc;
className="product_info" }, {});
id={prodBoxUniqueId} };
data-id={id} const productInfoDataAttributes = () => {
{...generatePointDataAttribbutes(previewMode)} if (preview || previewMode === "single")
> return generatePointDataAttribbutes(previewMode);
<a className="product_name" href="#"> else
{product ? product?.name : `Produkt ${index + 1}`} return {
</a> ...generatePointDataAttribbutes("mobile"),
<span className="product_price"> ...generatePointDataAttribbutes("tablet"),
{product ...generatePointDataAttribbutes("desktop"),
? product?.price?.price?.gross?.formatted };
: `Produkt ${index + 1}`} };
</span>
</div>
</div>
);
return ( return (
<div <div className="idm_picture__product" style={mainContainerStyles()}>
className="idm_picture__product"
style={
previewMode === "single"
? { top: `${positions.single.y}%`, left: `${positions.single.x}%` }
: Object.keys(BREAKPOINTS).reduce((acc, key) => {
acc[`--photo-prod-point-${key}-top`] = `${positions[key].y}%`;
acc[`--photo-prod-point-${key}-left`] = `${positions[key].x}%`;
acc[`--photo-prod-point-${key}-display`] = positions[key].hide
? "none"
: "block";
return acc;
}, {})
}
>
<button <button
className="idm_picture__product_point" className="idm_picture__product_point"
aria-describedby={prodBoxUniqueId} aria-describedby={prodBoxUniqueId}
tabIndex={-1} tabIndex={-1}
aria-hidden="true" aria-hidden="true"
onPointerDown={onPointerDown}
> >
+ +
</button> </button>
@@ -101,14 +87,52 @@ function GeneratePreviewSinglePoint({
className="product_info" className="product_info"
id={prodBoxUniqueId} id={prodBoxUniqueId}
data-id={id} data-id={id}
{...(previewMode === "single" {...productInfoDataAttributes()}
? generatePointDataAttribbutes(previewMode) >
: { {preview && (
...generatePointDataAttribbutes("mobile"), <div class="product_info__container">
...generatePointDataAttribbutes("tablet"), {labels && (
...generatePointDataAttribbutes("desktop"), <strong class="label_icons">
})} <span class="label --new">Nowość</span>
></div> </strong>
)}
{opinions && (
<div class="product_opinions">
<div class="product_opinions__stars">
<i class="icon-star">*</i>
<i class="icon-star">*</i>
<i class="icon-star">*</i>
<i class="icon-star">*</i>
<i class="icon-star">*</i>
</div>
<span class="product_opinions__score">0 / 5.00 </span>
<span class="product_opinions__count">0</span>
</div>
)}
<a className="product_name" href="#">
{product ? product?.name : `Produkt ${index + 1}`}
</a>
<div class="product_prices">
<span class="price --normal --main">
{product
? product?.price?.price?.gross?.formatted
: `${index + 1}`}
</span>
</div>
{addToBasket && (
<button
class="btn --solid --medium add_to_basket__button"
tabindex="0"
data-success="Dodany"
data-error="Wystąpił błąd"
data-text="Do koszyka"
>
<span>Do koszyka</span>
</button>
)}
</div>
)}
</div>
</div> </div>
); );
} }

View File

@@ -1,10 +1,12 @@
function GenerateStyle() { function GenerateStyle() {
return ( return (
<style> <>
{` <style>
{`
.idm_picture__module{ .idm_picture__module{
--photo-prod-box-bg: #fff; --photo-prod-box-bg: var(--photo-mod-col-bg, #fff);
--photo-prod-box-text: #111; --photo-prod-box-text: var(--photo-mod-col-text, #111);
--photo-prod-point-shadow: rgba(255,255,255,.5); --photo-prod-point-shadow: rgba(255,255,255,.5);
--photo-prod-box-pad-top: 1em; --photo-prod-box-pad-top: 1em;
@@ -35,7 +37,7 @@ function GenerateStyle() {
position: absolute; position: absolute;
z-index: 10; z-index: 10;
} }
.idm_picture__product:hover, .idm_picture__product.--show{ .idm_picture__product:hover{
z-index: 20; z-index: 20;
} }
.idm_picture__img{ .idm_picture__img{
@@ -116,15 +118,18 @@ PULSE ANIMATION
.product_info{ .product_info{
background: var(--photo-prod-box-bg); background: var(--photo-prod-box-bg);
flex-direction: column;
padding: 1em; padding: 1em;
gap: 1em;
display: none; display: none;
max-width: var(--photo-prod-box-width); max-width: var(--photo-prod-box-width);
position: absolute; position: absolute;
width: max-content; width: max-content;
box-shadow: 0px 0px 10px 1px #000; box-shadow: 0px 0px 10px 1px #000;
} }
.product_info__container{
display: flex;
flex-direction: column;
gap: 1em;
}
@media (min-width: 757px){ @media (min-width: 757px){
.product_info{ .product_info{
padding: 1.5em; padding: 1.5em;
@@ -296,19 +301,19 @@ PULSE ANIMATION
} }
} }
.idm_picture__product .product_info .product_name{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_info .product_name{
font-size: 1.6em; font-size: 1.6em;
color: var(--photo-prod-box-text); color: var(--photo-prod-box-text);
} }
.idm_picture__product .product_info .product_name:hover{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_info .product_name:hover{
color: var(--primary-color, #000)!important; color: var(--primary-color, #000)!important;
} }
.idm_picture__product .product_info .product_prices{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_info .product_prices{
font-size: 1.6em; font-size: 1.6em;
color: var(--photo-prod-box-text); color: var(--photo-prod-box-text);
} }
.idm_picture__product .product_info .price.--main{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_info .price.--main{
margin-bottom: 0; margin-bottom: 0;
} }
@@ -318,12 +323,6 @@ PULSE ANIMATION
animation: idmShowUp 0.3s ease-in-out; animation: idmShowUp 0.3s ease-in-out;
} }
} }
@media(max-width: 978px){
.idm_picture__product.--show .product_info{
display: flex;
animation: idmShowUp 0.3s ease-in-out;
}
}
@keyframes idmShowUp{ @keyframes idmShowUp{
from{ from{
@@ -354,47 +353,154 @@ PULSE ANIMATION
} }
} }
.idm_picture__module .label_icons{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .label_icons{
display: flex; display: flex;
gap: 0.5em; gap: 0.5em;
position: static; position: static;
} }
.idm_picture__module .product_opinions{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_opinions{
display: flex; display: flex;
color: var(--photo-prod-box-text);
} }
.idm_picture__module .icon-star:not(.--active)::before{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .icon-star:not(.--active)::before{
content: "\f006"; content: "\f006";
} }
.idm_picture__module .icon-star.--active{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .icon-star.--active{
color: var(--opinions-star-active-color, #fac917); color: var(--opinions-star-active-color, #fac917);
} }
.idm_picture__module .product_opinions__score{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_opinions__score{
margin-left: 1em; margin-left: 1em;
margin-right: 0.5em; margin-right: 0.5em;
} }
.idm_picture__module .product_opinions__count::before{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_opinions__count::before{
content: "("; content: "(";
} }
.idm_picture__module .product_opinions__count::after{ :is(.idm_picture__module, .modal.--photo-prod-mobile) .product_opinions__count::after{
content: ")"; content: ")";
} }
.idm_picture__module :is(.add_to_basket, .add_to_basket__link){ :is(.idm_picture__module, .modal.--photo-prod-mobile) :is(.add_to_basket, .add_to_basket__link){
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 0; margin-top: 0;
} }
:is(.idm_picture__module, .modal.--photo-prod-mobile) :is(.price.--omnibus.omnibus_price, .price.--max){
.idm_picture__product_point{ color: var(--photo-prod-box-text);
cursor: grab;
} }
:is(.idm_picture__module, .modal.--photo-prod-mobile) .--omnibus-higher :is(.price.--normal.--main, .price_percent){
color: var(--color-promo-price, #cd2323);
}
.idm_picture__module .product_icon{
display: none!important;
}
// MOBILE
@media (max-width: 978px){
.modal.--photo-prod-mobile .product_info.--mod-init{
position: static;
display: flex;
width: 100%;
box-shadow: none;
}
.modal.--photo-prod-mobile .modal__wrapper{
border-radius: 10px 10px 0 0;
background-color: var(--photo-prod-box-bg);
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile .modal__close{
position: absolute;
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile .modal__close :is(.product_name, .omnibus_price, .price.--max){
color: var(--photo-prod-box-text);
}
.modal.--photo-prod-mobile .product_icon{
display: flex;
justify-content: center;
align-items: center;
}
}
@media(min-width: 400px) and (max-width: 978px){
.modal.--photo-prod-mobile .product_info.--mod-init {
display: grid;
grid-template-columns: 1fr 2fr;
}
.modal.--photo-prod-mobile .product_info.--mod-init > *:not(.product_icon){
grid-column: ~"2/3";
}
.modal.--photo-prod-mobile .product_info.--mod-init > .product_icon{
grid-row: ~"1/-1"
}
}
`} `}
</style> </style>
<style>
{`
.idm_picture__product_point{
cursor: grab;
}
.idm_picture__product .product_info .product_prices{
font-size: 1.6em;
color: #000;
}
.label_icons > *{
font-size: 1em;
color: #fff;
background: #0090f6;
font-weight: 600;
padding: 4px 6px;
border-radius: 5px;
}
[class^="icon-"], [class*=" icon-"]{
font-family: FontAwesome;
}
.icon-star:before {
content: "\f005" / "";
}
.product_opinions{
font-size: 1.4em;
}
.btn{
background: none;
padding: 0;
border: 1px solid transparent;
display: inline-block;
text-decoration: none;
color: #E11D48;
width: 100%;
text-align: center;
cursor: pointer;
font-size: 1.2em;
padding: 0.9em 1em;
font-weight: normal;
transition: background-color 0.2s, color 0.2s, border-color 0.2s;
border-radius: 5px;
}
.btn.--solid {
background: #E11D48;
border: 1px solid #E11D48;
color: #fff;
}
.btn.--medium {
font-size: 1.4em;
padding: 1.25em 1.4em;
}
`}
</style>
</>
); );
} }

View File

@@ -1,35 +1,107 @@
import { useState } from "react";
import GenericBox from "../../ui/GenericBox";
import { URL_RADIO_DATA } from "./../../constants/photo";
import { useSharedState } from "../../store/useSharedState"; import { useSharedState } from "../../store/useSharedState";
import GenericRadioGroup from "./../../ui/GenericRadioGroup"; import {
import { Divider } from "@mui/material"; Alert,
Divider,
FormControlLabel,
FormGroup,
Switch,
} from "@mui/material";
import ColorPickerInput from "../../ui/ColorPickerInput";
import { DEFAULT_COLOR_PALLETE } from "../../constants/photo";
function PhotoGenericOptions() { function PhotoGenericOptions() {
const [urlPoint, setUrlPoint] = useState("single"); const setField = useSharedState((state) => state.setField);
const setPreviewMode = useSharedState((state) => state.setPreviewMode); const toggleField = useSharedState((state) => state.toggleField);
const handleUrlRadioChange = (event) => { const isRWD = useSharedState((state) => state.isRWD);
setUrlPoint(event.target.value); const labels = useSharedState((state) => state.labels);
setPreviewMode(event.target.value === "rwd" ? "desktop" : "single"); const opinions = useSharedState((state) => state.opinions);
const addToBasket = useSharedState((state) => state.addToBasket);
const bgColor = useSharedState((state) => state.bgColor);
const textColor = useSharedState((state) => state.textColor);
const handleUrlRadioChange = (e) => {
toggleField("isRWD");
setField("previewMode", e.target.checked ? "desktop" : "single");
}; };
return ( return (
<GenericBox variant="inner" title="Opcje"> // <GenericBox variant="inner" title="Opcje">
<GenericRadioGroup <FormGroup sx={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
{/* <GenericRadioGroup
radioData={URL_RADIO_DATA} radioData={URL_RADIO_DATA}
value={urlPoint} value={isRwd}
onChange={handleUrlRadioChange} onChange={handleUrlRadioChange}
direction="row" direction="row"
/> */}
<FormControlLabel
control={<Switch checked={isRWD} onChange={handleUrlRadioChange} />}
label="Czy wyświetlać inne zdjęcia dla innych szerokości ekranu (RWD)"
/> />
<Divider />
Labelki <Divider sx={{ borderColor: "#ccc" }} />
<Divider />
Opinie <FormControlLabel
<Divider /> control={
Dodaj do koszyka <Switch checked={labels} onChange={() => toggleField("labels")} />
<Divider /> }
</GenericBox> label="Czy wyświetlać labele produktu (np Nowość, Promocja itp)"
/>
<Divider sx={{ borderColor: "#ccc" }} />
<FormControlLabel
control={
<Switch checked={opinions} onChange={() => toggleField("opinions")} />
}
label="Czy wyświetlać dane o opinii o produkcie"
/>
<Divider sx={{ borderColor: "#ccc" }} />
<FormControlLabel
control={
<Switch
checked={addToBasket}
onChange={() => toggleField("addToBasket")}
/>
}
label="Czy wyświetlać przycisk dodania do koszyka na produkcie"
/>
<Divider sx={{ borderColor: "#ccc" }} />
<div
style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "2rem" }}
>
<ColorPickerInput
color={bgColor}
onChange={(newColor) => setField("bgColor", newColor)}
label="Kolor Tła"
palette={DEFAULT_COLOR_PALLETE}
/>
<ColorPickerInput
color={textColor}
onChange={(newColor) => setField("textColor", newColor)}
label="Kolor Tekstu"
palette={DEFAULT_COLOR_PALLETE}
/>
</div>
<Divider sx={{ borderColor: "#ccc" }} />
<Alert severity="warning">
Uwaga!
<br />
Wygląd boxa produktowego na podglądzie może się różnić od tego
wrzuconego na sklep. Aplikacja nie pobiera czcionek ani kolorów ze
sklepu!
<br />
Podgląd wyświetla tylko jeden testowy label i nie wyświetla danych
omnibusa.
</Alert>
</FormGroup>
// {/* </GenericBox> */}
); );
} }

View File

@@ -10,7 +10,7 @@ function PhotoUrl() {
const setUrl = useSharedState((state) => state.setUrl); const setUrl = useSharedState((state) => state.setUrl);
const photoAlt = useSharedState((state) => state.photoAlt); const photoAlt = useSharedState((state) => state.photoAlt);
const setPhotoAlt = useSharedState((state) => state.setPhotoAlt); const setField = useSharedState((state) => state.setField);
const handleChangeURL = ({ event, type }) => { const handleChangeURL = ({ event, type }) => {
event.preventDefault(); event.preventDefault();
@@ -29,7 +29,7 @@ function PhotoUrl() {
<Divider /> <Divider />
<InputField <InputField
name="Alt zdjęcia" name="Alt zdjęcia"
onChange={(e) => setPhotoAlt(e.target.value)} onChange={(e) => setField("photoAlt", e.target.value)}
value={photoAlt} value={photoAlt}
/> />
</GenericBox> </GenericBox>

View File

@@ -1,28 +1,26 @@
import React from 'react' import React from "react";
import ReactDOM from 'react-dom/client' import ReactDOM from "react-dom/client";
import App from './App' import App from "./App";
import { ThemeProvider, CssBaseline } from '@mui/material'; import { ThemeProvider, CssBaseline } from "@mui/material";
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from "react-router-dom";
import theme from './styles/theme'; import theme from "./styles/theme";
// import ThemeCssVars from './styles/ThemeCssVars'; // import ThemeCssVars from './styles/ThemeCssVars';
const BASENAME = import.meta.env.VITE_PUBLIC_URL || "/"; const BASENAME = import.meta.env.VITE_PUBLIC_URL || "/";
const path = window.location.pathname; const path = window.location.pathname;
if (path.endsWith('index.html')) { if (path.endsWith("index.html")) {
window.location.replace(BASENAME); window.location.replace(BASENAME);
} }
ReactDOM.createRoot(document.getElementById("root")).render(
ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode>
<React.StrictMode> <BrowserRouter basename={BASENAME}>
<BrowserRouter basename={BASENAME}> <ThemeProvider theme={theme}>
<ThemeProvider theme={theme}> <CssBaseline />
<CssBaseline/> {/* <ThemeCssVars/> */}
{/* <ThemeCssVars/> */} <App />
<App /> </ThemeProvider>
</ThemeProvider> </BrowserRouter>
</BrowserRouter> </React.StrictMode>,
</React.StrictMode> );
)

View File

@@ -1,4 +1,5 @@
import { create } from "zustand"; import { create } from "zustand";
import { persist } from "zustand/middleware";
import { DEFAULT_POINT } from "./../constants/photo"; import { DEFAULT_POINT } from "./../constants/photo";
// export const createBox = () => ({}); // export const createBox = () => ({});
@@ -7,67 +8,116 @@ const defaultState = {
points: [{ ...DEFAULT_POINT }], points: [{ ...DEFAULT_POINT }],
photoAlt: "Zdjęcie pokazowe", photoAlt: "Zdjęcie pokazowe",
previewMode: "single", // desktop, tablet, mobile previewMode: "single", // desktop, tablet, mobile
isRWD: false,
labels: false,
opinions: false,
addToBasket: false,
//colros
bgColor: "#fff",
textColor: "#060606",
}; };
export const useSharedState = create((set, get) => ({ export const useSharedState = create(
//DATA persist(
...defaultState, (set, get) => ({
products: {}, //DATA
...defaultState,
products: {},
//SETTERS/UPDATERS //SETTERS/UPDATERS
setUrl: (type, url) => setField: (key, value) =>
set((state) => ({ urls: { ...state.urls, [type]: url } })), set(() => ({
[key]: value,
})),
setPhotoAlt: (alt) => set({ photoAlt: alt }), toggleField: (key) =>
setPoints: (points) => set({ points }), set((state) => ({
setPointsLength: (length) => set({ pointsLength: length }), [key]: !state[key],
setPreviewMode: (mode) => set({ previewMode: mode }), })),
// Update a single point by ID // setPhotoAlt: (alt) => set({ photoAlt: alt }),
setSinglePoint: (id, newData) => // setPreviewMode: (mode) => set({ previewMode: mode }),
set((state) => ({
points: state.points.map((p, index) =>
index === id ? { ...p, ...newData } : p
),
})),
setSinglePointPosition: (id, brName, positionName, newValue) => /* =========
set((state) => ({ URLS
points: state.points.map((p, index) => ========= */
index === id setUrl: (type, url) =>
? { set((state) => ({ urls: { ...state.urls, [type]: url } })),
...p,
positions: {
...p.positions,
[brName]: {
...p.positions[brName],
[positionName]: newValue,
},
},
}
: p
),
})),
// Add a point AND increment pointsLength /* =========
addSinglePoint: (point) => POINTS
set((state) => ({ ========= */
points: [...state.points, point],
})),
// Remove a point AND decrement pointsLength setPoints: (points) => set({ points }),
removeSinglePoint: (id) => // Update a single point by ID
set((state) => ({ setSinglePoint: (id, newData) =>
points: state.points.filter((p, index) => index !== id), set((state) => ({
})), points: state.points.map((p, index) =>
index === id ? { ...p, ...newData } : p,
),
})),
clearAll: () => set(() => ({ ...defaultState })), setSinglePointPosition: (id, brName, positionName, newValue) =>
set((state) => ({
points: state.points.map((p, index) =>
index === id
? {
...p,
positions: {
...p.positions,
[brName]: {
...p.positions[brName],
[positionName]: newValue,
},
},
}
: p,
),
})),
addProduct: (prod) => // Add a point AND increment pointsLength
set((state) => ({ addSinglePoint: (point) =>
products: { set((state) => ({
...state.products, points: [...state.points, point],
[prod.id]: prod, })),
},
})), // Remove a point AND decrement pointsLength
})); removeSinglePoint: (id) =>
set((state) => ({
points: state.points.filter((p, index) => index !== id),
})),
/* =========
PRODUCTS
========= */
addProduct: (prod) =>
set((state) => ({
products: {
...state.products,
[prod.id]: prod,
},
})),
/* =========
RESET
========= */
clearAll: () => set(() => ({ ...defaultState })),
}),
{
name: "photo-generic-options",
partialize: (state) => ({
labels: state.labels,
opinions: state.opinions,
addToBasket: state.addToBasket,
previewMode: state.previewMode,
isRWD: state.isRWD,
bgColor: state.bgColor,
textColor: state.textColor,
}),
},
),
);

View File

@@ -0,0 +1,71 @@
import { useEffect, useRef, useState } from "react";
import { BlockPicker } from "react-color";
import { Box, ButtonBase, Typography } from "@mui/material";
function ColorPickerButton({ color, onChange, label = "Color", palette = [] }) {
const [open, setOpen] = useState(false);
const wrapperRef = useRef(null);
useEffect(() => {
function handleClickOutside(e) {
if (!wrapperRef.current?.contains(e.target)) {
setOpen(false);
}
}
if (open) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [open]);
return (
<Box
ref={wrapperRef}
sx={{ position: "relative", display: "inline-block" }}
>
{label && (
<Typography variant="caption" sx={{ display: "block", mb: 0.5 }}>
{label}
</Typography>
)}
<ButtonBase
onClick={() => setOpen((v) => !v)}
sx={{
width: "100%",
height: 32,
borderRadius: 1,
border: "1px solid",
borderColor: "divider",
backgroundColor: color,
}}
/>
{open && (
<Box
className="color-picker block-picker"
sx={{
position: "absolute",
top: "100%",
left: "50%",
mt: 1,
zIndex: 1300,
transform: "translateX(-50%)",
}}
>
<BlockPicker
color={color}
colors={palette}
onChange={(c) => onChange(c.hex)}
/>
</Box>
)}
</Box>
);
}
export default ColorPickerButton;