10 changed files with 421 additions and 13 deletions
File diff suppressed because one or more lines are too long
@ -1,5 +1,120 @@ |
|||
import React, {useCallback, useEffect, useState} from 'react'; |
|||
import React, {createRef, useCallback, useEffect, useState} from 'react'; |
|||
import {PreviewButtonStyle, PreviewStyle} from "./design-preview.style.js"; |
|||
|
|||
export function DesignPreview(props = {}) { |
|||
return <span style={{color: '#999'}}>Preview</span> |
|||
let startDragPosition = {x: 0, y: 0}; |
|||
|
|||
export function DesignPreview({previewOption = {widthDimension: 0}}) { |
|||
const reference = createRef(); |
|||
const [active, setActive] = useState(false); |
|||
const [position, setPosition] = useState({x: getInitialXPosition(previewOption), y: 0}); |
|||
const [opacity, setOpacity] = useState(100); |
|||
|
|||
const keyDownHandler = useCallback( |
|||
function (e) { |
|||
const step = e.shiftKey ? 10 : 1; |
|||
if (e.key === 'ArrowUp') { |
|||
updatePosition({y: step * -1}, true) |
|||
} else if (e.key === 'ArrowDown') { |
|||
updatePosition({y: step}, true) |
|||
} else if (e.key === 'ArrowLeft') { |
|||
updatePosition({x: step * -1}, true) |
|||
} else if (e.key === 'ArrowRight') { |
|||
updatePosition({x: step}, true) |
|||
} |
|||
|
|||
}, [position]); |
|||
|
|||
useEffect(() => { |
|||
document.addEventListener('keydown', keyDownHandler); |
|||
|
|||
return () => { |
|||
document.removeEventListener('keydown', keyDownHandler) |
|||
} |
|||
}, [keyDownHandler]); |
|||
|
|||
if (!previewOption) { |
|||
return; |
|||
} |
|||
|
|||
const disabled = !previewOption.url; |
|||
|
|||
const classNames = []; |
|||
if (active) { |
|||
classNames.push('active'); |
|||
} |
|||
|
|||
if (disabled) { |
|||
classNames.push('disabled'); |
|||
} |
|||
|
|||
const handleOpacityChange = (e) => { |
|||
setOpacity(e.target.value); |
|||
} |
|||
|
|||
return <> |
|||
<PreviewButtonStyle onClick={() => toggle()} className={classNames.join(' ')} disabled={disabled}/> |
|||
|
|||
{active && |
|||
<> |
|||
<input type="range" value={opacity} min="0" max="100" onChange={handleOpacityChange}/> |
|||
<PreviewStyle onMouseUp={e => stop(e)} style={{opacity: opacity / 100}}> |
|||
<img src={previewOption.url} alt="" ref={reference} |
|||
onMouseDown={e => start(e)} |
|||
draggable={false} |
|||
style={{top: position.y + 'px', left: position.x + 'px'}} |
|||
/> |
|||
</PreviewStyle> |
|||
</> |
|||
} |
|||
</> |
|||
|
|||
function start(e) { |
|||
startDragPosition.x = e.clientX; |
|||
startDragPosition.y = e.clientY; |
|||
|
|||
document.body.addEventListener('mousemove', handler); |
|||
} |
|||
|
|||
function stop(e) { |
|||
const target = reference.current; |
|||
updatePosition({x: target.offsetLeft, y: target.offsetTop}); |
|||
|
|||
document.body.removeEventListener('mousemove', handler); |
|||
} |
|||
|
|||
function handler(e) { |
|||
const target = reference.current; |
|||
|
|||
if (target) { |
|||
const updatedPosition = { |
|||
x: target.offsetLeft - (startDragPosition.x - e.clientX), |
|||
y: target.offsetTop - (startDragPosition.y - e.clientY), |
|||
}; |
|||
|
|||
target.style.left = updatedPosition.x + 'px'; |
|||
target.style.top = updatedPosition.y + 'px'; |
|||
|
|||
startDragPosition.x = e.clientX; |
|||
startDragPosition.y = e.clientY; |
|||
} |
|||
} |
|||
|
|||
function toggle() { |
|||
setActive(!active); |
|||
} |
|||
|
|||
function updatePosition({x, y}, relative = false) { |
|||
x = typeof x === 'undefined' ? position.x : relative ? position.x + x : x; |
|||
y = typeof y === 'undefined' ? position.y : relative ? position.y + y : y; |
|||
|
|||
setPosition({x, y}) |
|||
} |
|||
|
|||
function getInitialXPosition(previewOption) { |
|||
if (!previewOption) { |
|||
return 0; |
|||
} |
|||
|
|||
return window.innerWidth / 2 - previewOption.widthDimension / 2; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,60 @@ |
|||
import styled from "styled-components"; |
|||
|
|||
export const PreviewButtonStyle = styled.button` |
|||
--size: 1.5rem; |
|||
cursor: pointer; |
|||
border: 0; |
|||
background-image: url("/scripts/dist/toolbar/images/icon-preview.svg"); |
|||
background-repeat: no-repeat; |
|||
background-size: calc(var(--size) - 0.15rem); |
|||
background-position: center center; |
|||
background-color: initial; |
|||
font-size: 1px; |
|||
color: rgba(0, 0, 0, 0); |
|||
line-height: 1; |
|||
display: block; |
|||
width: var(--size); |
|||
height: var(--size); |
|||
border-radius: 0.25rem; |
|||
outline: none; |
|||
|
|||
&.active { |
|||
background-image: url("/scripts/dist/toolbar/images/icon-preview-disabled.svg"); |
|||
} |
|||
|
|||
&.disabled { |
|||
opacity: 0.25; |
|||
cursor: default; |
|||
} |
|||
`;
|
|||
|
|||
export const PreviewStyle = styled.div` |
|||
position: fixed; |
|||
top: 3rem; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
|
|||
background-color: rgba(0, 0, 0, 0.1); |
|||
display: flex; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
align-items: baseline; |
|||
|
|||
img { |
|||
width: auto; |
|||
height: auto; |
|||
display: block; |
|||
cursor: move; |
|||
position: absolute; |
|||
} |
|||
`;
|
|||
|
|||
export const PreviewImageStyle = styled.div` |
|||
height: 100%; |
|||
width: 100%; |
|||
|
|||
background-repeat: no-repeat; |
|||
background-position: top; |
|||
cursor: move; |
|||
`;
|
|||
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 604 B |
@ -1 +1 @@ |
|||
{"version":3,"sourceRoot":"","sources":["page--main.scss","_buttons.scss","_overlay.scss","_page--breakpoints.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EAEA;;;ACTF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;ACtBJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AFIF;EACE;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;AAKN;EAEE;EACA;EACA;;;AGxEJ;EACE;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AH0DJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA","file":"page--main.css"} |
|||
{"version":3,"sourceRoot":"","sources":["page--main.scss","_buttons.scss","_overlay.scss","_page--breakpoints.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EAEA;;;ACTF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;ACtBJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AFIF;EACE;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAKF;EADF;IAEI;;;AAKN;EAEE;EACA;EACA;EACA;;AAGF;EACE;;;AG7EJ;EACE;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;EAEA;;AAEA;EACE;EAEA;EACA;EACA;;;AH+DJ;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA","file":"page--main.css"} |
|||
Loading…
Reference in new issue