Skip to content

Commit

Permalink
feat: double click on AdLib in the Dashboard and Buckets to trigger (#…
Browse files Browse the repository at this point in the history
…430)

* feat: (WIP) touch-activated, double-click buttons

* fix(NoraFloatingInspector): IntersectionObserver instance could be undefined when used

* feat: single touch/double click dashboard buttons

* feat: add optional toggleOnSingleClick to dashboard layout

* chore: code docs
  • Loading branch information
jstarpl committed Jan 21, 2021
1 parent d34978e commit 8d4f75c
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 77 deletions.
9 changes: 9 additions & 0 deletions meteor/client/lib/lib.tsx
Expand Up @@ -98,3 +98,12 @@ export function ensureHasTrailingSlash(input: string | null): string | null {
return input
}
}

/**
* This CSS Variable is used to indicate to the UI that it's being run on a Browser without ordinary pointers
* but one that emulates mouse-clicks using some other input method (like a Stream Deck).
*/
export const USER_AGENT_POINTER_PROPERTY = '--pointer'
export enum UserAgentPointer {
NO_POINTER = 'no-pointer',
}
7 changes: 7 additions & 0 deletions meteor/client/styles/shelf/dashboard-streamdeck.scss
Expand Up @@ -5,6 +5,13 @@
button size 96px */ screen and (width: 768px) and (height: 384px),
/** Stream Deck Mini,
button size 80px */ screen and (width: 240px) and (height: 160px) {
:root {
// by setting the --pointer CSS variable, we indicate to the UI that it's being run on a Stream Deck and
// that it should run in a single-click-to-activate mode (like a touchscreen) even though the events emitted
// are mouse clicks
--pointer: 'no-pointer';
}

#render-target > .container-fluid > .rundown-view {
background: #000;

Expand Down
5 changes: 3 additions & 2 deletions meteor/client/styles/shelf/dashboard.scss
Expand Up @@ -261,7 +261,7 @@ $dashboard-button-height: 5.625em;
border: none;
margin: 4px;
vertical-align: top;
cursor: pointer;
cursor: default;

@include item-type-colors();
@include invalid-overlay();
Expand Down Expand Up @@ -323,7 +323,8 @@ $dashboard-button-height: 5.625em;
box-shadow: none;
}

&:active {
&:active,
&.active {
&::before {
content: ' ';
display: block;
Expand Down
13 changes: 13 additions & 0 deletions meteor/client/ui/Settings/RundownLayoutEditor.tsx
Expand Up @@ -764,6 +764,19 @@ export default translateWithTracker<IProps, IState, ITrackedProps>((props: IProp
/>
</label>
</div>
<div className="mod mvs mhs">
<label className="field">
{t('Toggle AdLibs on single mouse click')}
<EditAttribute
modifiedClassName="bghl"
attribute={`filters.${index}.toggleOnSingleClick`}
obj={item}
type="checkbox"
collection={RundownLayouts}
className="mod mas"
/>
</label>
</div>
</React.Fragment>
)}
</React.Fragment>
Expand Down
31 changes: 29 additions & 2 deletions meteor/client/ui/Shelf/BucketPanel.tsx
Expand Up @@ -30,7 +30,12 @@ import { PubSub } from '../../../lib/api/pubsub'
import { doUserAction, UserAction } from '../../lib/userAction'
import { NotificationCenter, Notification, NoticeLevel } from '../../lib/notifications/notifications'
import { literal, unprotectString, partial, protectString } from '../../../lib/lib'
import { ensureHasTrailingSlash, contextMenuHoldToDisplayTime } from '../../lib/lib'
import {
ensureHasTrailingSlash,
contextMenuHoldToDisplayTime,
UserAgentPointer,
USER_AGENT_POINTER_PROPERTY,
} from '../../lib/lib'
import { Studio } from '../../../lib/collections/Studios'
import {
IDashboardPanelTrackedProps,
Expand Down Expand Up @@ -156,6 +161,7 @@ interface IState {
dropActive: boolean
bucketName: string
adLibPieces: BucketAdLibItem[]
singleClickMode: boolean
}

export function actionToAdLibPieceUi(
Expand Down Expand Up @@ -211,6 +217,7 @@ export interface IBucketPanelProps {
moveBucket: (id: BucketId, atIndex: number) => void
findBucket: (id: BucketId) => { bucket: Bucket | undefined; index: number }
onBucketReorder: (draggedId: BucketId, newIndex: number, oldIndex: number) => void
onSelectAdlib
onAdLibContext: (args: { contextBucket: Bucket; contextBucketAdLib: BucketAdLibItem }, callback: () => void) => void
onPieceNameRename: () => void
}
Expand Down Expand Up @@ -340,6 +347,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
dropActive: false,
bucketName: props.bucket.name,
adLibPieces: props.adLibPieces.slice(),
singleClickMode: false,
}
}

Expand Down Expand Up @@ -450,6 +458,8 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
}
}

onSelectAdLib = (piece: BucketAdLibItem, e: any) => {}

onToggleAdLib = (piece: BucketAdLibItem, queue: boolean, e: any, mode?: IBlueprintActionTriggerMode) => {
const { t } = this.props

Expand Down Expand Up @@ -710,6 +720,21 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
}
}

private setRef = (ref: HTMLDivElement) => {
this._panel = ref
if (this._panel) {
const style = window.getComputedStyle(this._panel)
// check if a special variable is set through CSS to indicate that we shouldn't expect
// double clicks to trigger AdLibs
const value = style.getPropertyValue(USER_AGENT_POINTER_PROPERTY)
if (this.state.singleClickMode !== (value === UserAgentPointer.NO_POINTER)) {
this.setState({
singleClickMode: value === UserAgentPointer.NO_POINTER,
})
}
}
}

render() {
const { isDragging, connectDragSource, connectDragPreview, connectDropTarget } = this.props
const opacity = isDragging ? 0 : 1
Expand All @@ -723,7 +748,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
'dashboard-panel__panel--sort-dragging': this.props.isDragging,
})}
data-bucket-id={this.props.bucket._id}
ref={(el) => (this._panel = el)}>
ref={this.setRef}>
{this.props.editableName ? (
<input
className="h4 dashboard-panel__header"
Expand Down Expand Up @@ -774,6 +799,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
layer={this.props.sourceLayers[adlib.sourceLayerId]}
outputLayer={this.props.outputLayers[adlib.outputLayerId]}
onToggleAdLib={this.onToggleAdLib as any}
onSelectAdLib={this.onSelectAdLib as any}
playlist={this.props.playlist}
isOnAir={this.isAdLibOnAir((adlib as any) as AdLibPieceUi)}
mediaPreviewUrl={
Expand All @@ -793,6 +819,7 @@ export const BucketPanel = translateWithTracker<Translated<IBucketPanelProps>, I
RundownUtils.isAdLibPiece(this.props.selectedPiece) &&
adlib._id === this.props.selectedPiece._id
}
toggleOnSingleClick={this.state.singleClickMode}
displayStyle={PieceDisplayStyle.BUTTONS}>
{adlib.name}
</BucketPieceButton>
Expand Down

0 comments on commit 8d4f75c

Please sign in to comment.