punkty rwd
This commit is contained in:
@@ -17,6 +17,7 @@ Użyte biblioteki
|
|||||||
2. React Router
|
2. React Router
|
||||||
3. zustand
|
3. zustand
|
||||||
4. react shadow
|
4. react shadow
|
||||||
|
5. react-hot-toast
|
||||||
|
|
||||||
Dodać contentEditable
|
Dodać contentEditable
|
||||||
przepisać działanie tej listy. Może przygotować gotowy komponent od listy w zustand
|
przepisać działanie tej listy. Może przygotować gotowy komponent od listy w zustand
|
||||||
@@ -29,10 +30,8 @@ Jak zrobić Preview - CodeBox
|
|||||||
2. Do Preview powinno się dać dodać jakieś funkcje pozwalające zmieniać zustand
|
2. Do Preview powinno się dać dodać jakieś funkcje pozwalające zmieniać zustand
|
||||||
3. zmiany w codebox powinny też pozwalać na zmianę zustand'a(może lepiej najpierw generować kod w codebox a później wklejać go do preview? tylko co później z podpinaniem funkcji do np punktów żeby dało się je przesuwać myszką? i pobieranie i używanie danych z GraphQL w Preview?)
|
3. zmiany w codebox powinny też pozwalać na zmianę zustand'a(może lepiej najpierw generować kod w codebox a później wklejać go do preview? tylko co później z podpinaniem funkcji do np punktów żeby dało się je przesuwać myszką? i pobieranie i używanie danych z GraphQL w Preview?)
|
||||||
|
|
||||||
Trzy położenia punktów
|
Trzy położenia punktów(+ wycentrowanie położenia boxa produktowego)
|
||||||
za duże plusy - kwestia czcionki apliakcji pewnie
|
|
||||||
xd przez shadow roota stylowanie komponentów z MUI nie działa w preview
|
xd przez shadow roota stylowanie komponentów z MUI nie działa w preview
|
||||||
|
|
||||||
poprawić rerenderowanie szczególnie inputow
|
poprawić rerenderowanie szczególnie inputow
|
||||||
|
|
||||||
problem rem em w shadow root
|
problem rem em w shadow root
|
||||||
|
|||||||
30
package-lock.json
generated
30
package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@mui/material": "^7.3.6",
|
"@mui/material": "^7.3.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-router-dom": "^7.11.0",
|
"react-router-dom": "^7.11.0",
|
||||||
"react-shadow": "^20.6.0",
|
"react-shadow": "^20.6.0",
|
||||||
"zustand": "^5.0.9"
|
"zustand": "^5.0.9"
|
||||||
@@ -2267,7 +2268,8 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
@@ -2724,6 +2726,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/goober": {
|
||||||
|
"version": "2.1.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz",
|
||||||
|
"integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"csstype": "^3.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
@@ -3302,6 +3313,23 @@
|
|||||||
"react": "^19.2.3"
|
"react": "^19.2.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hot-toast": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"goober": "^2.1.16"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16",
|
||||||
|
"react-dom": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "19.2.3",
|
"version": "19.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"@mui/material": "^7.3.6",
|
"@mui/material": "^7.3.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-router-dom": "^7.11.0",
|
"react-router-dom": "^7.11.0",
|
||||||
"react-shadow": "^20.6.0",
|
"react-shadow": "^20.6.0",
|
||||||
"zustand": "^5.0.9"
|
"zustand": "^5.0.9"
|
||||||
|
|||||||
12
src/App.jsx
12
src/App.jsx
@@ -3,13 +3,12 @@ import Home from "./pages/Home";
|
|||||||
import "./styles/index.css";
|
import "./styles/index.css";
|
||||||
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";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// <AppLayout>
|
<>
|
||||||
// <Home/>
|
<Toaster />
|
||||||
// </AppLayout>
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<AppLayout />}>
|
<Route element={<AppLayout />}>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
@@ -18,7 +17,8 @@ function App() {
|
|||||||
<Route path="/instruction" element={<Instruction />} />
|
<Route path="/instruction" element={<Instruction />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
|||||||
@@ -1,17 +1,29 @@
|
|||||||
|
import { BREAKPOINTS } from "./rwd";
|
||||||
|
|
||||||
// Constants for PhotoModule
|
// Constants for PhotoModule
|
||||||
export const DIRECTIONS = ["tl", "tr", "bl", "br"]; // top-left, top-right, bottom-left, bottom-right
|
|
||||||
export const DIRECTIONS_NAMES = {
|
// top-left, top-right, bottom-left, bottom-right
|
||||||
tl: "Góra Lewo",
|
export const DIRECTIONS = {
|
||||||
tr: "Góra Prawo",
|
"t-l": "Góra Lewo",
|
||||||
bl: "Dół Lewo",
|
"t-r": "Góra Prawo",
|
||||||
br: "Dół Prawo",
|
"b-l": "Dół Lewo",
|
||||||
|
"b-r": "Dół Prawo",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_POINT = {
|
export const DEFAULT_POINT = {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
|
||||||
|
positions: {
|
||||||
|
single: { x: 0, y: 0 },
|
||||||
|
// ...Object.BREAKPOINTS
|
||||||
|
desktop: { x: 0, y: 0 },
|
||||||
|
tablet: { x: 0, y: 0 },
|
||||||
|
mobile: { x: 0, y: 0 },
|
||||||
|
},
|
||||||
|
|
||||||
id: 0,
|
id: 0,
|
||||||
direction: DIRECTIONS[0],
|
direction: Object.keys(DIRECTIONS)[0],
|
||||||
}; // Default point structure
|
}; // Default point structure
|
||||||
|
|
||||||
export const URL_RADIO_DATA = [
|
export const URL_RADIO_DATA = [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const BREAKPOINTS = {
|
export const BREAKPOINTS = {
|
||||||
mobile: 0,
|
|
||||||
tablet: 757,
|
|
||||||
desktop: 979,
|
desktop: 979,
|
||||||
|
tablet: 757,
|
||||||
|
mobile: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,62 @@
|
|||||||
import { TextareaAutosize } from "@mui/material";
|
import { Button, TextareaAutosize } from "@mui/material";
|
||||||
import GenericBox from "../../../ui/GenericBox";
|
import GenericBox from "../../../ui/GenericBox";
|
||||||
|
import GeneratePreview from "../generated/GeneratePreview";
|
||||||
|
import { useSharedState } from "../../../store/useSharedState";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
|
export default function CodeBox() {
|
||||||
|
const state = useSharedState();
|
||||||
|
const hiddenRef = useRef(null);
|
||||||
|
const [code, setCode] = useState("");
|
||||||
|
|
||||||
|
// Whenever state changes, update the string
|
||||||
|
useEffect(() => {
|
||||||
|
if (hiddenRef.current) {
|
||||||
|
setCode(hiddenRef.current.innerHTML);
|
||||||
|
}
|
||||||
|
}, [state.urls, state.urlRadioPoint, state.points, state.photoAlt]); // reactive slices
|
||||||
|
|
||||||
|
const copyCode = () => {
|
||||||
|
navigator.clipboard.writeText("code");
|
||||||
|
toast.success("Skopiowano tekst!");
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCode = () => {
|
||||||
|
state.clearAll();
|
||||||
|
toast.success("Wyczyszczono kod");
|
||||||
|
};
|
||||||
|
|
||||||
function CodeBox() {
|
|
||||||
return (
|
return (
|
||||||
<GenericBox variant="outer" title="Twój Kod" canCollapse={false}>
|
<GenericBox variant="outer" title="Twój Kod" canCollapse={false}>
|
||||||
|
{/* Hidden live component */}
|
||||||
|
<div style={{ display: "none" }} ref={hiddenRef}>
|
||||||
|
<GeneratePreview
|
||||||
|
preview={false}
|
||||||
|
urls={state.urls}
|
||||||
|
urlRadioPoint={state.urlRadioPoint}
|
||||||
|
points={state.points}
|
||||||
|
photoAlt={state.photoAlt}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: "flex", gap: "1rem" }}>
|
||||||
|
<Button variant="contained" onClick={copyCode}>
|
||||||
|
Kopiuj
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" onClick={clearCode}>
|
||||||
|
Wyczyść
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained">Wczytaj kod</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Textarea showing the HTML */}
|
||||||
<TextareaAutosize
|
<TextareaAutosize
|
||||||
sx={{ width: "100%", height: "100%" }}
|
sx={{ width: "100%", height: "100%" }}
|
||||||
style={{ resize: "none" }}
|
style={{ resize: "none" }}
|
||||||
minRows={5}
|
minRows={5}
|
||||||
|
value={code}
|
||||||
/>
|
/>
|
||||||
</GenericBox>
|
</GenericBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CodeBox;
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import styled from "@emotion/styled";
|
|||||||
import { Button } from "@mui/material";
|
import { Button } 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";
|
||||||
|
|
||||||
const StyledPreviewTabsContainer = styled("div")({
|
const StyledPreviewTabsContainer = styled("div")({
|
||||||
display: "grid",
|
display: "grid",
|
||||||
@@ -16,14 +17,16 @@ function PreviewRWDTabs() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledPreviewTabsContainer>
|
<StyledPreviewTabsContainer>
|
||||||
{currentPreviewMode === "single" ? null : Object.keys(BREAKPOINTS).map((key) => (
|
{currentPreviewMode === "single"
|
||||||
|
? null
|
||||||
|
: Object.keys(BREAKPOINTS).map((key) => (
|
||||||
<Button
|
<Button
|
||||||
key={key}
|
key={key}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disabled={currentPreviewMode === key}
|
disabled={currentPreviewMode === key}
|
||||||
onClick={() => setPreviewMode(key)}
|
onClick={() => setPreviewMode(key)}
|
||||||
>
|
>
|
||||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
{capitalizeFirstLetter(key)}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
</StyledPreviewTabsContainer>
|
</StyledPreviewTabsContainer>
|
||||||
|
|||||||
@@ -12,59 +12,6 @@ function GeneratePreview({ preview = true }) {
|
|||||||
<div className="idm_picture__module">
|
<div className="idm_picture__module">
|
||||||
{<GeneratePreviewImage preview={preview} urls={urls} />}
|
{<GeneratePreviewImage preview={preview} urls={urls} />}
|
||||||
{<GeneratePreviewPoints preview={preview} />}
|
{<GeneratePreviewPoints preview={preview} />}
|
||||||
{/*
|
|
||||||
<div className="idm_picture__overlay">
|
|
||||||
<div
|
|
||||||
className="idm_picture__product"
|
|
||||||
style={{
|
|
||||||
"--photo-prod-point-desktop-top": "50%",
|
|
||||||
"--photo-prod-point-desktop-left": "60%",
|
|
||||||
"--photo-prod-point-tablet-top": "50%",
|
|
||||||
"--photo-prod-point-tablet-left": "60%",
|
|
||||||
"--photo-prod-point-mobile-top": "50%",
|
|
||||||
"--photo-prod-point-mobile-left": "60%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="idm_picture__product_point"
|
|
||||||
aria-describedby="prod_id_199974"
|
|
||||||
aria-label="Product Info"
|
|
||||||
tabIndex="-1"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
className="product_info"
|
|
||||||
id="prod_id_199974"
|
|
||||||
data-id="199974"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="idm_picture__product"
|
|
||||||
style={{
|
|
||||||
"--photo-prod-point-desktop-top": "30%",
|
|
||||||
"--photo-prod-point-desktop-left": "20%",
|
|
||||||
"--photo-prod-point-tablet-top": "30%",
|
|
||||||
"--photo-prod-point-tablet-left": "20%",
|
|
||||||
"--photo-prod-point-mobile-top": "30%",
|
|
||||||
"--photo-prod-point-mobile-left": "20%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className="idm_picture__product_point"
|
|
||||||
aria-describedby="prod_id_196974"
|
|
||||||
aria-label="Product Info"
|
|
||||||
tabIndex="-1"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
className="product_info"
|
|
||||||
id="prod_id_196974"
|
|
||||||
data-id="196974"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,27 @@ import { useSharedState } from "../../../store/useSharedState";
|
|||||||
//
|
//
|
||||||
|
|
||||||
function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
||||||
const { x, y, id, direction } = useSharedState(
|
const { positions, id, direction } = useSharedState(
|
||||||
(state) => state.points[index]
|
(state) => state.points[index]
|
||||||
);
|
);
|
||||||
const setPoint = useSharedState((state) => state.setSinglePoint); // you need a setter in your store
|
const previewMode = useSharedState((state) => state.previewMode);
|
||||||
|
|
||||||
|
const setSinglePointPosition = useSharedState(
|
||||||
|
(state) => state.setSinglePointPosition
|
||||||
|
); // you need a setter in your store
|
||||||
|
|
||||||
|
const [directionY, directionX] = direction.split("-");
|
||||||
|
|
||||||
const onPointerDown = (e) => {
|
const onPointerDown = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
console.log(containerRef);
|
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
const rect = container.getBoundingClientRect();
|
const rect = container.getBoundingClientRect();
|
||||||
const startX = e.clientX;
|
const startX = e.clientX;
|
||||||
const startY = e.clientY;
|
const startY = e.clientY;
|
||||||
const initialX = x;
|
const initialX = positions[previewMode].x;
|
||||||
const initialY = y;
|
const initialY = positions[previewMode].y;
|
||||||
|
|
||||||
const onPointerMove = (moveEvent) => {
|
const onPointerMove = (moveEvent) => {
|
||||||
const deltaX = ((moveEvent.clientX - startX) / rect.width) * 100;
|
const deltaX = ((moveEvent.clientX - startX) / rect.width) * 100;
|
||||||
@@ -28,7 +33,8 @@ function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
|||||||
const newX = Math.min(100, Math.max(0, initialX + deltaX));
|
const newX = Math.min(100, Math.max(0, initialX + deltaX));
|
||||||
const newY = Math.min(100, Math.max(0, initialY + deltaY));
|
const newY = Math.min(100, Math.max(0, initialY + deltaY));
|
||||||
|
|
||||||
setPoint(index, { x: newX, y: newY });
|
setSinglePointPosition(index, previewMode, "x", newX);
|
||||||
|
setSinglePointPosition(index, previewMode, "y", newY);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPointerUp = () => {
|
const onPointerUp = () => {
|
||||||
@@ -40,16 +46,13 @@ function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
|||||||
window.addEventListener("pointerup", onPointerUp);
|
window.addEventListener("pointerup", onPointerUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (preview)
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="idm_picture__product"
|
className="idm_picture__product"
|
||||||
style={{
|
style={{
|
||||||
"--photo-prod-point-desktop-top": `${y}%`,
|
top: `${positions[previewMode].x}%`,
|
||||||
"--photo-prod-point-desktop-left": `${x}%`,
|
left: `${positions[previewMode].y}%`,
|
||||||
// "--photo-prod-point-tablet-top": "50%",
|
|
||||||
// "--photo-prod-point-tablet-left": "60%",
|
|
||||||
// "--photo-prod-point-mobile-top": "50%",
|
|
||||||
// "--photo-prod-point-mobile-left": "60%",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -65,6 +68,52 @@ function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
|||||||
className="product_info"
|
className="product_info"
|
||||||
// id="prod_id_199974"
|
// id="prod_id_199974"
|
||||||
data-id={id}
|
data-id={id}
|
||||||
|
data-direction-x={directionX}
|
||||||
|
data-direction-y={directionY}
|
||||||
|
>
|
||||||
|
{preview && (
|
||||||
|
<>
|
||||||
|
<a className="product_name" href="#">
|
||||||
|
Produkt {index + 1}
|
||||||
|
</a>
|
||||||
|
<span className="product_price">XX,XX zł</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="idm_picture__product"
|
||||||
|
style={
|
||||||
|
previewMode === "single"
|
||||||
|
? { top: positions.single.y, left: positions.single.y }
|
||||||
|
: {
|
||||||
|
"--photo-prod-point-desktop-top": `${positions.desktop.y}%`,
|
||||||
|
"--photo-prod-point-desktop-left": `${positions.desktop.x}%`,
|
||||||
|
"--photo-prod-point-tablet-top": `${positions.tablet.y}%`,
|
||||||
|
"--photo-prod-point-tablet-left": `${positions.tablet.x}%`,
|
||||||
|
"--photo-prod-point-mobile-top": `${positions.mobile.y}%`,
|
||||||
|
"--photo-prod-point-mobile-left": `${positions.mobile.x}%`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="idm_picture__product_point"
|
||||||
|
// aria-describedby="prod_id_199974"
|
||||||
|
aria-label="Product Info"
|
||||||
|
tabIndex="-1"
|
||||||
|
onPointerDown={onPointerDown}
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className="product_info"
|
||||||
|
// id="prod_id_199974"
|
||||||
|
data-id={id}
|
||||||
|
data-direction-x={directionX}
|
||||||
|
data-direction-y={directionY}
|
||||||
>
|
>
|
||||||
{preview && (
|
{preview && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ function GenerateStyle() {
|
|||||||
--photo-prod-point-size: 24px;
|
--photo-prod-point-size: 24px;
|
||||||
|
|
||||||
--photo-prod-box-radius: 20px;
|
--photo-prod-box-radius: 20px;
|
||||||
--photo-prod-box-radius-rb: 0px 20px 20px 20px;
|
--photo-prod-box-radius-br: 0px 20px 20px 20px;
|
||||||
--photo-prod-box-radius-lb: 20px 0px 20px 20px;
|
--photo-prod-box-radius-bl: 20px 0px 20px 20px;
|
||||||
--photo-prod-box-radius-lu: 20px 20px 0px 20px;
|
--photo-prod-box-radius-tl: 20px 20px 0px 20px;
|
||||||
--photo-prod-box-radius-ru: 20px 20px 20px 0px;
|
--photo-prod-box-radius-tr: 20px 20px 20px 0px;
|
||||||
|
|
||||||
|
|
||||||
--photo-prod-point-desktop-top: 0%;
|
--photo-prod-point-desktop-top: 0%;
|
||||||
@@ -26,6 +26,14 @@ function GenerateStyle() {
|
|||||||
--photo-prod-point-tablet-left: 0%;
|
--photo-prod-point-tablet-left: 0%;
|
||||||
--photo-prod-point-mobile-top: 0%;
|
--photo-prod-point-mobile-top: 0%;
|
||||||
--photo-prod-point-mobile-left: 0%;
|
--photo-prod-point-mobile-left: 0%;
|
||||||
|
|
||||||
|
|
||||||
|
--photo-box-offset: 1em;
|
||||||
|
// --photo-box-offset-t: calc(-1 * var(--photo-box-offset));
|
||||||
|
// --photo-box-offset-b: calc(var(--photo-prod-point-size) + var(--photo-box-offset));
|
||||||
|
|
||||||
|
// --photo-box-offset-l: calc(var(--photo-prod-point-size) + var(--photo-box-offset));
|
||||||
|
// --photo-box-offset-r: calc(-1 * var(--photo-box-offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
.idm_picture__module{
|
.idm_picture__module{
|
||||||
@@ -38,14 +46,12 @@ function GenerateStyle() {
|
|||||||
.idm_picture__img{
|
.idm_picture__img{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.idm_picture__overlay{
|
.idm_picture__overlay{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* =========================
|
/* =========================
|
||||||
PRODUCT POINT ( + )
|
PRODUCT POINT ( + )
|
||||||
========================= */
|
========================= */
|
||||||
@@ -119,14 +125,56 @@ PULSE ANIMATION
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: var(--photo-prod-box-pad-top) var(--photo-prod-box-pad-left);
|
padding: var(--photo-prod-box-pad-top) var(--photo-prod-box-pad-left);
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin-left: var(--photo-prod-box-pad-left);
|
|
||||||
margin-top: var(--photo-prod-box-pad-top);
|
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
max-width: var(--photo-prod-box-width);
|
max-width: var(--photo-prod-box-width);
|
||||||
border-radius: var(--photo-prod-box-radius-rb);
|
position: absolute;
|
||||||
/* opacity: 0;
|
width: max-content;
|
||||||
transition: opacity 0.3s; */
|
}
|
||||||
|
.product_info::before{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
width: calc(100% + var(--photo-box-offset) + var(--photo-prod-point-size));
|
||||||
|
height: calc(100% + var(--photo-box-offset) + var(--photo-prod-point-size));
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="t"]{
|
||||||
|
bottom: calc(100% + var(--photo-box-offset));
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="b"]{
|
||||||
|
top: calc(100% + var(--photo-box-offset));
|
||||||
|
}
|
||||||
|
.product_info[data-direction-x="l"]{
|
||||||
|
right: calc(100% + var(--photo-box-offset));
|
||||||
|
}
|
||||||
|
.product_info[data-direction-x="r"]{
|
||||||
|
left: calc(100% + var(--photo-box-offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.product_info[data-direction-y="t"]::before{
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="b"]::before{
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
.product_info[data-direction-x="l"]::before{
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.product_info[data-direction-x="r"]::before{
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product_info[data-direction-y="t"][data-direction-x="l"]{
|
||||||
|
border-radius: var(--photo-prod-box-radius-tl);
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="t"][data-direction-x="r"]{
|
||||||
|
border-radius: var(--photo-prod-box-radius-tr);
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="b"][data-direction-x="l"]{
|
||||||
|
border-radius: var(--photo-prod-box-radius-bl);
|
||||||
|
}
|
||||||
|
.product_info[data-direction-y="b"][data-direction-x="r"]{
|
||||||
|
border-radius: var(--photo-prod-box-radius-br);
|
||||||
}
|
}
|
||||||
|
|
||||||
.product_name{
|
.product_name{
|
||||||
@@ -172,6 +220,9 @@ PULSE ANIMATION
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.idm_picture__product_point{
|
.idm_picture__product_point{
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/features/htmlBuilder/generated/useRecoverCode.js
Normal file
5
src/features/htmlBuilder/generated/useRecoverCode.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { useSharedState } from "../../../store/useSharedState";
|
||||||
|
|
||||||
|
export default function useRecoverCode(code) {
|
||||||
|
const state = useSharedState();
|
||||||
|
}
|
||||||
115
src/features/photoModule/PhotoPointPosition.jsx
Normal file
115
src/features/photoModule/PhotoPointPosition.jsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import styled from "@emotion/styled";
|
||||||
|
import { BREAKPOINTS } from "../../constants/rwd";
|
||||||
|
import { useSharedState } from "../../store/useSharedState";
|
||||||
|
import InputField from "../../ui/InputField";
|
||||||
|
import { capitalizeFirstLetter } from "../../utils/capitalizeFirstLetter";
|
||||||
|
|
||||||
|
const StyledPointsContainer = styled("div")({
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "100px 1fr 1fr",
|
||||||
|
gap: "0.5rem",
|
||||||
|
alignItems: "center",
|
||||||
|
});
|
||||||
|
|
||||||
|
function PhotoPointPosition({ index }) {
|
||||||
|
const setSinglePointPosition = useSharedState(
|
||||||
|
(state) => state.setSinglePointPosition
|
||||||
|
);
|
||||||
|
const urlRadioPoint = useSharedState((state) => state.urlRadioPoint);
|
||||||
|
const positions = useSharedState((state) => state.points[index].positions);
|
||||||
|
|
||||||
|
const handleChangeNumber = ({
|
||||||
|
e,
|
||||||
|
brName,
|
||||||
|
positionName,
|
||||||
|
min = 0,
|
||||||
|
max = 100,
|
||||||
|
}) => {
|
||||||
|
let newValue = Number(e.target.value);
|
||||||
|
|
||||||
|
if (newValue < min) newValue = min;
|
||||||
|
if (newValue > max) newValue = max;
|
||||||
|
|
||||||
|
setSinglePointPosition(index, brName, positionName, newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", gap: "1.5rem", flexDirection: "column" }}>
|
||||||
|
{urlRadioPoint === "single" ? (
|
||||||
|
<StyledPointsContainer>
|
||||||
|
<span>Położenie:</span>
|
||||||
|
<InputField
|
||||||
|
id={`point-${index}-x`}
|
||||||
|
type="number"
|
||||||
|
name="lewo-prawo"
|
||||||
|
value={positions.single.x}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleChangeNumber({
|
||||||
|
e,
|
||||||
|
brName: "single",
|
||||||
|
positionName: "x",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
id={`point-${index}-y`}
|
||||||
|
type="number"
|
||||||
|
name="góra-dół"
|
||||||
|
value={positions.single.y}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleChangeNumber({
|
||||||
|
e,
|
||||||
|
brName: "single",
|
||||||
|
positionName: "y",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledPointsContainer>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{Object.keys(BREAKPOINTS).map((brPoint) => (
|
||||||
|
<StyledPointsContainer key={brPoint}>
|
||||||
|
<span>{capitalizeFirstLetter(brPoint)}:</span>
|
||||||
|
<InputField
|
||||||
|
id={`point-${index}-x`}
|
||||||
|
type="number"
|
||||||
|
name="lewo-prawo"
|
||||||
|
value={positions[brPoint].x}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleChangeNumber({
|
||||||
|
e,
|
||||||
|
brName: brPoint,
|
||||||
|
positionName: "x",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<InputField
|
||||||
|
id={`point-${index}-y`}
|
||||||
|
type="number"
|
||||||
|
name="góra-dół"
|
||||||
|
value={positions[brPoint].y}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleChangeNumber({
|
||||||
|
e,
|
||||||
|
brName: brPoint,
|
||||||
|
positionName: "y",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledPointsContainer>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PhotoPointPosition;
|
||||||
@@ -1,29 +1,19 @@
|
|||||||
import { MenuItem, Select, FormControl, InputLabel } from "@mui/material";
|
import { MenuItem, Select, FormControl, InputLabel } from "@mui/material";
|
||||||
import InputField from "../../ui/InputField";
|
import InputField from "../../ui/InputField";
|
||||||
import { DIRECTIONS, DIRECTIONS_NAMES } from "./../../constants/photo";
|
import { DIRECTIONS } from "./../../constants/photo";
|
||||||
import GenericBox from "../../ui/GenericBox";
|
import GenericBox from "../../ui/GenericBox";
|
||||||
import { useSharedState } from "../../store/useSharedState";
|
import { useSharedState } from "../../store/useSharedState";
|
||||||
|
import PhotoPointPosition from "./PhotoPointPosition";
|
||||||
|
|
||||||
function PhotoSinglePoint({ index }) {
|
function PhotoSinglePoint({ index }) {
|
||||||
const setSinglePoint = useSharedState((state) => state.setSinglePoint);
|
const setSinglePoint = useSharedState((state) => state.setSinglePoint);
|
||||||
const removeSinglePoint = useSharedState((state) => state.removeSinglePoint);
|
const removeSinglePoint = useSharedState((state) => state.removeSinglePoint);
|
||||||
const point = useSharedState((state) => state.points[index]);
|
const direction = useSharedState((state) => state.points[index].direction);
|
||||||
if (!point) return null;
|
const id = useSharedState((state) => state.points[index].id);
|
||||||
const { x, y, direction, id } = point;
|
|
||||||
|
|
||||||
const handleChangeNumber = ({ e, name, min = 0, max = 100 }) => {
|
|
||||||
let newValue = Number(e.target.value);
|
|
||||||
|
|
||||||
if (newValue < min) newValue = min;
|
|
||||||
if (newValue > max) newValue = max;
|
|
||||||
|
|
||||||
setSinglePoint(index, { [name]: newValue });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GenericBox
|
<GenericBox
|
||||||
variant="inner"
|
variant="inner"
|
||||||
sx={{ gridTemplateColumns: "repeat(2, 1fr)" }}
|
|
||||||
title={`${index + 1}`}
|
title={`${index + 1}`}
|
||||||
removeFn={() => removeSinglePoint(index)}
|
removeFn={() => removeSinglePoint(index)}
|
||||||
>
|
>
|
||||||
@@ -32,24 +22,10 @@ function PhotoSinglePoint({ index }) {
|
|||||||
type="text"
|
type="text"
|
||||||
name="id produktu"
|
name="id produktu"
|
||||||
value={id}
|
value={id}
|
||||||
sx={{ gridColumn: "span 2" }}
|
|
||||||
onChange={(e) => setSinglePoint(index, { id: e.target.value })}
|
onChange={(e) => setSinglePoint(index, { id: e.target.value })}
|
||||||
/>
|
/>
|
||||||
<InputField
|
<PhotoPointPosition index={index} />
|
||||||
id={`point-${index}-x`}
|
<FormControl fullWidth variant="outlined">
|
||||||
type="number"
|
|
||||||
name="Położenie x"
|
|
||||||
value={x}
|
|
||||||
onChange={(e) => handleChangeNumber({ e, name: "x", min: 0, max: 100 })}
|
|
||||||
/>
|
|
||||||
<InputField
|
|
||||||
id={`point-${index}-y`}
|
|
||||||
type="number"
|
|
||||||
name="Położenie y"
|
|
||||||
value={y}
|
|
||||||
onChange={(e) => handleChangeNumber({ e, name: "y", min: 0, max: 100 })}
|
|
||||||
/>
|
|
||||||
<FormControl fullWidth variant="outlined" sx={{ gridColumn: "span 2" }}>
|
|
||||||
<InputLabel id="direction-label">Kierunek</InputLabel>
|
<InputLabel id="direction-label">Kierunek</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="direction-label"
|
labelId="direction-label"
|
||||||
@@ -57,9 +33,9 @@ function PhotoSinglePoint({ index }) {
|
|||||||
label="Kierunek"
|
label="Kierunek"
|
||||||
onChange={(e) => setSinglePoint(index, { direction: e.target.value })}
|
onChange={(e) => setSinglePoint(index, { direction: e.target.value })}
|
||||||
>
|
>
|
||||||
{DIRECTIONS.map((dir) => (
|
{Object.keys(DIRECTIONS).map((dir) => (
|
||||||
<MenuItem key={dir} value={dir}>
|
<MenuItem key={dir} value={dir}>
|
||||||
{DIRECTIONS_NAMES[dir]}
|
{DIRECTIONS[dir]}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -1,37 +1,18 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { DEFAULT_POINT, URL_RADIO_DATA } from "./../constants/photo";
|
import { DEFAULT_POINT, URL_RADIO_DATA } from "./../constants/photo";
|
||||||
|
|
||||||
//
|
|
||||||
/*
|
|
||||||
const [urlRadio, setUrlRadio] = useState(URL_RADIO_DATA[0].value);
|
|
||||||
const [points, setPoints] = useState([
|
|
||||||
{
|
|
||||||
x: 80,
|
|
||||||
y: 80,
|
|
||||||
id: 75667,
|
|
||||||
direction: "tl",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
url: {
|
|
||||||
desktop: "",
|
|
||||||
tablet: "",
|
|
||||||
mobile: "",
|
|
||||||
single: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// export const createBox = () => ({});
|
// export const createBox = () => ({});
|
||||||
|
const defaultState = {
|
||||||
export const useSharedState = create((set, get) => ({
|
|
||||||
//DATA
|
|
||||||
urls: { desktop: "", tablet: "", mobile: "", single: "" },
|
urls: { desktop: "", tablet: "", mobile: "", single: "" },
|
||||||
urlRadioPoint: URL_RADIO_DATA[0].value,
|
urlRadioPoint: URL_RADIO_DATA[0].value,
|
||||||
points: [{ ...DEFAULT_POINT }],
|
points: [{ ...DEFAULT_POINT }],
|
||||||
photoAlt: "Zdjęcie pokazowe",
|
photoAlt: "Zdjęcie pokazowe",
|
||||||
previewMode: "single", // desktop, tablet, mobile
|
previewMode: "single", // desktop, tablet, mobile
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSharedState = create((set, get) => ({
|
||||||
|
//DATA
|
||||||
|
...defaultState,
|
||||||
|
|
||||||
//SETTERS/UPDATERS
|
//SETTERS/UPDATERS
|
||||||
setUrl: (type, url) =>
|
setUrl: (type, url) =>
|
||||||
@@ -51,6 +32,24 @@ export const useSharedState = create((set, get) => ({
|
|||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
|
|
||||||
|
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
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
|
||||||
// Add a point AND increment pointsLength
|
// Add a point AND increment pointsLength
|
||||||
addSinglePoint: (point) =>
|
addSinglePoint: (point) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
@@ -62,4 +61,6 @@ export const useSharedState = create((set, get) => ({
|
|||||||
set((state) => ({
|
set((state) => ({
|
||||||
points: state.points.filter((p, index) => index !== id),
|
points: state.points.filter((p, index) => index !== id),
|
||||||
})),
|
})),
|
||||||
|
|
||||||
|
clearAll: () => set(() => ({ ...defaultState })),
|
||||||
}));
|
}));
|
||||||
|
|||||||
4
src/utils/capitalizeFirstLetter.js
Normal file
4
src/utils/capitalizeFirstLetter.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export function capitalizeFirstLetter(str) {
|
||||||
|
if (!str) return "";
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user