Skip to content

Commit 1d0f8fc

Browse files
author
Mint de Wit
authored
Merge pull request Sofie-Automation#1146 from nrkno/SOFIE-2867-r50-drop-plugin
feat: support external url dropzones
2 parents 842c6e3 + 155397f commit 1d0f8fc

File tree

8 files changed

+302
-16
lines changed

8 files changed

+302
-16
lines changed

meteor/client/styles/shelf/externalFramePanel.scss

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,33 @@
2626
margin: 0.625rem;
2727
}
2828
}
29+
30+
.dropzone-panel {
31+
position: absolute;
32+
top: 0;
33+
left: 0;
34+
bottom: 0;
35+
right: 0;
36+
37+
.external-frame-panel__iframe {
38+
position: absolute;
39+
top: 0;
40+
left: 0;
41+
bottom: 0;
42+
right: 0;
43+
width: 100%;
44+
height: 100%;
45+
border: none;
46+
transform-origin: top left;
47+
z-index: 99;
48+
}
49+
.external-frame-panel__overlay {
50+
position: absolute;
51+
top: 0;
52+
left: 0;
53+
bottom: 0;
54+
right: 0;
55+
border: none;
56+
transform-origin: top left;
57+
}
58+
}

meteor/client/ui/Settings/components/FilterEditor.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,18 @@ export default withTranslation()(
531531
/>
532532
</label>
533533

534+
<label className="field">
535+
<LabelActual label={t('Dropzone URL')} />
536+
<EditAttribute
537+
modifiedClassName="bghl"
538+
attribute={`filters.${index}.dropzoneUrl`}
539+
obj={item}
540+
type="text"
541+
collection={RundownLayouts}
542+
className="input text-input input-l"
543+
/>
544+
</label>
545+
534546
<label className="field">
535547
<LabelActual label={t('Display Rank')} />
536548
<EditAttribute

meteor/client/ui/Shelf/BucketPanel.tsx

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { PieceDisplayStyle } from '../../../lib/collections/RundownLayouts'
4747
import RundownViewEventBus, {
4848
RundownViewEvents,
4949
RevealInShelfEvent,
50+
ToggleShelfDropzoneEvent,
5051
} from '../../../lib/api/triggers/RundownViewEventBus'
5152
import { setShelfContextMenuContext, ContextType } from './ShelfContextMenu'
5253
import { translateMessage } from '@sofie-automation/corelib/dist/TranslatableMessage'
@@ -178,6 +179,7 @@ const bucketTarget = {
178179

179180
interface IState {
180181
dropActive: boolean
182+
dropFrameActive: string | null
181183
bucketName: string
182184
adLibPieces: BucketAdLibItem[]
183185
singleClickMode: boolean
@@ -244,6 +246,7 @@ export interface IBucketPanelProps {
244246
onSelectAdlib
245247
onAdLibContext: (args: { contextBucket: Bucket; contextBucketAdLib: BucketAdLibItem }, callback: () => void) => void
246248
onPieceNameRename: () => void
249+
extFrameDropZones: { _id: string; url: string }[]
247250
}
248251

249252
export interface IBucketPanelTrackedProps extends IDashboardPanelTrackedProps {
@@ -378,6 +381,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
378381

379382
this.state = {
380383
dropActive: false,
384+
dropFrameActive: null,
381385
bucketName: props.bucket.name,
382386
adLibPieces: props.adLibPieces.slice(),
383387
singleClickMode: false,
@@ -419,6 +423,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
419423
window.addEventListener(MOSEvents.dragleave, this.onDragLeave)
420424

421425
RundownViewEventBus.on(RundownViewEvents.REVEAL_IN_SHELF, this.onRevealInShelf)
426+
RundownViewEventBus.on(RundownViewEvents.TOGGLE_SHELF_DROPZONE, this.onToggleDropFrame)
422427
}
423428

424429
componentDidUpdate(prevProps: IBucketPanelProps & IBucketPanelTrackedProps) {
@@ -436,6 +441,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
436441

437442
window.removeEventListener(MOSEvents.dragenter, this.onDragEnter)
438443
window.removeEventListener(MOSEvents.dragleave, this.onDragLeave)
444+
RundownViewEventBus.removeListener(RundownViewEvents.TOGGLE_SHELF_DROPZONE, this.onToggleDropFrame)
439445
}
440446

441447
onRevealInShelf = (e: RevealInShelfEvent) => {
@@ -791,6 +797,13 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
791797
)
792798
}
793799

800+
private onToggleDropFrame = (e: ToggleShelfDropzoneEvent) => {
801+
this.setState({
802+
// dropActive: e.display,
803+
dropFrameActive: e.display ? e.id : null,
804+
})
805+
}
806+
794807
render(): JSX.Element | null {
795808
const { connectDragSource, connectDragPreview, connectDropTarget } = this.props
796809

@@ -830,7 +843,8 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
830843
<div
831844
className={ClassNames('dashboard-panel', 'dashboard-panel__panel--bucket', {
832845
'dashboard-panel__panel--bucket-active': this.state.dropActive,
833-
'dashboard-panel__panel--sort-dragging': this.props.isDragging,
846+
'dashboard-panel__panel--sort-dragging':
847+
(this.props.isDragging || this.state.dropFrameActive) && !this.state.dropActive,
834848
})}
835849
data-bucket-id={this.props.bucket._id}
836850
ref={this.setRef}
@@ -908,6 +922,18 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
908922
</BucketPieceButton>
909923
</ContextMenuTrigger>
910924
))}
925+
{this.props.extFrameDropZones.map((dropZone) => (
926+
<DropzoneHolder
927+
key={dropZone._id}
928+
bucketId={this.props.bucket._id}
929+
id={dropZone._id}
930+
url={dropZone.url}
931+
hidden={this.state.dropFrameActive !== dropZone._id}
932+
showStyleBaseId={this.props.showStyleBaseId}
933+
onDragEnter={this.onDragEnter}
934+
onDragLeave={this.onDragLeave}
935+
/>
936+
))}
911937
</div>
912938
</div>
913939
)
@@ -919,3 +945,88 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
919945
)
920946
)
921947
)
948+
949+
interface DropzoneHolderProps {
950+
id: string
951+
bucketId: BucketId
952+
url: string
953+
hidden: boolean
954+
showStyleBaseId: ShowStyleBaseId
955+
956+
onDragEnter?: () => void
957+
onDragLeave?: () => void
958+
}
959+
const DropzoneHolder = (props: DropzoneHolderProps) => {
960+
const [dropzoneElementRef, setDropzoneElementRef] = React.useState<HTMLIFrameElement | null>(null)
961+
962+
const onMessage = React.useCallback(
963+
(event: MessageEvent) => {
964+
// filter out messages from this panel
965+
if (event.source !== dropzoneElementRef?.contentWindow) return
966+
967+
switch (event.data?.event) {
968+
case 'drop':
969+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
970+
id: props.id,
971+
bucketId: props.bucketId,
972+
ev: event,
973+
})
974+
if (props.onDragLeave) props.onDragLeave()
975+
break
976+
case 'data':
977+
if (event.data.data.trim().endsWith('</mos>')) {
978+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
979+
id: props.id,
980+
bucketId: props.bucketId,
981+
message: event.data.data,
982+
ev: event,
983+
})
984+
}
985+
break
986+
case 'error':
987+
RundownViewEventBus.emit(RundownViewEvents.ITEM_DROPPED, {
988+
id: props.id,
989+
bucketId: props.bucketId,
990+
error: event.data.message,
991+
ev: event,
992+
})
993+
break
994+
case 'dragEnter':
995+
if (props.onDragEnter) props.onDragEnter()
996+
break
997+
case 'dragLeave':
998+
if (props.onDragLeave) props.onDragLeave()
999+
break
1000+
}
1001+
},
1002+
[dropzoneElementRef, props.onDragEnter, props.onDragLeave]
1003+
)
1004+
1005+
React.useEffect(() => {
1006+
if (!dropzoneElementRef) return
1007+
1008+
const registerHandlers = () => {
1009+
window.addEventListener('message', onMessage)
1010+
}
1011+
const unregisterHandlers = () => {
1012+
window.removeEventListener('message', onMessage)
1013+
}
1014+
1015+
registerHandlers()
1016+
1017+
return () => {
1018+
unregisterHandlers()
1019+
}
1020+
}, [dropzoneElementRef, onMessage])
1021+
1022+
return (
1023+
<div className="dropzone-panel" style={{ visibility: props.hidden ? 'hidden' : 'visible' }}>
1024+
<iframe
1025+
ref={setDropzoneElementRef}
1026+
className="external-frame-panel__iframe"
1027+
src={props.url}
1028+
sandbox="allow-forms allow-popups allow-scripts allow-same-origin"
1029+
></iframe>
1030+
</div>
1031+
)
1032+
}

0 commit comments

Comments
 (0)