diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2021-03-31 17:37:40 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2021-03-31 17:37:40 +0200 |
| commit | cda9c115283be8e4e224f6036ba07e5eca243289 (patch) | |
| tree | d0b150bf108813873c7b59cc9f9bd9c00ea3eba7 | |
| parent | a6793f922991d326eeb33cf08b245863218eaef7 (diff) | |
refactor tile forms into own files. add full-width marquee support
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", @@ -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" |
