Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit 8e0c03e

Browse files
committed
feat(Output): support flipping, dynamic font size
1 parent ddfa7c6 commit 8e0c03e

File tree

8 files changed

+61
-14
lines changed

8 files changed

+61
-14
lines changed

packages/apps/backend/src/data-stores/OutputSettingsStore.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ export class OutputSettingsStore {
77
// _id: '',
88

99
// TODO: load these from persistent store upon startup?
10-
fontSize: 10,
10+
fontSize: 7,
1111

1212
mirrorHorizontally: false,
1313
mirrorVertically: false,
1414

1515
focusPosition: 'center',
1616
showFocusPosition: false,
1717

18-
marginHorizontal: 5,
19-
marginVertical: 5,
18+
marginHorizontal: 1,
19+
marginVertical: 1,
2020

2121
activeRundownPlaylistId: null,
2222
})

packages/apps/client/src/PrompterStyles.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020

2121
font-size: var(--prompter-font-size-base);
2222
line-height: var(--prompter-line-height);
23+
24+
/* These are needed to maintain compatibility with Prose-mirror's text layout */
25+
word-wrap: break-word;
26+
white-space: pre-wrap;
27+
white-space: break-spaces;
28+
-webkit-font-variant-ligatures: none;
29+
font-variant-ligatures: none;
30+
font-feature-settings: 'liga' 0;
2331
}
2432

2533
.Prompter p {

packages/apps/client/src/components/CurrentRundown/CurrentRundown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ const CurrentRundown = observer((): React.JSX.Element => {
3737
</Button>
3838
</p>
3939
<SystemStatusAlertBars />
40-
<ul className={classes.SegmentLineList}>
40+
<ul className={classes.SegmentLineList} role="tree">
4141
{openRundown.segmentsInOrder.map((segment) => (
42-
<li key={segment.id} data-segment-id={segment.id} className={classes.SegmentContainer}>
42+
<li key={segment.id} data-segment-id={segment.id} className={classes.SegmentContainer} role="tree">
4343
<Segment segment={segment} />
4444
</li>
4545
))}

packages/apps/client/src/components/CurrentRundown/Segment.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@ const Segment = observer(({ segment }: { segment: UISegment }): React.JSX.Elemen
1313
return lineId === RootAppStore.uiStore.selectedLineId
1414
}
1515

16-
function onClick(e: React.MouseEvent<HTMLLIElement>) {
16+
function onFocus(e: React.FocusEvent<HTMLLIElement>) {
1717
const lineId = e.currentTarget.dataset['lineId'] as UILineId
1818
RootAppStore.uiStore.setSelectedLineId(lineId)
1919
}
2020

2121
return (
2222
<>
23-
<div className={classes.SegmentIdentifier}>{segment.name}</div>
23+
<div className={classes.SegmentIdentifier} role="heading">
24+
{segment.name}
25+
</div>
2426
<ul className={classes.LineContainer}>
2527
{segment.linesInOrder.map((line) => (
2628
<li
2729
key={line.id}
2830
className={isSelected(line.id) ? classes.LineSelected : classes.Line}
29-
onClickCapture={onClick}
31+
onFocus={onFocus}
3032
data-line-id={line.id}
33+
tabIndex={0}
34+
role="treeitem"
3135
>
3236
<Line line={line} />
3337
</li>

packages/apps/client/src/index.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,13 @@ $theme-colors: (
3434
--color-dark-1: #{$dark-1};
3535
--color-dark-2: #{$dark-2};
3636
}
37+
38+
/* Remove outline for non-keyboard :focus */
39+
*:focus:not(:focus-visible) {
40+
outline: none;
41+
}
42+
43+
/* Optional: Customize .focus-visible */
44+
:focus-visible {
45+
outline: $primary solid 2px;
46+
}

packages/apps/client/src/stores/RundownStore.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ export class RundownStore {
9797
sendRundownToOutput = (id: RundownPlaylistId) => {
9898
if (!this.outputSettings) return
9999
// TODO: This really shouldn't require the entire outputSettings object to be available first
100-
this.connection.outputSettings.update(null, {
101-
...this.outputSettings,
100+
this.connection.outputSettings.patch(null, {
102101
activeRundownPlaylistId: id,
103102
})
104103
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.Output {
2+
position: absolute;
3+
top: 0;
4+
left: 0;
5+
width: 100vw;
6+
height: 100vh;
7+
overflow: auto;
8+
}

packages/apps/client/src/views/Output/Output.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useEffect, useRef } from 'react'
1+
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
22
import { observer } from 'mobx-react-lite'
33
import { RootAppStore } from 'src/stores/RootAppStore.ts'
44

@@ -8,7 +8,10 @@ import { Helmet } from 'react-helmet-async'
88
import { getCurrentTime } from 'src/lib/getCurrentTime'
99
import { useQueryParam } from 'src/lib/useQueryParam'
1010

11+
import classes from './Output.module.scss'
12+
1113
const Output = observer(function Output(): React.ReactElement {
14+
const rootEl = useRef<HTMLDivElement>(null)
1215
const speed = useRef(0)
1316

1417
const isPrimary = useQueryParam('primary') !== null
@@ -26,7 +29,7 @@ const Output = observer(function Output(): React.ReactElement {
2629

2730
// don't do this, it's just for testing:
2831
const interval = setInterval(() => {
29-
window.scrollBy(0, speed.current)
32+
rootEl.current?.scrollBy(0, speed.current)
3033
}, 1000 / 60)
3134

3235
return () => {
@@ -139,19 +142,34 @@ const Output = observer(function Output(): React.ReactElement {
139142
140143
*/
141144

145+
const fontSize = RootAppStore.outputSettingsStore.outputSettings.fontSize
146+
const scaleVertical = RootAppStore.outputSettingsStore.outputSettings.mirrorVertically ? '-1' : '1'
147+
const scaleHorizontal = RootAppStore.outputSettingsStore.outputSettings.mirrorHorizontally ? '-1' : '1'
148+
149+
const styleVariables = useMemo(
150+
() =>
151+
({
152+
'--prompter-font-size-base': `${fontSize}vw`,
153+
transform: `scale(${scaleHorizontal}, ${scaleVertical})`,
154+
} as React.CSSProperties),
155+
[fontSize, scaleVertical, scaleHorizontal]
156+
)
157+
158+
const className = `Prompter ${classes.Output}`
159+
142160
if (!rundown) {
143161
return (
144162
<>
145163
{GLOBAL_SETTINGS}
146-
<div className="Prompter"></div>
164+
<div className={className}></div>
147165
</>
148166
)
149167
}
150168

151169
return (
152170
<>
153171
{GLOBAL_SETTINGS}
154-
<div className="Prompter">
172+
<div className={className} style={styleVariables} ref={rootEl}>
155173
<h1>{rundown.name}</h1>
156174
{rundown.segmentsInOrder.map((segment) => (
157175
<Segment key={segment.id} segment={segment} />

0 commit comments

Comments
 (0)