diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2021-03-15 19:09:37 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2021-03-15 19:09:37 +0100 |
| commit | 8792e9fe1c7ab76c35f9a18d866880ba3da2c13e (patch) | |
| tree | fbdc78484f654ec344d10814cb83987c873d4360 /frontend | |
| parent | 7c15f34186622410e25ee85c01d832e48e012140 (diff) | |
move frontend site folder. add video support
Diffstat (limited to 'frontend')
| -rw-r--r-- | frontend/app/utils/index.js | 29 | ||||
| -rw-r--r-- | frontend/app/views/page/components/page.editor.js | 1 | ||||
| -rw-r--r-- | frontend/app/views/page/components/tile.form.js | 103 | ||||
| -rw-r--r-- | frontend/app/views/page/components/tile.handle.js | 52 | ||||
| -rw-r--r-- | frontend/app/views/page/cursors.css | 3 | ||||
| -rw-r--r-- | frontend/app/views/page/page.css | 3 | ||||
| -rw-r--r-- | frontend/site/actions.js (renamed from frontend/app/site/actions.js) | 4 | ||||
| -rw-r--r-- | frontend/site/app.js (renamed from frontend/app/site/app.js) | 4 | ||||
| -rw-r--r-- | frontend/site/index.js (renamed from frontend/app/site/index.js) | 4 | ||||
| -rw-r--r-- | frontend/site/site/site.actions.js (renamed from frontend/app/site/site/site.actions.js) | 2 | ||||
| -rw-r--r-- | frontend/site/site/site.reducer.js (renamed from frontend/app/site/site/site.reducer.js) | 2 | ||||
| -rw-r--r-- | frontend/site/store.js (renamed from frontend/app/site/store.js) | 2 | ||||
| -rw-r--r-- | frontend/site/types.js (renamed from frontend/app/site/types.js) | 0 | ||||
| -rw-r--r-- | frontend/site/viewer/viewer.container.js (renamed from frontend/app/site/viewer/viewer.container.js) | 4 |
14 files changed, 200 insertions, 13 deletions
diff --git a/frontend/app/utils/index.js b/frontend/app/utils/index.js index bb5e01d..ce8d75c 100644 --- a/frontend/app/utils/index.js +++ b/frontend/app/utils/index.js @@ -120,6 +120,35 @@ export const preloadImage = url => ( }) ) +export const preloadVideo = url => ( + new Promise((resolve, reject) => { + const video = document.createElement('video') + let loaded = false + const bind = () => { + video.addEventListener('loadedmetadata', onload) + video.addEventListener('error', onerror) + } + const unbind = () => { + video.removeEventListener('loadedmetadata', onload) + video.removeEventListener('error', onerror) + } + const onload = () => { + if (loaded) return + loaded = true + unbind() + resolve(video) + } + const onerror = (error) => { + if (loaded) return + loaded = true + unbind() + reject(error) + } + bind() + video.src = url + }) +) + export const cropImage = (url, crop) => { return new Promise((resolve, reject) => { let { x, y, w, h } = crop diff --git a/frontend/app/views/page/components/page.editor.js b/frontend/app/views/page/components/page.editor.js index d324874..25342d2 100644 --- a/frontend/app/views/page/components/page.editor.js +++ b/frontend/app/views/page/components/page.editor.js @@ -73,6 +73,7 @@ class PageEditor extends Component { } handleMouseDown(e, tile) { + if (e.metaKey || e.ctrlKey || e.altKey || e.button !== 0) return const bounds = this.getBoundingClientRect() const mouseX = e.pageX const mouseY = e.pageY diff --git a/frontend/app/views/page/components/tile.form.js b/frontend/app/views/page/components/tile.form.js index 3f43dd0..a9f34a7 100644 --- a/frontend/app/views/page/components/tile.form.js +++ b/frontend/app/views/page/components/tile.form.js @@ -10,12 +10,12 @@ import { TextInput, NumberInput, ColorInput, Slider, Select, LabelDescription, TextArea, Checkbox, SubmitButton, Loader } from 'app/common' -import { preloadImage } from 'app/utils' +import { preloadImage, preloadVideo } from 'app/utils' import * as tileActions from '../../tile/tile.actions' const SELECT_TYPES = [ - "image", "text", "link", "script", + "image", "text", "video", "link", "script", ].map(s => ({ name: s, label: s })) const ALIGNMENTS = [ @@ -31,6 +31,7 @@ const ALIGNMENTS = [ const REQUIRED_KEYS = { image: ['url'], + video: ['url'], text: ['content'], link: [], script: [], @@ -40,6 +41,10 @@ const IMAGE_TILE_STYLES = [ 'tile', 'cover', 'contain', 'contain no-repeat' ].map(style => ({ name: style, label: style })) +const VIDEO_STYLES = [ + 'normal', 'cover', 'contain', +].map(style => ({ name: style, label: style })) + const TEXT_FONT_FAMILIES = [ 'sans-serif', 'serif', 'fantasy', 'monospace', 'cursive', ].map(style => ({ name: style, label: style })) @@ -49,6 +54,7 @@ const TEXT_FONT_STYLES = [ ].map(style => ({ name: style, label: style })) const CURSORS = [ + { name: 'none', label: 'None', }, { name: 'hand_up', label: 'Up', }, { name: 'hand_down', label: 'Down', }, { name: 'hand_left', label: 'Left', }, @@ -80,6 +86,22 @@ const newImage = (data) => ({ ...data, }) +const newVideo = (data) => ({ + settings: { + ...newPosition(), + video_style: 'cover', + url: "", + external_link_url: "", + cursor: 'none', + muted: false, + loop: false, + autoadvance: false, + }, + type: 'video', + target_page_id: null, + ...data, +}) + const newText = (data) => ({ settings: { ...newPosition(), @@ -123,12 +145,14 @@ const newPosition = (data) => ({ width: 0, height: 0, rotation: 0, scale: 1, opacity: 1, + units: false, align: "center_center", ...data, }) const TYPE_CONSTRUCTORS = { image: newImage, + video: newVideo, text: newText, link: newLink, script: newScript, @@ -277,6 +301,24 @@ class TileForm extends Component { }) } + handleVideoChange(e) { + const { name, value } = e.target + this.handleSettingsSelect(name, value) + preloadVideo(value).then(video => { + // console.log(img) + this.props.tileActions.updateTemporaryTile({ + ...this.props.temporaryTile, + settings: { + ...this.props.temporaryTile.settings, + [name]: value, + width: video.videoWidth, + height: video.videoHeight, + x: 0, y: 0, + } + }) + }) + } + clearErrorField(name) { const { errorFields } = this.state if (errorFields.has(name)) { @@ -362,6 +404,8 @@ class TileForm extends Component { {temporaryTile.type === 'image' ? this.renderImageForm() + : temporaryTile.type === 'video' + ? this.renderVideoForm() : temporaryTile.type === 'text' ? this.renderTextForm() : temporaryTile.type === 'link' @@ -453,6 +497,61 @@ class TileForm extends Component { ) } + renderVideoForm() { + // const { isNew } = this.props + const { temporaryTile } = this.props + const { errorFields } = this.state + // console.log(temporaryTile.settings) + return ( + <div> + <div className='row imageUrl'> + <TextInput + title="" + placeholder='http://' + name="url" + required + data={temporaryTile.settings} + error={errorFields.has('url')} + onChange={this.handleVideoChange.bind(this)} + autoComplete="off" + /> + </div> + <div className='row pair'> + <Select + name='video_style' + selected={temporaryTile.settings.video_style || 'none'} + options={VIDEO_STYLES} + title='' + onChange={this.handleSettingsSelect.bind(this)} + /> + <Checkbox + label="Loop" + name="loop" + checked={temporaryTile.settings.loop} + onChange={this.handleSettingsSelect.bind(this)} + autoComplete="off" + /> + </div> + <div className='row pair'> + <Checkbox + label="Muted" + name="muted" + checked={temporaryTile.settings.muted} + onChange={this.handleSettingsSelect.bind(this)} + autoComplete="off" + /> + <Checkbox + label="Autoadvance" + name="autoadvance" + checked={temporaryTile.settings.autoadvance} + onChange={this.handleSettingsSelect.bind(this)} + autoComplete="off" + /> + </div> + </div> + ) + } + renderTextForm() { const { temporaryTile } = this.props const { errorFields } = this.state diff --git a/frontend/app/views/page/components/tile.handle.js b/frontend/app/views/page/components/tile.handle.js index 624b175..96574ff 100644 --- a/frontend/app/views/page/components/tile.handle.js +++ b/frontend/app/views/page/components/tile.handle.js @@ -44,6 +44,24 @@ const TileHandle = ({ tile, bounds, box, viewing, onMouseDown, onDoubleClick }) content = <img src={tile.settings.url} /> } break + + case 'video': + if (!tile.settings.url) { + return null + } + className += ' ' + tile.settings.align + content = ( + <video + src={tile.settings.url} + autoPlay={true} + controls={false} + loop={tile.settings.loop} + muted={tile.settings.muted} + style={generateVideoStyle(tile, bounds)} + /> + ) + break + case 'text': if (!tile.settings.content) { return null @@ -60,12 +78,14 @@ const TileHandle = ({ tile, bounds, box, viewing, onMouseDown, onDoubleClick }) style.backgroundColor = tile.settings.background_color || 'transparent' style.color = tile.settings.font_color || '#dddddd!important' break + case 'link': content = "" className += ' ' + tile.settings.align style.width = tile.settings.width ? tile.settings.width + 'px' : 'auto' style.height = tile.settings.height ? tile.settings.height + 'px' : 'auto' break + case 'script': content = "" if (viewing) { @@ -143,4 +163,36 @@ const generateTransform = (tile, box) => { return transform.join(' ') } +const generateVideoStyle = (tile, bounds) => { + console.log(bounds) + const style = { + pointerEvents: "none", + } + switch (tile.settings.video_style) { + case 'normal': + style.width = "auto" + style.height = "auto" + break + case 'cover': + if (tile.settings.width && (tile.settings.width / tile.settings.height) > (bounds.width / bounds.height)) { + style.width = Math.round((tile.settings.width / tile.settings.height) * bounds.height) + style.height = bounds.height + } else { + style.width = bounds.width + style.height = Math.round((tile.settings.height / tile.settings.width) * bounds.width) + } + break + case 'contain': + if (tile.settings.width && (tile.settings.width / tile.settings.height) > (bounds.width / bounds.height)) { + style.width = bounds.width + style.height = Math.round((tile.settings.height / tile.settings.width) * bounds.width) + } else { + style.width = Math.round((tile.settings.width / tile.settings.height) * bounds.height) + style.height = bounds.height + } + break + } + return style +} + export default TileHandle diff --git a/frontend/app/views/page/cursors.css b/frontend/app/views/page/cursors.css index 56fb088..778b614 100644 --- a/frontend/app/views/page/cursors.css +++ b/frontend/app/views/page/cursors.css @@ -13,6 +13,9 @@ .tile.hand_left { cursor: url(/static/img/hand_left.png) 10 60, pointer; } +.tile.none { + cursor: default; +} .tile.link { cursor: pointer; diff --git a/frontend/app/views/page/page.css b/frontend/app/views/page/page.css index 4559543..2014289 100644 --- a/frontend/app/views/page/page.css +++ b/frontend/app/views/page/page.css @@ -15,6 +15,9 @@ .tile.image { display: block; } +.tile.video { + display: block; +} .tile.image.is_tiled { width: 100%; height: 100%; diff --git a/frontend/app/site/actions.js b/frontend/site/actions.js index e672028..dea882c 100644 --- a/frontend/app/site/actions.js +++ b/frontend/site/actions.js @@ -1,9 +1,9 @@ import { bindActionCreators } from 'redux' // import { actions as crudActions } from './api' -import * as siteActions from './site/site.actions' +import * as siteActions from 'site/site/site.actions' -import { store } from './store' +import { store } from 'site/store' export default // Object.keys(crudActions) diff --git a/frontend/app/site/app.js b/frontend/site/app.js index 389e5b5..4bb352b 100644 --- a/frontend/app/site/app.js +++ b/frontend/site/app.js @@ -2,8 +2,8 @@ import React, { Component } from 'react' import { ConnectedRouter } from 'connected-react-router' import { Route } from 'react-router' -import ViewerContainer from './viewer/viewer.container' -import actions from './actions' +import ViewerContainer from 'site/viewer/viewer.container' +import actions from 'site/actions' export default class App extends Component { componentDidMount() { diff --git a/frontend/app/site/index.js b/frontend/site/index.js index 6f1a0a5..36eeae8 100644 --- a/frontend/app/site/index.js +++ b/frontend/site/index.js @@ -2,9 +2,9 @@ import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' -import App from './app' +import App from 'site/app' -import { store, history } from './store' +import { store, history } from 'site/store' const container = document.createElement('div') container.classList.add('container') diff --git a/frontend/app/site/site/site.actions.js b/frontend/site/site/site.actions.js index 79e4573..3547ec0 100644 --- a/frontend/app/site/site/site.actions.js +++ b/frontend/site/site/site.actions.js @@ -1,4 +1,4 @@ -import * as types from '../types' +import * as types from 'site/types' import { api } from 'app/utils' export const setSiteTitle = title => dispatch => { diff --git a/frontend/app/site/site/site.reducer.js b/frontend/site/site/site.reducer.js index 85c3486..53fa555 100644 --- a/frontend/app/site/site/site.reducer.js +++ b/frontend/site/site/site.reducer.js @@ -1,4 +1,4 @@ -import * as types from '../types' +import * as types from 'site/types' const initialState = { siteTitle: 'swimmer', diff --git a/frontend/app/site/store.js b/frontend/site/store.js index a228e2b..6511613 100644 --- a/frontend/app/site/store.js +++ b/frontend/site/store.js @@ -3,7 +3,7 @@ import { connectRouter, routerMiddleware } from 'connected-react-router' import { createBrowserHistory } from 'history' import thunk from 'redux-thunk' -import siteReducer from './site/site.reducer' +import siteReducer from 'site/site/site.reducer' const createRootReducer = history => ( combineReducers({ diff --git a/frontend/app/site/types.js b/frontend/site/types.js index 23bed98..23bed98 100644 --- a/frontend/app/site/types.js +++ b/frontend/site/types.js diff --git a/frontend/app/site/viewer/viewer.container.js b/frontend/site/viewer/viewer.container.js index 42ce6c2..1b3d564 100644 --- a/frontend/app/site/viewer/viewer.container.js +++ b/frontend/site/viewer/viewer.container.js @@ -3,11 +3,11 @@ import { Route } from 'react-router-dom' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import actions from '../actions' +import actions from 'site/actions' import { Loader } from 'app/common/loader.component' import TileHandle from 'app/views/page/components/tile.handle' -import '../../views/page/page.css' +import 'app/views/page/page.css' class ViewerContainer extends Component { state = { |
