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 = {}) { |
let startDragPosition = {x: 0, y: 0}; |
||||
return <span style={{color: '#999'}}>Preview</span> |
|
||||
|
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