Skip to content
Snippets Groups Projects
Commit a1965f37 authored by Albin Henriksson's avatar Albin Henriksson
Browse files

Resolve "improve editor"

parent c6387598
No related branches found
No related tags found
1 merge request!94Resolve "improve editor"
Pipeline #42328 passed with warnings
export enum ComponentTypes {
Text = 1,
Image,
Checkbox,
QuestionAlternative,
}
......@@ -64,6 +64,7 @@ export interface QuestionAlternative {
value: number
question_id: number
}
export interface QuestionAnswer {
id: number
question_id: number
......@@ -96,6 +97,11 @@ export interface TextComponent extends Component {
}
export interface QuestionAlternativeComponent extends Component {
question_alternative_id: number
font: string
data: {
question_id: number
text: string
value: number
question_alternative_id: number
font: string
}
}
import { Button, Card, IconButton, Tooltip, Typography } from '@material-ui/core'
import axios from 'axios'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { Rnd } from 'react-rnd'
import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks'
import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
import { Component, ImageComponent, QuestionAlternativeComponent, TextComponent } from '../../../interfaces/ApiModels'
import { Position, Size } from '../../../interfaces/Components'
import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay'
import { TextComponentContainer } from './styled'
import { HoverContainer } from './styled'
import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'
type ImageComponentProps = {
component: Component
width: number
height: number
}
const RndComponent = ({ component }: ImageComponentProps) => {
const RndComponent = ({ component, width, height }: ImageComponentProps) => {
//Makes scale close to 1, 800 height is approxemately for a 1920 by 1080 monitor
const scale = height / 800
const [hover, setHover] = useState(false)
const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
const competitionId = useAppSelector((state) => state.editor.competition.id)
const slideId = useAppSelector((state) => state.editor.activeSlideId)
const [shiftPressed, setShiftPressed] = useState(false)
const handleUpdatePos = (pos: Position) => {
axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
x: pos.x,
......@@ -31,28 +38,69 @@ const RndComponent = ({ component }: ImageComponentProps) => {
h: size.h,
})
}
const handleCenterHorizontal = () => {
console.log(width, currentSize.w)
const centerX = width / (2 * scale) - currentSize.w / 2
setCurrentPos({ x: centerX, y: currentPos.y })
handleUpdatePos({ x: centerX, y: currentPos.y })
}
const handleCenterVertical = () => {
console.log(height, currentSize.h)
const centerY = height / (2 * scale) - currentSize.h / 2
setCurrentPos({ x: currentPos.x, y: centerY })
handleUpdatePos({ x: currentPos.x, y: centerY })
}
useEffect(() => {
const downHandler = (ev: KeyboardEvent) => {
if (ev.key === 'Shift') setShiftPressed(true)
}
const upHandler = (ev: KeyboardEvent) => {
if (ev.key === 'Shift') setShiftPressed(false)
}
window.addEventListener('keydown', downHandler)
window.addEventListener('keyup', upHandler)
return () => {
window.removeEventListener('keydown', downHandler)
window.removeEventListener('keyup', upHandler)
}
}, [])
const renderInnerComponent = () => {
switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text:
return (
<TextComponentContainer
<HoverContainer
hover={hover}
dangerouslySetInnerHTML={{
__html: `<div style="font-size: 24px;"> ${(component as TextComponent).data.text} </div>`,
__html: `<div style="font-size: ${Math.round(24 * scale)}px;">${
(component as TextComponent).data.text
}</div>`,
}}
/>
)
case ComponentTypes.Image:
return (
<ImageComponentDisplay
key={component.id}
component={component as ImageComponent}
width={currentSize.w}
height={currentSize.h}
/>
<HoverContainer hover={hover}>
<img
key={component.id}
src={`/static/images/${(component as ImageComponent).data.filename}`}
height={currentSize.h * scale}
width={currentSize.w * scale}
draggable={false}
/>
</HoverContainer>
)
case ComponentTypes.Image:
return (
<HoverContainer hover={hover}>
<img
key={component.id}
src={`/static/images/${(component as ImageComponent).data.filename}`}
height={currentSize.h * scale}
width={currentSize.w * scale}
draggable={false}
/>
</HoverContainer>
)
default:
break
......@@ -61,33 +109,42 @@ const RndComponent = ({ component }: ImageComponentProps) => {
return (
<Rnd
minWidth={50}
minHeight={50}
minWidth={75 * scale}
minHeight={75 * scale}
bounds="parent"
onDragStop={(e, d) => {
setCurrentPos({ x: d.x, y: d.y })
handleUpdatePos(d)
//Have to divide by scale since d is position on current screen
setCurrentPos({ x: d.x / scale, y: d.y / scale })
handleUpdatePos({ x: d.x / scale, y: d.y / scale })
}}
lockAspectRatio={shiftPressed}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
size={{ width: currentSize.w, height: currentSize.h }}
position={{ x: currentPos.x, y: currentPos.y }}
//Multiply by scale to show components correctly for current screen size
size={{ width: currentSize.w * scale, height: currentSize.h * scale }}
position={{ x: currentPos.x * scale, y: currentPos.y * scale }}
onResizeStop={(e, direction, ref, delta, position) => {
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
})
setCurrentPos(position)
handleUpdateSize({ w: ref.offsetWidth, h: ref.offsetHeight })
handleUpdatePos(position)
handleUpdateSize({ w: currentSize.w, h: currentSize.h })
handleUpdatePos({ x: currentPos.x, y: currentPos.y })
}}
onResize={(e, direction, ref, delta, position) =>
onResize={(e, direction, ref, delta, position) => {
//Have to divide by scale since ref has position on current screen
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
w: ref.offsetWidth / scale,
h: ref.offsetHeight / scale,
})
}
}}
>
{hover && (
<Card elevation={6} style={{ position: 'absolute' }}>
<Tooltip title="Centrera horisontellt">
<IconButton onClick={handleCenterHorizontal}>X</IconButton>
</Tooltip>
<Tooltip title="Centrera Vertikalt">
<IconButton onClick={handleCenterVertical}>Y</IconButton>
</Tooltip>
</Card>
)}
{renderInnerComponent()}
</Rnd>
)
......
import React from 'react'
import { useAppSelector } from '../../../hooks'
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { getTypes } from '../../../actions/typesAction'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import RndComponent from './RndComponent'
import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
......@@ -8,11 +9,33 @@ const SlideEditor: React.FC = () => {
(state) =>
state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)?.components
)
const dispatch = useAppDispatch()
const editorPaperRef = useRef<HTMLDivElement>(null)
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
useEffect(() => {
dispatch(getTypes())
}, [])
useLayoutEffect(() => {
const updateScale = () => {
if (editorPaperRef.current) {
setWidth(editorPaperRef.current.clientWidth)
setHeight(editorPaperRef.current.clientHeight)
}
}
window.addEventListener('resize', updateScale)
updateScale()
return () => window.removeEventListener('resize', updateScale)
}, [])
return (
<SlideEditorContainer>
<SlideEditorContainerRatio>
<SlideEditorPaper>
{components && components.map((component) => <RndComponent key={component.id} component={component} />)}
<SlideEditorPaper ref={editorPaperRef}>
{components &&
components.map((component) => (
<RndComponent height={height} width={width} key={component.id} component={component} />
))}
</SlideEditorPaper>
</SlideEditorContainerRatio>
</SlideEditorContainer>
......
......@@ -140,11 +140,11 @@ export const DeleteTextButton = styled(Button)`
margin-bottom: 7px;
`
interface TextComponentContainerProps {
interface HoverContainerProps {
hover: boolean
}
export const TextComponentContainer = styled.div<TextComponentContainerProps>`
export const HoverContainer = styled.div<HoverContainerProps>`
height: 100%;
width: 100%;
padding: ${(props) => (props.hover ? 0 : 1)}px;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment