Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Free wheel for LTC inputs
- Reference items now show a warning if they're missing a target id
- Keyboard triggers
- Drag and drop on references and triggers to automatically set target id
### Fixed
- An issue where invalid types could crash the rundown widget
- An issue where 0 (or falsy values) could not be used as ids for options in preferences of the select type
Expand All @@ -14,9 +15,11 @@
- The shortcut for opening settings now works as intented
- Settings no longer collide with other modals
- An issue where children weren't removed when their parent group was removed
- Updated dependencies
### Changed
- Keyboard shortcuts now use hotkeys-js for better stability
- The item.apply event has been changed to item.change
- Improved ux when dropping items on references, triggers and groups

## 1.0.0-beta.9
### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The roadmap is available on Notion
- HTTP triggers
- CasparCG library, playout and templates
- LTC timecode triggers
- Keyboard triggers

## Community plugins
- [CRON - triggers based on the time of day](https://github.com/axelboberg/bridge-plugin-cron)
Expand Down
18 changes: 17 additions & 1 deletion plugins/rundown/app/components/RundownGroupItem/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ import { Icon } from '../Icon'

export function RundownGroupItem ({ index, item }) {
const [shared] = React.useContext(SharedContext)
const [isDraggedOver, setIsDraggedOver] = React.useState(false)

const elRef = React.useRef()

React.useEffect(() => {
function onDragEnd () {
setIsDraggedOver(false)
}
window.addEventListener('dragend', onDragEnd)
return () => window.removeEventListener('dragend', onDragEnd)
}, [])

/**
* Set this group to be
* collapsed or expanded
Expand Down Expand Up @@ -69,6 +78,7 @@ export function RundownGroupItem ({ index, item }) {

async function handleDrop (e) {
e.stopPropagation()
setIsDraggedOver(false)
let itemId = e.dataTransfer.getData('text/plain')
const itemSpec = e.dataTransfer.getData('bridge/item')

Expand Down Expand Up @@ -104,6 +114,11 @@ export function RundownGroupItem ({ index, item }) {
function handleDragOver (e) {
e.preventDefault()
e.stopPropagation()
setIsDraggedOver(true)
}

function handleDragLeave (e) {
setIsDraggedOver(false)
}

function handleToggleCollapsed (e) {
Expand All @@ -121,7 +136,7 @@ export function RundownGroupItem ({ index, item }) {
const isCollapsed = item?.['rundown.ui.collapsed']

return (
<div ref={elRef} className={`RundownGroupItem ${isCollapsed ? 'is-collapsed' : ''}`} data-item-type={item.type}>
<div ref={elRef} className={`RundownGroupItem ${isCollapsed ? 'is-collapsed' : ''} ${isDraggedOver ? 'is-draggedOver' : ''}`} data-item-type={item.type}>
<div className='RundownGroupItem-color' style={{ backgroundColor: item?.data?.color }} />
<div className='RundownGroupItem-background' style={{ backgroundColor: item?.data?.color }} />
<div className='RundownGroupItem-header is-scrollTarget' onDoubleClick={e => handleToggleCollapsed(e)}>
Expand Down Expand Up @@ -153,6 +168,7 @@ export function RundownGroupItem ({ index, item }) {
<div
className='RundownGroupItem-dropGuard'
onDragOver={e => handleDragOver(e)}
onDragLeave={e => handleDragLeave(e)}
>
{
(itemIds || []).length === 0 || isCollapsed
Expand Down
12 changes: 12 additions & 0 deletions plugins/rundown/app/components/RundownGroupItem/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@
width: 100%;
}

.RundownGroupItem.is-draggedOver::before {
position: absolute;
content: '';

top: 0;
left: 0;
right: 0;
bottom: 0;

border: 2px dashed var(--base-color);
}

.RundownGroupItem-header {
position: relative;
display: flex;
Expand Down
51 changes: 49 additions & 2 deletions plugins/rundown/app/components/RundownItem/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,20 @@ async function getReadablePropertiesForType (typeName) {
}
}

export function RundownItem ({ index, icon, item }) {
export function RundownItem ({ index, icon, item, dropzone, onDrop = () => {} }) {
const [shared] = React.useContext(SharedContext)
const [typeProperties, setTypeProperties] = React.useState([])

const [isDraggedOver, setIsDraggedOver] = React.useState(false)

React.useEffect(() => {
function onDragEnd () {
setIsDraggedOver(false)
}
window.addEventListener('dragend', onDragEnd)
return () => window.removeEventListener('dragend', onDragEnd)
}, [])

const [name] = useAsyncValue(() => {
/*
Make sure to check if there
Expand Down Expand Up @@ -119,8 +129,34 @@ export function RundownItem ({ index, icon, item }) {
loadProperties()
}, [item?.type])

/**
* Prevent the event from propagating
* so that we don't trigger the parent
* rundown's listeners
*/
function handleDragOver (e) {
e.preventDefault()
e.stopPropagation()
setIsDraggedOver(true)
}

function handleDragLeave (e) {
setIsDraggedOver(false)
}

async function handleDrop (e) {
e.stopPropagation()
setIsDraggedOver(false)

const itemId = e.dataTransfer.getData('text/plain')

if (itemId) {
onDrop(itemId)
}
}

return (
<div className={`RundownItem ${isCompact ? 'is-compact' : ''}`}>
<div className={`RundownItem ${isCompact ? 'is-compact' : ''} ${isDraggedOver ? 'is-draggedOver' : ''}`}>
<div className='RundownItem-margin'>
<Layout.Spread>
<div className='RundownItem-section'>
Expand Down Expand Up @@ -180,6 +216,17 @@ export function RundownItem ({ index, icon, item }) {
<RundownItemTimeSection item={item} />
</Layout.Spread>
</div>
{
dropzone &&
(
<div
className='RundownItem-dropZone'
onDragOver={e => handleDragOver(e)}
onDragLeave={e => handleDragLeave(e)}
onDrop={e => handleDrop(e)}
/>
)
}
<RundownItemProgress item={item} />
</div>
)
Expand Down
21 changes: 21 additions & 0 deletions plugins/rundown/app/components/RundownItem/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
margin: 0.4em 1em;
}

.RundownItem.is-draggedOver::before {
position: absolute;
content: '';

top: 0;
left: 0;
right: 0;
bottom: 0;

border: 2px dashed var(--base-color);
}

.RundownItem-section {
display: flex;
width: 100%;
Expand Down Expand Up @@ -92,3 +104,12 @@
opacity: 0.3;
z-index: -1;
}

.RundownItem-dropZone {
position: absolute;
width: 100%;
height: 50%;
bottom: 0;

z-index: 1;
}
2 changes: 2 additions & 0 deletions plugins/rundown/app/components/RundownList/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import './style.css'

import { SharedContext } from '../../sharedContext'

import { RundownReferenceItem } from '../RundownReferenceItem'
import { RundownVariableItem } from '../RundownVariableItem'
import { RundownTriggerItem } from '../RundownTriggerItem'
import { RundownDividerItem } from '../RundownDividerItem'
Expand All @@ -24,6 +25,7 @@ import * as keyboard from '../../utils/keyboard'
*/
const TYPE_COMPONENTS = {
'bridge.variables.variable': { item: RundownVariableItem },
'bridge.types.reference': { item: RundownReferenceItem },
'bridge.types.trigger': { item: RundownTriggerItem },
'bridge.types.divider': { item: RundownDividerItem },
'bridge.types.group': {
Expand Down
21 changes: 21 additions & 0 deletions plugins/rundown/app/components/RundownReferenceItem/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import './style.css'

import bridge from 'bridge'

import { RundownItem } from '../RundownItem'

export function RundownReferenceItem ({ index, item }) {
function handleDrop (itemId) {
if (typeof itemId !== 'string') {
return
}
bridge.items.applyItem(item.id, {
data: {
targetId: itemId
}
}, true)
}

return <RundownItem index={index} item={item} onDrop={itemId => handleDrop(itemId)} dropzone />
}
Empty file.
15 changes: 14 additions & 1 deletion plugins/rundown/app/components/RundownTriggerItem/index.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import React from 'react'
import './style.css'

import bridge from 'bridge'

import { RundownItem } from '../RundownItem'

export function RundownTriggerItem ({ index, item }) {
return <RundownItem index={index} item={item} icon='trigger' />
function handleDrop (itemId) {
if (typeof itemId !== 'string') {
return
}
bridge.items.applyItem(item.id, {
data: {
targetId: itemId
}
}, true)
}

return <RundownItem index={index} item={item} icon='trigger' onDrop={itemId => handleDrop(itemId)} dropzone />
}
3 changes: 2 additions & 1 deletion plugins/types/lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ async function init (htmlPath) {
targetId: {
name: 'Target id',
type: 'string',
'ui.group': 'Reference'
'ui.group': 'Reference',
'ui.readable': true
},
targetButton: {
name: '',
Expand Down