punkty rwd
This commit is contained in:
@@ -17,6 +17,7 @@ Użyte biblioteki
|
||||
2. React Router
|
||||
3. zustand
|
||||
4. react shadow
|
||||
5. react-hot-toast
|
||||
|
||||
Dodać contentEditable
|
||||
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
|
||||
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
|
||||
za duże plusy - kwestia czcionki apliakcji pewnie
|
||||
Trzy położenia punktów(+ wycentrowanie położenia boxa produktowego)
|
||||
xd przez shadow roota stylowanie komponentów z MUI nie działa w preview
|
||||
|
||||
poprawić rerenderowanie szczególnie inputow
|
||||
|
||||
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",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-router-dom": "^7.11.0",
|
||||
"react-shadow": "^20.6.0",
|
||||
"zustand": "^5.0.9"
|
||||
@@ -2267,7 +2268,8 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
@@ -2724,6 +2726,15 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -3302,6 +3313,23 @@
|
||||
"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": {
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@mui/material": "^7.3.6",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-router-dom": "^7.11.0",
|
||||
"react-shadow": "^20.6.0",
|
||||
"zustand": "^5.0.9"
|
||||
|
||||
20
src/App.jsx
20
src/App.jsx
@@ -3,22 +3,22 @@ import Home from "./pages/Home";
|
||||
import "./styles/index.css";
|
||||
import AppLayout from "./ui/AppLayout";
|
||||
import Instruction from "./pages/Instruction";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
// <AppLayout>
|
||||
// <Home/>
|
||||
// </AppLayout>
|
||||
<>
|
||||
<Toaster />
|
||||
<Routes>
|
||||
<Route element={<AppLayout/>}>
|
||||
<Route path="/" element={<Home/>}/>
|
||||
<Route element={<AppLayout />}>
|
||||
<Route path="/" element={<Home />} />
|
||||
</Route>
|
||||
<Route element={<AppLayout showSidebar={false}/>}>
|
||||
<Route path="/instruction" element={<Instruction/>}/>
|
||||
<Route element={<AppLayout showSidebar={false} />}>
|
||||
<Route path="/instruction" element={<Instruction />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
)
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
import { BREAKPOINTS } from "./rwd";
|
||||
|
||||
// Constants for PhotoModule
|
||||
export const DIRECTIONS = ["tl", "tr", "bl", "br"]; // top-left, top-right, bottom-left, bottom-right
|
||||
export const DIRECTIONS_NAMES = {
|
||||
tl: "Góra Lewo",
|
||||
tr: "Góra Prawo",
|
||||
bl: "Dół Lewo",
|
||||
br: "Dół Prawo",
|
||||
|
||||
// top-left, top-right, bottom-left, bottom-right
|
||||
export const DIRECTIONS = {
|
||||
"t-l": "Góra Lewo",
|
||||
"t-r": "Góra Prawo",
|
||||
"b-l": "Dół Lewo",
|
||||
"b-r": "Dół Prawo",
|
||||
};
|
||||
|
||||
export const DEFAULT_POINT = {
|
||||
x: 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,
|
||||
direction: DIRECTIONS[0],
|
||||
direction: Object.keys(DIRECTIONS)[0],
|
||||
}; // Default point structure
|
||||
|
||||
export const URL_RADIO_DATA = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const BREAKPOINTS = {
|
||||
mobile: 0,
|
||||
tablet: 757,
|
||||
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 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 (
|
||||
<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
|
||||
sx={{ width: "100%", height: "100%" }}
|
||||
style={{ resize: "none" }}
|
||||
minRows={5}
|
||||
value={code}
|
||||
/>
|
||||
</GenericBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default CodeBox;
|
||||
|
||||
@@ -2,6 +2,7 @@ import styled from "@emotion/styled";
|
||||
import { Button } from "@mui/material";
|
||||
import { BREAKPOINTS } from "../../../constants/rwd";
|
||||
import { useSharedState } from "../../../store/useSharedState";
|
||||
import { capitalizeFirstLetter } from "../../../utils/capitalizeFirstLetter";
|
||||
|
||||
const StyledPreviewTabsContainer = styled("div")({
|
||||
display: "grid",
|
||||
@@ -16,14 +17,16 @@ function PreviewRWDTabs() {
|
||||
|
||||
return (
|
||||
<StyledPreviewTabsContainer>
|
||||
{currentPreviewMode === "single" ? null : Object.keys(BREAKPOINTS).map((key) => (
|
||||
{currentPreviewMode === "single"
|
||||
? null
|
||||
: Object.keys(BREAKPOINTS).map((key) => (
|
||||
<Button
|
||||
key={key}
|
||||
variant="contained"
|
||||
disabled={currentPreviewMode === key}
|
||||
onClick={() => setPreviewMode(key)}
|
||||
>
|
||||
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||
{capitalizeFirstLetter(key)}
|
||||
</Button>
|
||||
))}
|
||||
</StyledPreviewTabsContainer>
|
||||
|
||||
@@ -12,59 +12,6 @@ function GeneratePreview({ preview = true }) {
|
||||
<div className="idm_picture__module">
|
||||
{<GeneratePreviewImage preview={preview} urls={urls} />}
|
||||
{<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,22 +3,27 @@ import { useSharedState } from "../../../store/useSharedState";
|
||||
//
|
||||
|
||||
function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
||||
const { x, y, id, direction } = useSharedState(
|
||||
const { positions, id, direction } = useSharedState(
|
||||
(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) => {
|
||||
e.preventDefault();
|
||||
const container = containerRef.current;
|
||||
console.log(containerRef);
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const startX = e.clientX;
|
||||
const startY = e.clientY;
|
||||
const initialX = x;
|
||||
const initialY = y;
|
||||
const initialX = positions[previewMode].x;
|
||||
const initialY = positions[previewMode].y;
|
||||
|
||||
const onPointerMove = (moveEvent) => {
|
||||
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 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 = () => {
|
||||
@@ -40,16 +46,13 @@ function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
||||
window.addEventListener("pointerup", onPointerUp);
|
||||
};
|
||||
|
||||
if (preview)
|
||||
return (
|
||||
<div
|
||||
className="idm_picture__product"
|
||||
style={{
|
||||
"--photo-prod-point-desktop-top": `${y}%`,
|
||||
"--photo-prod-point-desktop-left": `${x}%`,
|
||||
// "--photo-prod-point-tablet-top": "50%",
|
||||
// "--photo-prod-point-tablet-left": "60%",
|
||||
// "--photo-prod-point-mobile-top": "50%",
|
||||
// "--photo-prod-point-mobile-left": "60%",
|
||||
top: `${positions[previewMode].x}%`,
|
||||
left: `${positions[previewMode].y}%`,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
@@ -65,6 +68,52 @@ function GeneratePreviewSinglePoint({ index, preview, containerRef }) {
|
||||
className="product_info"
|
||||
// id="prod_id_199974"
|
||||
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 && (
|
||||
<>
|
||||
|
||||
@@ -14,10 +14,10 @@ function GenerateStyle() {
|
||||
--photo-prod-point-size: 24px;
|
||||
|
||||
--photo-prod-box-radius: 20px;
|
||||
--photo-prod-box-radius-rb: 0px 20px 20px 20px;
|
||||
--photo-prod-box-radius-lb: 20px 0px 20px 20px;
|
||||
--photo-prod-box-radius-lu: 20px 20px 0px 20px;
|
||||
--photo-prod-box-radius-ru: 20px 20px 20px 0px;
|
||||
--photo-prod-box-radius-br: 0px 20px 20px 20px;
|
||||
--photo-prod-box-radius-bl: 20px 0px 20px 20px;
|
||||
--photo-prod-box-radius-tl: 20px 20px 0px 20px;
|
||||
--photo-prod-box-radius-tr: 20px 20px 20px 0px;
|
||||
|
||||
|
||||
--photo-prod-point-desktop-top: 0%;
|
||||
@@ -26,6 +26,14 @@ function GenerateStyle() {
|
||||
--photo-prod-point-tablet-left: 0%;
|
||||
--photo-prod-point-mobile-top: 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{
|
||||
@@ -38,14 +46,12 @@ function GenerateStyle() {
|
||||
.idm_picture__img{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.idm_picture__overlay{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* =========================
|
||||
PRODUCT POINT ( + )
|
||||
========================= */
|
||||
@@ -119,14 +125,56 @@ PULSE ANIMATION
|
||||
flex-direction: column;
|
||||
padding: var(--photo-prod-box-pad-top) var(--photo-prod-box-pad-left);
|
||||
gap: 0.5em;
|
||||
margin-left: var(--photo-prod-box-pad-left);
|
||||
margin-top: var(--photo-prod-box-pad-top);
|
||||
display: none;
|
||||
|
||||
max-width: var(--photo-prod-box-width);
|
||||
border-radius: var(--photo-prod-box-radius-rb);
|
||||
/* opacity: 0;
|
||||
transition: opacity 0.3s; */
|
||||
position: absolute;
|
||||
width: max-content;
|
||||
}
|
||||
.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{
|
||||
@@ -172,6 +220,9 @@ PULSE ANIMATION
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.idm_picture__product_point{
|
||||
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 InputField from "../../ui/InputField";
|
||||
import { DIRECTIONS, DIRECTIONS_NAMES } from "./../../constants/photo";
|
||||
import { DIRECTIONS } from "./../../constants/photo";
|
||||
import GenericBox from "../../ui/GenericBox";
|
||||
import { useSharedState } from "../../store/useSharedState";
|
||||
import PhotoPointPosition from "./PhotoPointPosition";
|
||||
|
||||
function PhotoSinglePoint({ index }) {
|
||||
const setSinglePoint = useSharedState((state) => state.setSinglePoint);
|
||||
const removeSinglePoint = useSharedState((state) => state.removeSinglePoint);
|
||||
const point = useSharedState((state) => state.points[index]);
|
||||
if (!point) return null;
|
||||
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 });
|
||||
};
|
||||
const direction = useSharedState((state) => state.points[index].direction);
|
||||
const id = useSharedState((state) => state.points[index].id);
|
||||
|
||||
return (
|
||||
<GenericBox
|
||||
variant="inner"
|
||||
sx={{ gridTemplateColumns: "repeat(2, 1fr)" }}
|
||||
title={`${index + 1}`}
|
||||
removeFn={() => removeSinglePoint(index)}
|
||||
>
|
||||
@@ -32,24 +22,10 @@ function PhotoSinglePoint({ index }) {
|
||||
type="text"
|
||||
name="id produktu"
|
||||
value={id}
|
||||
sx={{ gridColumn: "span 2" }}
|
||||
onChange={(e) => setSinglePoint(index, { id: e.target.value })}
|
||||
/>
|
||||
<InputField
|
||||
id={`point-${index}-x`}
|
||||
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" }}>
|
||||
<PhotoPointPosition index={index} />
|
||||
<FormControl fullWidth variant="outlined">
|
||||
<InputLabel id="direction-label">Kierunek</InputLabel>
|
||||
<Select
|
||||
labelId="direction-label"
|
||||
@@ -57,9 +33,9 @@ function PhotoSinglePoint({ index }) {
|
||||
label="Kierunek"
|
||||
onChange={(e) => setSinglePoint(index, { direction: e.target.value })}
|
||||
>
|
||||
{DIRECTIONS.map((dir) => (
|
||||
{Object.keys(DIRECTIONS).map((dir) => (
|
||||
<MenuItem key={dir} value={dir}>
|
||||
{DIRECTIONS_NAMES[dir]}
|
||||
{DIRECTIONS[dir]}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -1,37 +1,18 @@
|
||||
import { create } from "zustand";
|
||||
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 useSharedState = create((set, get) => ({
|
||||
//DATA
|
||||
const defaultState = {
|
||||
urls: { desktop: "", tablet: "", mobile: "", single: "" },
|
||||
urlRadioPoint: URL_RADIO_DATA[0].value,
|
||||
points: [{ ...DEFAULT_POINT }],
|
||||
photoAlt: "Zdjęcie pokazowe",
|
||||
previewMode: "single", // desktop, tablet, mobile
|
||||
};
|
||||
|
||||
export const useSharedState = create((set, get) => ({
|
||||
//DATA
|
||||
...defaultState,
|
||||
|
||||
//SETTERS/UPDATERS
|
||||
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
|
||||
addSinglePoint: (point) =>
|
||||
set((state) => ({
|
||||
@@ -62,4 +61,6 @@ export const useSharedState = create((set, get) => ({
|
||||
set((state) => ({
|
||||
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