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 { export enum ComponentTypes {
Text = 1, Text = 1,
Image, Image,
Checkbox, QuestionAlternative,
} }
...@@ -64,6 +64,7 @@ export interface QuestionAlternative { ...@@ -64,6 +64,7 @@ export interface QuestionAlternative {
value: number value: number
question_id: number question_id: number
} }
export interface QuestionAnswer { export interface QuestionAnswer {
id: number id: number
question_id: number question_id: number
...@@ -96,6 +97,11 @@ export interface TextComponent extends Component { ...@@ -96,6 +97,11 @@ export interface TextComponent extends Component {
} }
export interface QuestionAlternativeComponent extends Component { export interface QuestionAlternativeComponent extends Component {
question_alternative_id: number data: {
font: string 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 axios from 'axios'
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { Rnd } from 'react-rnd' import { Rnd } from 'react-rnd'
import { ComponentTypes } from '../../../enum/ComponentTypes' import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks' 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 { Position, Size } from '../../../interfaces/Components'
import CheckboxComponent from './CheckboxComponent' import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay' import ImageComponentDisplay from './ImageComponentDisplay'
import { TextComponentContainer } from './styled' import { HoverContainer } from './styled'
import FormatAlignCenterIcon from '@material-ui/icons/FormatAlignCenter'
type ImageComponentProps = { type ImageComponentProps = {
component: Component 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 [hover, setHover] = useState(false)
const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y }) const [currentPos, setCurrentPos] = useState<Position>({ x: component.x, y: component.y })
const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h }) const [currentSize, setCurrentSize] = useState<Size>({ w: component.w, h: component.h })
const competitionId = useAppSelector((state) => state.editor.competition.id) const competitionId = useAppSelector((state) => state.editor.competition.id)
const slideId = useAppSelector((state) => state.editor.activeSlideId) const slideId = useAppSelector((state) => state.editor.activeSlideId)
const [shiftPressed, setShiftPressed] = useState(false)
const handleUpdatePos = (pos: Position) => { const handleUpdatePos = (pos: Position) => {
axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, { axios.put(`/api/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
x: pos.x, x: pos.x,
...@@ -31,28 +38,69 @@ const RndComponent = ({ component }: ImageComponentProps) => { ...@@ -31,28 +38,69 @@ const RndComponent = ({ component }: ImageComponentProps) => {
h: size.h, 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 = () => { const renderInnerComponent = () => {
switch (component.type_id) { switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text: case ComponentTypes.Text:
return ( return (
<TextComponentContainer <HoverContainer
hover={hover} hover={hover}
dangerouslySetInnerHTML={{ 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: case ComponentTypes.Image:
return ( return (
<ImageComponentDisplay <HoverContainer hover={hover}>
key={component.id} <img
component={component as ImageComponent} key={component.id}
width={currentSize.w} src={`/static/images/${(component as ImageComponent).data.filename}`}
height={currentSize.h} 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: default:
break break
...@@ -61,33 +109,42 @@ const RndComponent = ({ component }: ImageComponentProps) => { ...@@ -61,33 +109,42 @@ const RndComponent = ({ component }: ImageComponentProps) => {
return ( return (
<Rnd <Rnd
minWidth={50} minWidth={75 * scale}
minHeight={50} minHeight={75 * scale}
bounds="parent" bounds="parent"
onDragStop={(e, d) => { onDragStop={(e, d) => {
setCurrentPos({ x: d.x, y: d.y }) //Have to divide by scale since d is position on current screen
handleUpdatePos(d) setCurrentPos({ x: d.x / scale, y: d.y / scale })
handleUpdatePos({ x: d.x / scale, y: d.y / scale })
}} }}
lockAspectRatio={shiftPressed}
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)} onMouseLeave={() => setHover(false)}
size={{ width: currentSize.w, height: currentSize.h }} //Multiply by scale to show components correctly for current screen size
position={{ x: currentPos.x, y: currentPos.y }} size={{ width: currentSize.w * scale, height: currentSize.h * scale }}
position={{ x: currentPos.x * scale, y: currentPos.y * scale }}
onResizeStop={(e, direction, ref, delta, position) => { onResizeStop={(e, direction, ref, delta, position) => {
setCurrentSize({ handleUpdateSize({ w: currentSize.w, h: currentSize.h })
w: ref.offsetWidth, handleUpdatePos({ x: currentPos.x, y: currentPos.y })
h: ref.offsetHeight,
})
setCurrentPos(position)
handleUpdateSize({ w: ref.offsetWidth, h: ref.offsetHeight })
handleUpdatePos(position)
}} }}
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({ setCurrentSize({
w: ref.offsetWidth, w: ref.offsetWidth / scale,
h: ref.offsetHeight, 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()} {renderInnerComponent()}
</Rnd> </Rnd>
) )
......
import React from 'react' import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useAppSelector } from '../../../hooks' import { getTypes } from '../../../actions/typesAction'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import RndComponent from './RndComponent' import RndComponent from './RndComponent'
import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled' import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
...@@ -8,11 +9,33 @@ const SlideEditor: React.FC = () => { ...@@ -8,11 +9,33 @@ const SlideEditor: React.FC = () => {
(state) => (state) =>
state.editor.competition.slides.find((slide) => slide && slide.id === state.editor.activeSlideId)?.components 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 ( return (
<SlideEditorContainer> <SlideEditorContainer>
<SlideEditorContainerRatio> <SlideEditorContainerRatio>
<SlideEditorPaper> <SlideEditorPaper ref={editorPaperRef}>
{components && components.map((component) => <RndComponent key={component.id} component={component} />)} {components &&
components.map((component) => (
<RndComponent height={height} width={width} key={component.id} component={component} />
))}
</SlideEditorPaper> </SlideEditorPaper>
</SlideEditorContainerRatio> </SlideEditorContainerRatio>
</SlideEditorContainer> </SlideEditorContainer>
......
...@@ -140,11 +140,11 @@ export const DeleteTextButton = styled(Button)` ...@@ -140,11 +140,11 @@ export const DeleteTextButton = styled(Button)`
margin-bottom: 7px; margin-bottom: 7px;
` `
interface TextComponentContainerProps { interface HoverContainerProps {
hover: boolean hover: boolean
} }
export const TextComponentContainer = styled.div<TextComponentContainerProps>` export const HoverContainer = styled.div<HoverContainerProps>`
height: 100%; height: 100%;
width: 100%; width: 100%;
padding: ${(props) => (props.hover ? 0 : 1)}px; 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