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

Resolve "Move tiny mce to settings panel"

parent 1d40817d
No related branches found
No related tags found
1 merge request!87Resolve "Move tiny mce to settings panel"
Pipeline #41838 passed with warnings
import { Editor } from '@tinymce/tinymce-react'
import axios from 'axios'
import React, { useState } from 'react'
import { Rnd } from 'react-rnd'
import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks'
import { TextComponent } from '../../../interfaces/ApiModels'
import { Component, ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
import { Position, Size } from '../../../interfaces/Components'
import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay'
import { TextComponentContainer } from './styled'
type ImageComponentProps = {
component: TextComponent
component: Component
}
const TextComponentDisplay = ({ component }: ImageComponentProps) => {
const RndComponent = ({ component }: ImageComponentProps) => {
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)
if (component.id === 1) console.log(component)
const handleEditorChange = (e: any) => {
console.log('Content was updated:', e.target.getContent())
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
data: { ...component.data, text: e.target.getContent() },
})
}
const handleUpdatePos = (pos: Position) => {
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
// TODO: change path to /slides/${slideId}
axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
x: pos.x,
y: pos.y,
})
}
const handleUpdateSize = () => {
axios.put(`/competitions/${competitionId}/slides/${slideId}/components/${component.id}`, {
w: currentSize.w,
h: currentSize.h,
const handleUpdateSize = (size: Size) => {
// TODO: change path to /slides/${slideId}
axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
w: size.w,
h: size.h,
})
}
const renderInnerComponent = () => {
switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text:
return (
<TextComponentContainer
hover={hover}
dangerouslySetInnerHTML={{ __html: (component as TextComponent).data.text }}
/>
)
case ComponentTypes.Image:
return <ImageComponentDisplay key={component.id} component={component as ImageComponent} />
default:
break
}
}
return (
<Rnd
minWidth={50}
......@@ -43,38 +61,29 @@ const TextComponentDisplay = ({ component }: ImageComponentProps) => {
setCurrentPos({ x: d.x, y: d.y })
handleUpdatePos(d)
}}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
size={{ width: currentSize.w, height: currentSize.h }}
position={{ x: currentPos.x, y: currentPos.y }}
onResize={(e, direction, ref, delta, position) => {
onResizeStop={(e, direction, ref, delta, position) => {
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
})
setCurrentPos(position)
handleUpdateSize({ w: ref.offsetWidth, h: ref.offsetHeight })
handleUpdatePos(position)
}}
onResizeStop={handleUpdateSize}
onResize={(e, direction, ref, delta, position) =>
setCurrentSize({
w: ref.offsetWidth,
h: ref.offsetHeight,
})
}
>
<div style={{ height: '100%', width: '100%' }}>
<Editor
initialValue={component.data.text}
init={{
height: '100%',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount',
],
toolbar:
'undo redo | formatselect | fontselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help',
}}
onChange={handleEditorChange}
/>
</div>
{renderInnerComponent()}
</Rnd>
)
}
export default TextComponentDisplay
export default RndComponent
import React from 'react'
import { ComponentTypes } from '../../../enum/ComponentTypes'
import { useAppSelector } from '../../../hooks'
import { ImageComponent, TextComponent } from '../../../interfaces/ApiModels'
import CheckboxComponent from './CheckboxComponent'
import ImageComponentDisplay from './ImageComponentDisplay'
import RndComponent from './RndComponent'
import { SlideEditorContainer, SlideEditorContainerRatio, SlideEditorPaper } from './styled'
import TextComponentDisplay from './TextComponentDisplay'
const SlideEditor: React.FC = () => {
const components = useAppSelector(
......@@ -16,19 +12,7 @@ const SlideEditor: React.FC = () => {
<SlideEditorContainer>
<SlideEditorContainerRatio>
<SlideEditorPaper>
{components &&
components.map((component) => {
switch (component.type_id) {
case ComponentTypes.Checkbox:
return <CheckboxComponent key={component.id} component={component} />
case ComponentTypes.Text:
return <TextComponentDisplay key={component.id} component={component as TextComponent} />
case ComponentTypes.Image:
return <ImageComponentDisplay key={component.id} component={component as ImageComponent} />
default:
break
}
})}
{components && components.map((component) => <RndComponent key={component.id} component={component} />)}
</SlideEditorPaper>
</SlideEditorContainerRatio>
</SlideEditorContainer>
......
......@@ -6,6 +6,7 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
Divider,
FormControl,
InputLabel,
List,
......@@ -26,7 +27,8 @@ import { useParams } from 'react-router-dom'
import { getEditorCompetition } from '../../../actions/editor'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import { QuestionAlternative, TextComponent } from '../../../interfaces/ApiModels'
import { HiddenInput } from './styled'
import { HiddenInput, TextCard } from './styled'
import TextComponentEdit from './TextComponentEdit'
const useStyles = makeStyles((theme: Theme) =>
createStyles({
......@@ -234,9 +236,15 @@ const SlideSettings: React.FC = () => {
}
const handleAddText = async () => {
console.log('Add text component')
// TODO: post the new text]
// setTexts([...texts, { id: 'newText', name: 'New Text' }])
if (activeSlide) {
await axios.post(`/competitions/${id}/slides/${activeSlide?.order}/components`, {
type_id: 1,
data: { text: 'Ny text' },
w: 315,
h: 50,
})
dispatch(getEditorCompetition(id))
}
}
const GreenCheckbox = withStyles({
......@@ -338,9 +346,8 @@ const SlideSettings: React.FC = () => {
helperText="Lämna blank för att inte använda timerfunktionen"
label="Timer"
type="number"
defaultValue={activeSlide?.timer || 0}
onChange={updateTimer}
value={timer}
value={timer || ''}
/>
</ListItem>
......@@ -383,13 +390,13 @@ const SlideSettings: React.FC = () => {
</ListItem>
{texts &&
texts.map((text) => (
<div key={text.id}>
<ListItem divider>
<TextField className={classes.textInput} label={text.data.text} variant="outlined" />
<CloseIcon className={classes.clickableIcon} />
</ListItem>
</div>
<TextCard elevation={4} key={text.id}>
<TextComponentEdit component={text} />
<Divider />
</TextCard>
))}
<ListItem className={classes.center} button onClick={handleAddText}>
<Typography className={classes.addButtons} variant="button">
Lägg till text
......
import { Editor } from '@tinymce/tinymce-react'
import { mount } from 'enzyme'
import React from 'react'
import { Provider } from 'react-redux'
import store from '../../../store'
import TextComponentDisplay from './TextComponentDisplay'
it('renders text component display', () => {
const testText = 'TEST'
const container = mount(
<Provider store={store}>
<TextComponentDisplay
component={{ id: 0, x: 0, y: 0, w: 0, h: 0, data: { text: testText, font: '123123' }, type_id: 2 }}
/>
</Provider>
)
expect(container.find(Editor).prop('initialValue')).toBe(testText)
})
import { Editor } from '@tinymce/tinymce-react'
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { getEditorCompetition } from '../../../actions/editor'
import { useAppDispatch, useAppSelector } from '../../../hooks'
import { TextComponent } from '../../../interfaces/ApiModels'
import { DeleteTextButton } from './styled'
type ImageComponentProps = {
component: TextComponent
}
interface CompetitionParams {
id: string
}
const TextComponentEdit = ({ component }: ImageComponentProps) => {
const { id }: CompetitionParams = useParams()
const competitionId = useAppSelector((state) => state.editor.competition.id)
const [content, setContent] = useState('')
const [timerHandle, setTimerHandle] = React.useState<number | undefined>(undefined)
const dispatch = useAppDispatch()
useEffect(() => {
setContent(component.data.text)
}, [])
const handleSaveText = async (a: string) => {
setContent(a)
if (timerHandle) {
clearTimeout(timerHandle)
setTimerHandle(undefined)
}
//Only updates 250ms after last input was made to not spam
setTimerHandle(
window.setTimeout(async () => {
console.log('Content was updated on server. id: ', component.id)
await axios.put(`/competitions/${competitionId}/slides/0/components/${component.id}`, {
data: { ...component.data, text: a },
})
dispatch(getEditorCompetition(id))
}, 250)
)
}
const handleDeleteText = async (componentId: number) => {
await axios.delete(`/competitions/${id}/slides/0/components/${componentId}`)
dispatch(getEditorCompetition(id))
}
return (
<div style={{ minHeight: '300px', height: '100%', width: '100%' }}>
<Editor
value={content || ''}
init={{
height: '300px',
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste code help wordcount',
],
toolbar:
'undo redo save | fontselect | formatselect | bold italic backcolor | \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | help',
}}
onEditorChange={(a, e) => handleSaveText(a)}
/>
<DeleteTextButton variant="contained" color="secondary" onClick={() => handleDeleteText(component.id)}>
Ta bort
</DeleteTextButton>
</div>
)
}
export default TextComponentEdit
import { Tab } from '@material-ui/core'
import { Button, Card, Tab } from '@material-ui/core'
import styled from 'styled-components'
export const SettingsTab = styled(Tab)`
......@@ -44,3 +44,23 @@ export const ToolbarPadding = styled.div`
height: 0;
padding-top: 55px;
`
export const TextCard = styled(Card)`
margin-bottom: 15px;
margin-top: 10px;
`
export const DeleteTextButton = styled(Button)`
width: 100%;
margin-bottom: 7px;
`
interface TextComponentContainerProps {
hover: boolean
}
export const TextComponentContainer = styled.div<TextComponentContainerProps>`
height: 100%;
width: 100%;
border: solid ${(props) => (props.hover ? 1 : 0)}px;
`
......@@ -67,7 +67,7 @@ const PresenterViewPage: React.FC = () => {
socket_connect()
socketSetSlide // Behövs denna?
setTimeout(startCompetition, 500) // Ghetto, wait for everything to load
console.log(id)
// console.log(id)
}, [])
const handleOpenPopover = (event: React.MouseEvent<HTMLButtonElement>) => {
......
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