summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2021-03-31 17:37:40 +0200
committerJules Laplace <julescarbon@gmail.com>2021-03-31 17:37:40 +0200
commitcda9c115283be8e4e224f6036ba07e5eca243289 (patch)
treed0b150bf108813873c7b59cc9f9bd9c00ea3eba7
parenta6793f922991d326eeb33cf08b245863218eaef7 (diff)
refactor tile forms into own files. add full-width marquee support
-rw-r--r--frontend/app/common/form.component.js2
-rw-r--r--frontend/app/views/tile/components/tile.form.js795
-rw-r--r--frontend/app/views/tile/forms/index.js29
-rw-r--r--frontend/app/views/tile/forms/tile.constants.js92
-rw-r--r--frontend/app/views/tile/forms/tile.constructors.js113
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.gradient.js80
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.image.js46
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.link.js32
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.script.js22
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.text.js166
-rw-r--r--frontend/app/views/tile/forms/tile.form.element.video.js101
-rw-r--r--frontend/app/views/tile/forms/tile.form.hyperlink.js64
-rw-r--r--frontend/app/views/tile/forms/tile.form.misc.js98
-rw-r--r--frontend/app/views/tile/forms/tile.form.sound.js53
-rw-r--r--frontend/app/views/tile/forms/tile.form.type.js29
-rw-r--r--frontend/app/views/tile/handles/tile.gradient.js18
-rw-r--r--frontend/app/views/tile/handles/tile.text.js23
-rw-r--r--frontend/app/views/tile/tile.utils.js28
-rw-r--r--package.json1
-rw-r--r--yarn.lock5
20 files changed, 1021 insertions, 776 deletions
diff --git a/frontend/app/common/form.component.js b/frontend/app/common/form.component.js
index de1020a..ea4c5cb 100644
--- a/frontend/app/common/form.component.js
+++ b/frontend/app/common/form.component.js
@@ -68,7 +68,7 @@ export class NumberInput extends Component {
onKeyDown={this.handleKeyDown}
onChange={props.onChange}
name={props.name}
- value={props.data[props.name]}
+ value={props.name in props.data ? props.data[props.name] : (props.defaultValue || props.min)}
min={props.min}
max={props.max}
step={props.step || 1}
diff --git a/frontend/app/views/tile/components/tile.form.js b/frontend/app/views/tile/components/tile.form.js
index 6a65194..372cd1d 100644
--- a/frontend/app/views/tile/components/tile.form.js
+++ b/frontend/app/views/tile/components/tile.form.js
@@ -16,198 +16,36 @@ import { preloadImage, preloadVideo } from 'app/utils'
import * as pageActions from 'app/views/page/page.actions'
import * as tileActions from 'app/views/tile/tile.actions'
-const SELECT_TYPES = [
- "image", "text", "video", "link", "gradient", "script",
-].map(s => ({ name: s, label: s }))
-
-const ALIGNMENTS = [
- "top_left", "top_center", "top_right",
- "center_left", "center_center", "center_right",
- "bottom_left", "bottom_center", "bottom_right",
-].map(align => ({
- name: align,
- label: align === 'center_center'
- ? 'center'
- : align.replace('_', ' ')
- }))
-
-const REQUIRED_KEYS = {
- image: ['url'],
- video: ['url'],
- text: ['content'],
- link: [],
- gradient: [],
- script: [],
-}
-
-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 }))
-
-const TEXT_FONT_STYLES = [
- 'normal', 'bold', 'italic', 'bold-italic',
-].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', },
- { name: 'hand_right', label: 'Right', },
- { name: 'unclickable', label: 'Unclickable', },
-]
+import {
+ SELECT_TYPES, ALIGNMENTS,
+ REQUIRED_KEYS,
+ IMAGE_TILE_STYLES, VIDEO_STYLES,
+ TEXT_FONT_FAMILIES, TEXT_FONT_STYLES,
+ CURSORS, UNITS,
+ NO_LINK, EXTERNAL_LINK, OPEN_POPUP_LINK, CLOSE_POPUP_LINK,
+ PAGE_LIST_TOP_OPTIONS,
+ NO_POPUP, POPUP_LIST_TOP_OPTIONS,
+} from 'app/views/tile/forms/tile.constants'
-const UNITS = [
- { name: 'px', label: 'pixels' },
- { name: '%', label: 'percent' },
- { name: 'video', label: 'video' },
- { name: 'vmin', label: 'screen min' },
- { name: 'vmax', label: 'screen max' },
-]
+import {
+ TILE_CONSTRUCTORS,
-const NO_LINK = 0
-const EXTERNAL_LINK = -1
-const OPEN_POPUP_LINK = -2
-const CLOSE_POPUP_LINK = -3
-const PAGE_LIST_TOP_OPTIONS = [
- { name: NO_LINK, label: 'No link' },
- { name: EXTERNAL_LINK, label: 'External link' },
- { name: OPEN_POPUP_LINK, label: 'Open popup' },
- { name: CLOSE_POPUP_LINK, label: 'Close popup' },
- { name: -99, label: '──────────', disabled: true },
-]
+ TileImageForm,
+ TileLinkForm,
+ TileTextForm,
+ TileGradientForm,
+ TileScriptForm,
+ TileVideoForm,
-const NO_POPUP = 0
-const POPUP_LIST_TOP_OPTIONS = [
- { name: NO_POPUP, label: 'Select a popup group' },
- { name: -99, label: '──────────', disabled: true },
-]
+ TileHyperlinkForm,
+ TileMiscForm,
+ TileSoundForm,
+ TileTypeForm,
+} from 'app/views/tile/forms'
// target_page_id = Column(Integer, ForeignKey('page.id'), nullable=True)
// https://s3.amazonaws.com/i.asdf.us/im/1c/gradient_gold1-SpringGreen1_1321159749.jpg
-const newImage = (data) => ({
- settings: {
- ...newPosition(),
- is_tiled: false,
- tile_style: 'tile',
- url: "",
- external_link_url: "",
- cursor: 'hand_up',
- },
- type: 'image',
- target_page_id: 0,
- ...data,
-})
-
-const newVideo = (data) => ({
- settings: {
- ...newPosition(),
- video_style: 'cover',
- url: "",
- external_link_url: "",
- cursor: 'none',
- muted: false,
- loop_style: false,
- autoadvance: false,
- loop_section: false,
- loop_start: 0,
- loop_end: 0,
- },
- type: 'video',
- target_page_id: 0,
- ...data,
-})
-
-const newText = (data) => ({
- settings: {
- ...newPosition(),
- content: "",
- font_family: 'sans-serif',
- font_size: 16,
- font_style: 'normal',
- font_color: '#dddddd',
- background_color: 'transparent',
- width: 0,
- height: 0,
- units: 'px',
- external_link_url: "",
- cursor: 'hand_up',
- },
- type: 'text',
- target_page_id: 0,
- ...data,
-})
-
-const newGradient = (data) => ({
- settings: {
- ...newPosition({ width: 100, height: 100 }),
- from_color: '#ffffff',
- from_opacity: 1.0,
- to_color: '#000000',
- to_opacity: 1.0,
- angle: 0,
- stop: 50,
- units: '%',
- external_link_url: "",
- cursor: 'hand_up',
- },
- type: 'gradient',
- target_page_id: 0,
- ...data,
-})
-
-const newLink = (data) => ({
- settings: {
- ...newPosition({ width: 100, height: 100, }),
- external_link_url: "",
- cursor: 'hand_up',
- units: 'px',
- },
- type: 'link',
- target_page_id: 0,
- ...data,
-})
-
-const newScript = (data) => ({
- settings: {
- ...newPosition({ width: 100, height: 100, }),
- },
- type: 'script',
- ...data,
-})
-
-const newPosition = (data) => ({
- x: 0, y: 0,
- width: 0, height: 0,
- rotation: 0, scale: 1,
- opacity: 1,
- units: false,
- align: "center_center",
- has_audio: false,
- audio_on_click_id: 0,
- audio_on_hover_id: 0,
- navigate_when_audio_finishes: false,
- ...data,
-})
-
-const TYPE_CONSTRUCTORS = {
- image: newImage,
- video: newVideo,
- text: newText,
- link: newLink,
- gradient: newGradient,
- script: newScript,
-}
-
class TileForm extends Component {
state = {
title: "",
@@ -252,7 +90,7 @@ class TileForm extends Component {
]
this.setState({ pageList, popupList })
if (isNew) {
- const newTile = newGradient({
+ const newTile = TILE_CONSTRUCTORS.image({
id: "new",
graph_id: graph.show.res.id,
page_id: page.show.res.id,
@@ -292,7 +130,7 @@ class TileForm extends Component {
handleTypeChange(type) {
const { graph, page, temporaryTile } = this.props
- let newTile = TYPE_CONSTRUCTORS[type]({
+ let newTile = TILE_CONSTRUCTORS[type]({
id: temporaryTile.id,
graph_id: temporaryTile.graph_id,
page_id: temporaryTile.page_id,
@@ -455,42 +293,31 @@ class TileForm extends Component {
{'◁'}
</button>
<form onSubmit={this.handleSubmit}>
- <div className="row selects">
- <Select
- name='type'
- selected={temporaryTile.type}
- options={SELECT_TYPES}
- title=''
- onChange={this.handleSelect}
- />
- <Select
- name='align'
- selected={temporaryTile.settings.align}
- options={ALIGNMENTS}
- title=''
- onChange={this.handleAlignment}
- />
- </div>
+
+ <TileTypeForm tile={temporaryTile} errorFields={errorFields} parent={this} />
{this.renderPositionInfo()}
{temporaryTile.type === 'image'
- ? this.renderImageForm()
+ ? <TileImageForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: temporaryTile.type === 'video'
- ? this.renderVideoForm()
+ ? <TileVideoForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: temporaryTile.type === 'text'
- ? this.renderTextForm()
+ ? <TileTextForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: temporaryTile.type === 'link'
- ? this.renderLinkForm()
+ ? <TileLinkForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: temporaryTile.type === 'gradient'
- ? this.renderGradientForm()
+ ? <TileGradientForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: temporaryTile.type === 'script'
- ? this.renderScriptForm()
+ ? <TileScriptForm tile={temporaryTile} errorFields={errorFields} parent={this} />
: ""}
- {this.renderHyperlinkForm()}
- {this.renderMiscForm()}
- {this.renderAudioForm()}
+ <TileHyperlinkForm
+ tile={temporaryTile} errorFields={errorFields} parent={this}
+ pageList={this.state.pageList} popupList={this.state.popupList}
+ />
+ <TileMiscForm tile={temporaryTile} errorFields={errorFields} parent={this} />
+ <TileSoundForm tile={temporaryTile} errorFields={errorFields} parent={this} />
<div className='row buttons'>
<SubmitButton
@@ -529,550 +356,6 @@ class TileForm extends Component {
</div>
)
}
-
- renderImageForm() {
- // const { isNew } = this.props
- const { temporaryTile } = this.props
- const { errorFields } = this.state
- // console.log(temporaryTile.settings)
- return (
- <div>
- <div className='row imageUrl'>
- {temporaryTile.settings.url && <div className='thumb'><img src={temporaryTile.settings.url} /></div>}
- <TextInput
- title=""
- placeholder='http://'
- name="url"
- required
- data={temporaryTile.settings}
- error={errorFields.has('url')}
- onChange={this.handleImageChange}
- autoComplete="off"
- />
- </div>
- <div className='row pair'>
- <Checkbox
- label="Tiled"
- name="is_tiled"
- checked={temporaryTile.settings.is_tiled}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- {temporaryTile.settings.is_tiled &&
- <Select
- name='tile_style'
- selected={temporaryTile.settings.tile_style || 'tile'}
- options={IMAGE_TILE_STYLES}
- title=''
- onChange={this.handleSettingsSelect}
- />
- }
- </div>
- </div>
- )
- }
-
- 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}
- autoComplete="off"
- />
- </div>
- <div className='row pair with_checkbox'>
- <Select
- name='video_style'
- selected={temporaryTile.settings.video_style || 'none'}
- options={VIDEO_STYLES}
- title=''
- onChange={this.handleSettingsSelect}
- />
- <Checkbox
- label="Loop"
- name="loop"
- checked={temporaryTile.settings.loop}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- </div>
- <div className='row pair'>
- <Checkbox
- label="Muted"
- name="muted"
- className='short'
- checked={temporaryTile.settings.muted}
- onChange={this.handleSettingsSelect}
- />
- <Checkbox
- label="Autoadvance"
- name="autoadvance"
- className='short'
- checked={temporaryTile.settings.autoadvance}
- onChange={this.handleSettingsSelect}
- />
- </div>
- {!temporaryTile.settings.muted && (
- <Slider
- title='Volume'
- name='volume'
- value={('volume' in temporaryTile.settings) ? temporaryTile.settings.volume : 1.0}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={1.0}
- step={0.01}
- />
- )}
- {temporaryTile.settings.loop && (
- <div className='row'>
- <Checkbox
- label="Loop section?"
- className='short'
- name="loop_section"
- checked={temporaryTile.settings.loop_section}
- onChange={this.handleSettingsSelect}
- />
- </div>
- )}
- {temporaryTile.settings.loop && temporaryTile.settings.loop_section && (
- <div className='row pair'>
- <TextInput
- title="From"
- placeholder='0:00'
- name="loop_start"
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <TextInput
- title="To"
- placeholder='0:00'
- name="loop_end"
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- )}
- </div>
- )
- }
-
- renderTextForm() {
- const { temporaryTile } = this.props
- const { errorFields } = this.state
- return (
- <div>
- <TextArea
- title=""
- name="content"
- required
- data={temporaryTile.settings}
- error={errorFields.has('content')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <div className='row font'>
- <Select
- title="Font"
- name='font_family'
- selected={temporaryTile.settings.font_family || 'sans-serif'}
- options={TEXT_FONT_FAMILIES}
- title=''
- onChange={this.handleSettingsSelect}
- />
- <NumberInput
- title=''
- name='font_size'
- data={temporaryTile.settings}
- min={1}
- max={1200}
- error={errorFields.has('font_size')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <Select
- name='font_style'
- selected={temporaryTile.settings.font_style || 'normal'}
- options={TEXT_FONT_STYLES}
- title=''
- onChange={this.handleSettingsSelect}
- />
- </div>
- <ColorInput
- title='Text'
- name='font_color'
- data={temporaryTile.settings}
- error={errorFields.has('font_color')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <ColorInput
- title='BG'
- name='background_color'
- data={temporaryTile.settings}
- error={errorFields.has('background_color')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <div className='row pair'>
- <NumberInput
- title="Width"
- name="width"
- data={temporaryTile.settings}
- min={0}
- max={1200}
- error={errorFields.has('width')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <NumberInput
- title="Height"
- name="height"
- data={temporaryTile.settings}
- min={0}
- max={1200}
- error={errorFields.has('height')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- </div>
- )
- }
-
- renderLinkForm() {
- const { temporaryTile } = this.props
- const { errorFields } = this.state
- return (
- <div>
- <div className='row pair'>
- <NumberInput
- title="Width"
- name="width"
- data={temporaryTile.settings}
- min={0}
- max={2400}
- error={errorFields.has('width')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <NumberInput
- title="Height"
- name="height"
- data={temporaryTile.settings}
- min={0}
- max={2400}
- error={errorFields.has('height')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- </div>
- )
- }
-
- renderGradientForm() {
- const { temporaryTile } = this.props
- return (
- <div>
- <ColorInput
- title='From'
- name='from_color'
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <Slider
- title='Opacity'
- name='from_opacity'
- value={temporaryTile.settings.from_opacity}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={1.0}
- step={0.01}
- />
- <ColorInput
- title='To'
- name='to_color'
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <Slider
- title='Opacity'
- name='to_opacity'
- value={temporaryTile.settings.to_opacity}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={1.0}
- step={0.01}
- />
- <Slider
- title='Angle'
- name='angle'
- value={temporaryTile.settings.angle}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={360.0}
- step={0.1}
- />
- <Slider
- title='Stop'
- name='stop'
- value={temporaryTile.settings.stop}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={100.0}
- step={0.1}
- />
- <div className='row pair'>
- <NumberInput
- title="Width"
- name="width"
- data={temporaryTile.settings}
- min={0}
- max={2400}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <NumberInput
- title="Height"
- name="height"
- data={temporaryTile.settings}
- min={0}
- max={2400}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- </div>
- )
- }
-
- renderScriptForm() {
- const { temporaryTile } = this.props
- const { errorFields } = this.state
- return (
- <div>
- <TextArea
- title=""
- name="content"
- required
- data={temporaryTile.settings}
- error={errorFields.has('content')}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- <div>
- Scripts will be run on the live site when this page loads.
- </div>
- </div>
- )
- }
-
- renderHyperlinkForm() {
- const { temporaryTile } = this.props
- const { pageList, popupList } = this.state
- const isExternalLink = temporaryTile.target_page_id === EXTERNAL_LINK
- const isPopupLink = (
- temporaryTile.target_page_id === OPEN_POPUP_LINK ||
- temporaryTile.target_page_id === CLOSE_POPUP_LINK
- )
- return (
- <div>
- <div className={'row selects'}>
- <Select
- title=''
- name='target_page_id'
- selected={temporaryTile.target_page_id || NO_LINK}
- options={pageList}
- onChange={this.handleSelect}
- />
- <Select
- title=''
- name='cursor'
- selected={temporaryTile.settings.cursor}
- options={CURSORS}
- defaultOption="Cursor"
- onChange={this.handleSettingsSelect}
- />
- </div>
- {isExternalLink && (
- <div>
- <TextInput
- title=""
- placeholder='http://'
- name="external_link_url"
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- )}
- {(temporaryTile.target_page_id === OPEN_POPUP_LINK || temporaryTile.target_page_id === CLOSE_POPUP_LINK) && (
- <div className='row single'>
- <Select
- title="Popup"
- name='target_popup'
- selected={temporaryTile.settings.target_popup || NO_POPUP}
- options={popupList}
- onChange={this.handleSettingsSelect}
- />
- </div>
- )}
- </div>
- )
- }
-
- renderAudioForm() {
- const { temporaryTile } = this.props
- return (
- <div>
- <Checkbox
- label="Sound effects"
- name="has_audio"
- className='short'
- checked={temporaryTile.settings.has_audio}
- onChange={this.handleSettingsSelect}
- />
- {temporaryTile.settings.has_audio && (
- <div>
- <div className='row single'>
- <AudioSelect
- title="On click"
- name="audio_on_click_id"
- selected={temporaryTile.settings.audio_on_click_id}
- onChange={this.handleSettingsSelect}
- />
- </div>
-
- {!!temporaryTile.settings.audio_on_click_id && (
- <Checkbox
- label="Navigate when audio finishes"
- name="navigate_when_audio_finishes"
- className='short'
- checked={temporaryTile.settings.navigate_when_audio_finishes}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- )}
-
- <div className='row single'>
- <AudioSelect
- title="On hover"
- name="audio_on_hover_id"
- selected={temporaryTile.settings.audio_on_hover_id}
- onChange={this.handleSettingsSelect}
- />
- </div>
- </div>
- )}
- </div>
- )
- }
-
- renderMiscForm() {
- const { temporaryTile } = this.props
- return (
- <div>
- <div className='row single'>
- <Select
- name='units'
- selected={temporaryTile.settings.units || 'px'}
- options={UNITS}
- title='Units'
- onChange={this.handleSettingsSelect}
- />
- </div>
- <Slider
- title='Opacity'
- name='opacity'
- value={temporaryTile.settings.opacity}
- onChange={this.handleSettingsSelect}
- min={0.0}
- max={1.0}
- step={0.01}
- />
- <Slider
- title='Scale'
- name='scale'
- value={temporaryTile.settings.scale}
- onChange={this.handleSettingsSelect}
- min={0.01}
- max={10.0}
- step={0.01}
- />
- <Slider
- title='Rotation'
- name='rotation'
- value={temporaryTile.settings.rotation}
- onChange={this.handleSettingsSelect}
- min={-180.0}
- max={180.0}
- step={1}
- type='int'
- />
- <Checkbox
- label="Element is a Popup"
- name="is_popup"
- className='short'
- checked={temporaryTile.settings.is_popup}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- {temporaryTile.settings.is_popup && (
- <div className='row single_text'>
- <TextInput
- title="Popup group"
- name="popup_group"
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- )}
- <Checkbox
- label="Wait to appear"
- name="wait_to_appear"
- className='short'
- checked={temporaryTile.settings.wait_to_appear}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- {temporaryTile.settings.wait_to_appear && (
- <div className='row single_text'>
- <TextInput
- title="Appear after"
- name="appear_after"
- data={temporaryTile.settings}
- onChange={this.handleSettingsChange}
- autoComplete="off"
- />
- </div>
- )}
- <Checkbox
- label="Hide on click"
- name="hide_on_click"
- className='short'
- checked={temporaryTile.settings.hide_on_click}
- onChange={this.handleSettingsSelect}
- autoComplete="off"
- />
- </div>
- )
- }
}
const mapStateToProps = state => ({
diff --git a/frontend/app/views/tile/forms/index.js b/frontend/app/views/tile/forms/index.js
new file mode 100644
index 0000000..8225365
--- /dev/null
+++ b/frontend/app/views/tile/forms/index.js
@@ -0,0 +1,29 @@
+import { TILE_CONSTRUCTORS } from './tile.constructors'
+
+import TileImageForm from './tile.form.element.image'
+import TileLinkForm from './tile.form.element.link'
+import TileTextForm from './tile.form.element.text'
+import TileGradientForm from './tile.form.element.gradient'
+import TileScriptForm from './tile.form.element.script'
+import TileVideoForm from './tile.form.element.video'
+
+import TileHyperlinkForm from './tile.form.hyperlink'
+import TileMiscForm from './tile.form.misc'
+import TileSoundForm from './tile.form.sound'
+import TileTypeForm from './tile.form.type'
+
+export {
+ TILE_CONSTRUCTORS,
+
+ TileImageForm,
+ TileLinkForm,
+ TileTextForm,
+ TileGradientForm,
+ TileScriptForm,
+ TileVideoForm,
+
+ TileHyperlinkForm,
+ TileMiscForm,
+ TileSoundForm,
+ TileTypeForm,
+}
diff --git a/frontend/app/views/tile/forms/tile.constants.js b/frontend/app/views/tile/forms/tile.constants.js
new file mode 100644
index 0000000..f2dd0ad
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.constants.js
@@ -0,0 +1,92 @@
+/*
+import {
+ SELECT_TYPES, ALIGNMENTS,
+ REQUIRED_KEYS,
+ IMAGE_TILE_STYLES, VIDEO_STYLES,
+ TEXT_FONT_FAMILIES, TEXT_FONT_STYLES,
+ CURSORS, UNITS,
+ NO_LINK, EXTERNAL_LINK, OPEN_POPUP_LINK, CLOSE_POPUP_LINK,
+ PAGE_LIST_TOP_OPTIONS,
+ NO_POPUP, POPUP_LIST_TOP_OPTIONS,
+} from 'app/constants'
+*/
+
+export const SELECT_TYPES = [
+ "image", "text", "video", "link", "gradient", "script",
+].map(s => ({ name: s, label: s }))
+
+export const ALIGNMENTS = [
+ "top_left", "top_center", "top_right",
+ "center_left", "center_center", "center_right",
+ "bottom_left", "bottom_center", "bottom_right",
+].map(align => ({
+ name: align,
+ label: align === 'center_center'
+ ? 'center'
+ : align.replace('_', ' ')
+ }))
+
+export const REQUIRED_KEYS = {
+ image: ['url'],
+ video: ['url'],
+ text: ['content'],
+ link: [],
+ gradient: [],
+ script: [],
+}
+
+export const IMAGE_TILE_STYLES = [
+ 'tile', 'cover', 'contain', 'contain no-repeat'
+].map(style => ({ name: style, label: style }))
+
+export const VIDEO_STYLES = [
+ 'normal', 'cover', 'contain',
+].map(style => ({ name: style, label: style }))
+
+export const TEXT_FONT_FAMILIES = [
+ 'sans-serif', 'serif', 'fantasy', 'monospace', 'cursive',
+].map(style => ({ name: style, label: style }))
+
+export const TEXT_FONT_STYLES = [
+ 'normal', 'bold', 'italic', 'bold-italic',
+].map(style => ({ name: style, label: style }))
+
+export const CURSORS = [
+ { name: 'none', label: 'None', },
+ { name: 'hand_up', label: 'Up', },
+ { name: 'hand_down', label: 'Down', },
+ { name: 'hand_left', label: 'Left', },
+ { name: 'hand_right', label: 'Right', },
+ { name: 'unclickable', label: 'Unclickable', },
+]
+
+export const MARQUEE_DIRECTIONS = [
+ { name: 'left', label: 'Left', },
+ { name: 'right', label: 'Right', },
+]
+
+export const UNITS = [
+ { name: 'px', label: 'pixels' },
+ { name: '%', label: 'percent' },
+ { name: 'video', label: 'video' },
+ { name: 'vmin', label: 'screen min' },
+ { name: 'vmax', label: 'screen max' },
+]
+
+export const NO_LINK = 0
+export const EXTERNAL_LINK = -1
+export const OPEN_POPUP_LINK = -2
+export const CLOSE_POPUP_LINK = -3
+export const PAGE_LIST_TOP_OPTIONS = [
+ { name: NO_LINK, label: 'No link' },
+ { name: EXTERNAL_LINK, label: 'External link' },
+ { name: OPEN_POPUP_LINK, label: 'Open popup' },
+ { name: CLOSE_POPUP_LINK, label: 'Close popup' },
+ { name: -99, label: '──────────', disabled: true },
+]
+
+export const NO_POPUP = 0
+export const POPUP_LIST_TOP_OPTIONS = [
+ { name: NO_POPUP, label: 'Select a popup group' },
+ { name: -99, label: '──────────', disabled: true },
+]
diff --git a/frontend/app/views/tile/forms/tile.constructors.js b/frontend/app/views/tile/forms/tile.constructors.js
new file mode 100644
index 0000000..2407274
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.constructors.js
@@ -0,0 +1,113 @@
+const newImage = (data) => ({
+ settings: {
+ ...newPosition(),
+ is_tiled: false,
+ tile_style: 'tile',
+ url: "",
+ external_link_url: "",
+ cursor: 'hand_up',
+ },
+ type: 'image',
+ target_page_id: 0,
+ ...data,
+})
+
+const newVideo = (data) => ({
+ settings: {
+ ...newPosition(),
+ video_style: 'cover',
+ url: "",
+ external_link_url: "",
+ cursor: 'none',
+ muted: false,
+ loop_style: false,
+ autoadvance: false,
+ loop_section: false,
+ loop_start: 0,
+ loop_end: 0,
+ },
+ type: 'video',
+ target_page_id: 0,
+ ...data,
+})
+
+const newText = (data) => ({
+ settings: {
+ ...newPosition(),
+ content: "",
+ font_family: 'sans-serif',
+ font_size: 16,
+ font_style: 'normal',
+ font_color: '#dddddd',
+ background_color: 'transparent',
+ width: 0,
+ height: 0,
+ units: 'px',
+ external_link_url: "",
+ cursor: 'hand_up',
+ },
+ type: 'text',
+ target_page_id: 0,
+ ...data,
+})
+
+const newGradient = (data) => ({
+ settings: {
+ ...newPosition({ width: 100, height: 100 }),
+ from_color: '#ffffff',
+ from_opacity: 1.0,
+ to_color: '#000000',
+ to_opacity: 1.0,
+ angle: 0,
+ stop: 50,
+ units: '%',
+ external_link_url: "",
+ cursor: 'hand_up',
+ },
+ type: 'gradient',
+ target_page_id: 0,
+ ...data,
+})
+
+const newLink = (data) => ({
+ settings: {
+ ...newPosition({ width: 100, height: 100, }),
+ external_link_url: "",
+ cursor: 'hand_up',
+ units: 'px',
+ },
+ type: 'link',
+ target_page_id: 0,
+ ...data,
+})
+
+const newScript = (data) => ({
+ settings: {
+ ...newPosition({ width: 100, height: 100, }),
+ },
+ type: 'script',
+ ...data,
+})
+
+const newPosition = (data) => ({
+ x: 0, y: 0,
+ width: 0, height: 0,
+ rotation: 0, scale: 1,
+ opacity: 1,
+ units: false,
+ align: "center_center",
+ has_audio: false,
+ audio_on_click_id: 0,
+ audio_on_hover_id: 0,
+ navigate_when_audio_finishes: false,
+ ...data,
+})
+
+export const TILE_CONSTRUCTORS = {
+ image: newImage,
+ video: newVideo,
+ text: newText,
+ link: newLink,
+ gradient: newGradient,
+ script: newScript,
+}
diff --git a/frontend/app/views/tile/forms/tile.form.element.gradient.js b/frontend/app/views/tile/forms/tile.form.element.gradient.js
new file mode 100644
index 0000000..3e35cc0
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.gradient.js
@@ -0,0 +1,80 @@
+import React from 'react'
+
+import { NumberInput, ColorInput, Slider } from 'app/common'
+
+export default function TileGradientForm({ tile, parent }) {
+ return (
+ <div>
+ <ColorInput
+ title='From'
+ name='from_color'
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <Slider
+ title='Opacity'
+ name='from_opacity'
+ value={tile.settings.from_opacity}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={1.0}
+ step={0.01}
+ />
+ <ColorInput
+ title='To'
+ name='to_color'
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <Slider
+ title='Opacity'
+ name='to_opacity'
+ value={tile.settings.to_opacity}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={1.0}
+ step={0.01}
+ />
+ <Slider
+ title='Angle'
+ name='angle'
+ value={tile.settings.angle}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={360.0}
+ step={0.1}
+ />
+ <Slider
+ title='Stop'
+ name='stop'
+ value={tile.settings.stop}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={100.0}
+ step={0.1}
+ />
+ <div className='row pair'>
+ <NumberInput
+ title="Width"
+ name="width"
+ data={tile.settings}
+ min={0}
+ max={2400}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <NumberInput
+ title="Height"
+ name="height"
+ data={tile.settings}
+ min={0}
+ max={2400}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.element.image.js b/frontend/app/views/tile/forms/tile.form.element.image.js
new file mode 100644
index 0000000..68ef65a
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.image.js
@@ -0,0 +1,46 @@
+import React from 'react'
+
+import {
+ TextInput,
+ Select, Checkbox,
+} from 'app/common'
+
+import { IMAGE_TILE_STYLES } from './tile.constants'
+
+export default function TileImageForm({ tile, errorFields, parent }) {
+ return (
+ <div>
+ <div className='row imageUrl'>
+ {tile.settings.url && <div className='thumb'><img src={tile.settings.url} /></div>}
+ <TextInput
+ title=""
+ placeholder='http://'
+ name="url"
+ required
+ data={tile.settings}
+ error={errorFields.has('url')}
+ onChange={parent.handleImageChange}
+ autoComplete="off"
+ />
+ </div>
+ <div className='row pair'>
+ <Checkbox
+ label="Tiled"
+ name="is_tiled"
+ checked={tile.settings.is_tiled}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ {tile.settings.is_tiled &&
+ <Select
+ name='tile_style'
+ selected={tile.settings.tile_style || 'tile'}
+ options={IMAGE_TILE_STYLES}
+ title=''
+ onChange={parent.handleSettingsSelect}
+ />
+ }
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/frontend/app/views/tile/forms/tile.form.element.link.js b/frontend/app/views/tile/forms/tile.form.element.link.js
new file mode 100644
index 0000000..37dacd6
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.link.js
@@ -0,0 +1,32 @@
+import React from 'react'
+
+import { NumberInput } from 'app/common'
+
+export default function TileLinkForm({ tile, errorFields, parent }) {
+ return (
+ <div>
+ <div className='row pair'>
+ <NumberInput
+ title="Width"
+ name="width"
+ data={tile.settings}
+ min={0}
+ max={2400}
+ error={errorFields.has('width')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <NumberInput
+ title="Height"
+ name="height"
+ data={tile.settings}
+ min={0}
+ max={2400}
+ error={errorFields.has('height')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/frontend/app/views/tile/forms/tile.form.element.script.js b/frontend/app/views/tile/forms/tile.form.element.script.js
new file mode 100644
index 0000000..547e140
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.script.js
@@ -0,0 +1,22 @@
+import React from 'react'
+
+import { TextArea } from 'app/common'
+
+export default function TileScriptForm({ tile, errorFields, parent }) {
+ return (
+ <div>
+ <TextArea
+ title=""
+ name="content"
+ required
+ data={tile.settings}
+ error={errorFields.has('content')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <div>
+ Scripts will be run on the live site when this page loads.
+ </div>
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.element.text.js b/frontend/app/views/tile/forms/tile.form.element.text.js
new file mode 100644
index 0000000..425a605
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.text.js
@@ -0,0 +1,166 @@
+import React from 'react'
+
+import {
+ NumberInput, ColorInput,
+ Select, TextArea, Checkbox
+} from 'app/common'
+
+import { TEXT_FONT_FAMILIES, TEXT_FONT_STYLES, MARQUEE_DIRECTIONS } from './tile.constants'
+
+export default function TileTextForm({ tile, errorFields, parent }) {
+ return (
+ <div>
+ <TextArea
+ title=""
+ name="content"
+ required
+ data={tile.settings}
+ error={errorFields.has('content')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <div className='row font'>
+ <Select
+ title="Font"
+ name='font_family'
+ selected={tile.settings.font_family || 'sans-serif'}
+ options={TEXT_FONT_FAMILIES}
+ onChange={parent.handleSettingsSelect}
+ />
+ <NumberInput
+ title=''
+ name='font_size'
+ data={tile.settings}
+ min={1}
+ max={1200}
+ error={errorFields.has('font_size')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <Select
+ name='font_style'
+ selected={tile.settings.font_style || 'normal'}
+ options={TEXT_FONT_STYLES}
+ title=''
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ <ColorInput
+ title='Text'
+ name='font_color'
+ data={tile.settings}
+ error={errorFields.has('font_color')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <ColorInput
+ title='BG'
+ name='background_color'
+ data={tile.settings}
+ error={errorFields.has('background_color')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <div className='row pair'>
+ <NumberInput
+ title="Width"
+ name="width"
+ data={tile.settings}
+ min={0}
+ max={1200}
+ error={errorFields.has('width')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <NumberInput
+ title="Height"
+ name="height"
+ data={tile.settings}
+ min={0}
+ max={1200}
+ error={errorFields.has('height')}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+
+ <Checkbox
+ label="Marquee"
+ name="is_marquee"
+ className='short'
+ checked={tile.settings.is_marquee}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ {tile.settings.is_marquee && (
+ <div>
+ <div className='row single'>
+ <Select
+ title="Direction"
+ name='marquee_direction'
+ selected={tile.settings.marquee_direction || 'left'}
+ options={MARQUEE_DIRECTIONS}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ <div className="row single_text">
+ <NumberInput
+ title="Speed"
+ name="marquee_speed"
+ data={tile.settings}
+ step={1}
+ min={0}
+ max={1000}
+ defaultValue={20}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ <div className="row single_text">
+ <NumberInput
+ title="Content width"
+ name="marquee_content_width"
+ data={tile.settings}
+ step={1}
+ min={0}
+ max={5000}
+ defaultValue={200}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ <div className="row single_text">
+ <NumberInput
+ title="Content height"
+ name="marquee_content_height"
+ data={tile.settings}
+ step={1}
+ min={0}
+ max={2000}
+ defaultValue={parseInt(tile.settings.font_size) + 10}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ <Checkbox
+ label="Marquee gradient"
+ name="marquee_gradient"
+ className='short'
+ checked={tile.settings.marquee_gradient}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ {tile.settings.marquee_gradient && (
+ <ColorInput
+ title='Color'
+ name='marquee_gradient_color'
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ )}
+ </div>
+ )}
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.element.video.js b/frontend/app/views/tile/forms/tile.form.element.video.js
new file mode 100644
index 0000000..7220aa9
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.element.video.js
@@ -0,0 +1,101 @@
+import React from 'react'
+
+import {
+ TextInput, Slider,
+ Select, Checkbox,
+} from 'app/common'
+
+import { VIDEO_STYLES } from './tile.constants'
+
+export default function TileVideoForm({ tile, errorFields, parent }) {
+ return (
+ <div>
+ <div className='row imageUrl'>
+ <TextInput
+ title=""
+ placeholder='http://'
+ name="url"
+ required
+ data={tile.settings}
+ error={errorFields.has('url')}
+ onChange={parent.handleVideoChange}
+ autoComplete="off"
+ />
+ </div>
+ <div className='row pair with_checkbox'>
+ <Select
+ name='video_style'
+ selected={tile.settings.video_style || 'none'}
+ options={VIDEO_STYLES}
+ title=''
+ onChange={parent.handleSettingsSelect}
+ />
+ <Checkbox
+ label="Loop"
+ name="loop"
+ checked={tile.settings.loop}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ </div>
+ <div className='row pair'>
+ <Checkbox
+ label="Muted"
+ name="muted"
+ className='short'
+ checked={tile.settings.muted}
+ onChange={parent.handleSettingsSelect}
+ />
+ <Checkbox
+ label="Autoadvance"
+ name="autoadvance"
+ className='short'
+ checked={tile.settings.autoadvance}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ {!tile.settings.muted && (
+ <Slider
+ title='Volume'
+ name='volume'
+ value={('volume' in tile.settings) ? tile.settings.volume : 1.0}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={1.0}
+ step={0.01}
+ />
+ )}
+ {tile.settings.loop && (
+ <div className='row'>
+ <Checkbox
+ label="Loop section?"
+ className='short'
+ name="loop_section"
+ checked={tile.settings.loop_section}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ )}
+ {tile.settings.loop && tile.settings.loop_section && (
+ <div className='row pair'>
+ <TextInput
+ title="From"
+ placeholder='0:00'
+ name="loop_start"
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ <TextInput
+ title="To"
+ placeholder='0:00'
+ name="loop_end"
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ )}
+ </div>
+ )
+} \ No newline at end of file
diff --git a/frontend/app/views/tile/forms/tile.form.hyperlink.js b/frontend/app/views/tile/forms/tile.form.hyperlink.js
new file mode 100644
index 0000000..c444748
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.hyperlink.js
@@ -0,0 +1,64 @@
+import React from 'react'
+
+import {
+ TextInput,
+ Select,
+} from 'app/common'
+
+import {
+ CURSORS,
+ NO_LINK, EXTERNAL_LINK,
+ NO_POPUP, OPEN_POPUP_LINK, CLOSE_POPUP_LINK
+} from './tile.constants'
+
+export default function TileHyperlinkForm({ tile, pageList, popupList, parent }) {
+ const isExternalLink = tile.target_page_id === EXTERNAL_LINK
+ // const isPopupLink = (
+ // tile.target_page_id === OPEN_POPUP_LINK ||
+ // tile.target_page_id === CLOSE_POPUP_LINK
+ // )
+ return (
+ <div>
+ <div className={'row selects'}>
+ <Select
+ title=''
+ name='target_page_id'
+ selected={tile.target_page_id || NO_LINK}
+ options={pageList}
+ onChange={parent.handleSelect}
+ />
+ <Select
+ title=''
+ name='cursor'
+ selected={tile.settings.cursor}
+ options={CURSORS}
+ defaultOption="Cursor"
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ {isExternalLink && (
+ <div>
+ <TextInput
+ title=""
+ placeholder='http://'
+ name="external_link_url"
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ )}
+ {(tile.target_page_id === OPEN_POPUP_LINK || tile.target_page_id === CLOSE_POPUP_LINK) && (
+ <div className='row single'>
+ <Select
+ title="Popup"
+ name='target_popup'
+ selected={tile.settings.target_popup || NO_POPUP}
+ options={popupList}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ )}
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.misc.js b/frontend/app/views/tile/forms/tile.form.misc.js
new file mode 100644
index 0000000..dff5e68
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.misc.js
@@ -0,0 +1,98 @@
+import React from 'react'
+
+import {
+ TextInput,
+ Select, Checkbox, Slider,
+} from 'app/common'
+
+import { UNITS } from './tile.constants'
+
+export default function TileMiscForm({ tile, parent }) {
+ return (
+ <div>
+ <div className='row single'>
+ <Select
+ name='units'
+ selected={tile.settings.units || 'px'}
+ options={UNITS}
+ title='Units'
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ <Slider
+ title='Opacity'
+ name='opacity'
+ value={tile.settings.opacity}
+ onChange={parent.handleSettingsSelect}
+ min={0.0}
+ max={1.0}
+ step={0.01}
+ />
+ <Slider
+ title='Scale'
+ name='scale'
+ value={tile.settings.scale}
+ onChange={parent.handleSettingsSelect}
+ min={0.01}
+ max={10.0}
+ step={0.01}
+ />
+ <Slider
+ title='Rotation'
+ name='rotation'
+ value={tile.settings.rotation}
+ onChange={parent.handleSettingsSelect}
+ min={-180.0}
+ max={180.0}
+ step={1}
+ type='int'
+ />
+ <Checkbox
+ label="Element is a Popup"
+ name="is_popup"
+ className='short'
+ checked={tile.settings.is_popup}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ {tile.settings.is_popup && (
+ <div className='row single_text'>
+ <TextInput
+ title="Popup group"
+ name="popup_group"
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ )}
+ <Checkbox
+ label="Wait to appear"
+ name="wait_to_appear"
+ className='short'
+ checked={tile.settings.wait_to_appear}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ {tile.settings.wait_to_appear && (
+ <div className='row single_text'>
+ <TextInput
+ title="Appear after"
+ name="appear_after"
+ data={tile.settings}
+ onChange={parent.handleSettingsChange}
+ autoComplete="off"
+ />
+ </div>
+ )}
+ <Checkbox
+ label="Hide on click"
+ name="hide_on_click"
+ className='short'
+ checked={tile.settings.hide_on_click}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.sound.js b/frontend/app/views/tile/forms/tile.form.sound.js
new file mode 100644
index 0000000..0662725
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.sound.js
@@ -0,0 +1,53 @@
+import React from 'react'
+
+import {
+ Checkbox,
+} from 'app/common'
+
+import AudioSelect from 'app/views/audio/components/audio.select'
+
+export default function TileSoundForm({ tile, parent }) {
+ return (
+ <div>
+ <Checkbox
+ label="Sound effects"
+ name="has_audio"
+ className='short'
+ checked={tile.settings.has_audio}
+ onChange={parent.handleSettingsSelect}
+ />
+ {tile.settings.has_audio && (
+ <div>
+ <div className='row single'>
+ <AudioSelect
+ title="On click"
+ name="audio_on_click_id"
+ selected={tile.settings.audio_on_click_id}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+
+ {!!tile.settings.audio_on_click_id && (
+ <Checkbox
+ label="Navigate when audio finishes"
+ name="navigate_when_audio_finishes"
+ className='short'
+ checked={tile.settings.navigate_when_audio_finishes}
+ onChange={parent.handleSettingsSelect}
+ autoComplete="off"
+ />
+ )}
+
+ <div className='row single'>
+ <AudioSelect
+ title="On hover"
+ name="audio_on_hover_id"
+ selected={tile.settings.audio_on_hover_id}
+ onChange={parent.handleSettingsSelect}
+ />
+ </div>
+ </div>
+ )}
+ </div>
+ )
+}
diff --git a/frontend/app/views/tile/forms/tile.form.type.js b/frontend/app/views/tile/forms/tile.form.type.js
new file mode 100644
index 0000000..8759384
--- /dev/null
+++ b/frontend/app/views/tile/forms/tile.form.type.js
@@ -0,0 +1,29 @@
+import React from 'react'
+
+import {
+ Select,
+} from 'app/common'
+
+import { SELECT_TYPES, ALIGNMENTS } from './tile.constants'
+
+export default function TileTypeForm({ tile, parent }) {
+ return (
+ <div className="row selects">
+ <Select
+ name='type'
+ selected={tile.type}
+ options={SELECT_TYPES}
+ title=''
+ onChange={parent.handleSelect}
+ />
+ <Select
+ name='align'
+ selected={tile.settings.align}
+ options={ALIGNMENTS}
+ title=''
+ onChange={parent.handleAlignment}
+ />
+ </div>
+ )
+}
+
diff --git a/frontend/app/views/tile/handles/tile.gradient.js b/frontend/app/views/tile/handles/tile.gradient.js
index 1b3cd80..0b20ace 100644
--- a/frontend/app/views/tile/handles/tile.gradient.js
+++ b/frontend/app/views/tile/handles/tile.gradient.js
@@ -1,5 +1,5 @@
import React from 'react'
-import { generateTransform, unitsDimension } from 'app/views/tile/tile.utils'
+import { generateTransform, unitsDimension, hexToRgb } from 'app/views/tile/tile.utils'
export default function TileGradient({ tile, box, bounds, videoBounds, viewing, onMouseDown, onDoubleClick, onMouseEnter }) {
// console.log(tile)
@@ -51,19 +51,3 @@ function tileRGBA(hex, opacity) {
const { r, g, b } = hexToRgb(hex)
return "rgba(" + [r,g,b,opacity].join(",") + ")"
}
-
-function hexToRgb(hex) {
- let result;
- if (hex.length === 7) {
- result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
- } else if (hex.length === 4) {
- result = /^#?([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$/i.exec(hex)
- } else {
- return { r: 255, g: 0, b: 255 }
- }
- return result ? {
- r: parseInt(result[1], 16),
- g: parseInt(result[2], 16),
- b: parseInt(result[3], 16)
- } : null
-} \ No newline at end of file
diff --git a/frontend/app/views/tile/handles/tile.text.js b/frontend/app/views/tile/handles/tile.text.js
index 97fdfac..6ef8734 100644
--- a/frontend/app/views/tile/handles/tile.text.js
+++ b/frontend/app/views/tile/handles/tile.text.js
@@ -1,5 +1,6 @@
import React from 'react'
-import { generateTransform, unitsDimension } from 'app/views/tile/tile.utils'
+import { generateTransform, unitsDimension, hexToRgb } from 'app/views/tile/tile.utils'
+import Marquee from "react-fast-marquee"
export default function TileScript({ tile, box, bounds, videoBounds, viewing, onMouseDown, onDoubleClick, onMouseEnter }) {
// console.log(tile)
@@ -31,6 +32,26 @@ export default function TileScript({ tile, box, bounds, videoBounds, viewing, on
style.backgroundColor = tile.settings.background_color || 'transparent'
style.color = tile.settings.font_color || '#dddddd!important'
+ if (tile.settings.is_marquee) {
+ const gradientColor = hexToRgb(tile.settings.marquee_gradient_color)
+ style.width = "100vw"
+ style.height = style.fontSize + "px"
+ content = (
+ <Marquee
+ direction={tile.settings.marquee_direction || "left"}
+ speed={tile.settings.marquee_speed || 1}
+ gradient={!!tile.settings.marquee_gradient}
+ gradientColor={gradientColor ? [gradientColor.r, gradientColor.g, gradientColor.b] : [0,0,0]}
+ >
+ <div style={{
+ width: (tile.settings.marquee_content_width || 200) + "px",
+ height: (tile.settings.marquee_content_height || (parseInt(tile.settings.font_size) + 20)) + "px",
+ }}>
+ {content}
+ </div>
+ </Marquee>
+ )
+ }
return (
<div
className={className}
diff --git a/frontend/app/views/tile/tile.utils.js b/frontend/app/views/tile/tile.utils.js
index 46d7764..043732b 100644
--- a/frontend/app/views/tile/tile.utils.js
+++ b/frontend/app/views/tile/tile.utils.js
@@ -3,6 +3,13 @@ export const generateTransform = (tile, box, bounds, videoBounds) => {
if (is_tiled) {
return 'translateZ(0)'
}
+
+ const is_full_width = (tile.type === 'text' && tile.settings.is_marquee)
+
+ if (is_full_width) {
+ x = 0
+ }
+
if (box) {
x += box.dx
y += box.dy
@@ -77,4 +84,23 @@ export const unitsDimension = (tile, dimension, bounds, videoBounds) => {
export const videoUnits = (value, bounds, videoBounds) => (
Math.round(value / videoBounds.width * bounds.width) + 'px'
-) \ No newline at end of file
+)
+
+export const hexToRgb = (hex) => {
+ if (!hex) {
+ return null
+ }
+ let result;
+ if (hex.length === 7) {
+ result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
+ } else if (hex.length === 4) {
+ result = /^#?([a-f\d]{1})([a-f\d]{1})([a-f\d]{1})$/i.exec(hex)
+ } else {
+ return { r: 255, g: 0, b: 255 }
+ }
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null
+} \ No newline at end of file
diff --git a/package.json b/package.json
index 042d213..308d26c 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"query-string": "^6.11.1",
"react": "^16.13.0",
"react-dom": "^16.13.0",
+ "react-fast-marquee": "^1.1.2",
"react-infinite-scroller": "^1.2.4",
"react-markdown": "^4.3.1",
"react-redux": "^7.2.0",
diff --git a/yarn.lock b/yarn.lock
index 76d23ba..8b7bf3b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5402,6 +5402,11 @@ react-dom@^16.13.0:
prop-types "^15.6.2"
scheduler "^0.19.1"
+react-fast-marquee@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/react-fast-marquee/-/react-fast-marquee-1.1.2.tgz#5b29c3c8d4761d2e70c74b52ad01e46c705fc21a"
+ integrity sha512-j8pLxwPWrmqpBOUdcflPtV6C4osqftpPSx/NWA77TDxCeb0+M+NcKx6AebVOlcqr1HNnHg3SQS72urtu1AWssw==
+
react-infinite-scroller@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.2.4.tgz#f67eaec4940a4ce6417bebdd6e3433bfc38826e9"