@playkit-js/transcript 3.0.1 → 3.0.2-canary.1-f9e6d51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/dist/playkit-transcript.js +1 -1
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +2 -2
- package/src/components/caption/caption.tsx +20 -24
- package/src/components/caption-list/captionList.tsx +5 -5
- package/src/components/close-button/index.tsx +9 -3
- package/src/components/download-print-menu/download-print-menu.scss +0 -16
- package/src/components/download-print-menu/download-print-menu.tsx +48 -84
- package/src/components/plugin-button/plugin-button.tsx +10 -2
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.scss +43 -0
- package/src/components/popover/popover.tsx +142 -0
- package/src/components/search/search.tsx +93 -57
- package/src/components/transcript/transcript.tsx +22 -7
- package/src/transcript-plugin.tsx +18 -31
- package/src/utils/index.ts +0 -1
- package/src/components/popover-menu/index.ts +0 -1
- package/src/components/popover-menu/popover-menu.scss +0 -6
- package/src/components/popover-menu/popover-menu.tsx +0 -52
- package/src/utils/popover/popover.scss +0 -30
- package/src/utils/popover/popover.tsx +0 -178
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
import {h, Component} from 'preact';
|
|
2
|
+
import {A11yWrapper, OnClickEvent} from '@playkit-js/common';
|
|
2
3
|
import * as styles from './search.scss';
|
|
3
4
|
import {debounce} from '../../utils';
|
|
4
5
|
const DEBOUNCE_TIMEOUT = 300;
|
|
5
6
|
|
|
7
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
8
|
+
const translates = ({activeSearchIndex, totalSearchResults}: SearchProps) => ({
|
|
9
|
+
searchLabel: <Text id="transcript.search">Search in Transcript</Text>,
|
|
10
|
+
clearSearchLabel: <Text id="transcript.clear_search">Clear search</Text>,
|
|
11
|
+
nextMatchLabel: <Text id="transcript.next_search_match">Next</Text>,
|
|
12
|
+
prevMatchLabel: <Text id="transcript.prev_search_match">Previous</Text>,
|
|
13
|
+
searchResultsLabel: (
|
|
14
|
+
<Text
|
|
15
|
+
id="transcript.prev_search_match"
|
|
16
|
+
fields={{
|
|
17
|
+
current: totalSearchResults > 0 ? activeSearchIndex : 0,
|
|
18
|
+
total: totalSearchResults
|
|
19
|
+
}}>
|
|
20
|
+
{`Result ${totalSearchResults > 0 ? activeSearchIndex : 0} of ${totalSearchResults}`}
|
|
21
|
+
</Text>
|
|
22
|
+
)
|
|
23
|
+
});
|
|
24
|
+
|
|
6
25
|
export interface SearchProps {
|
|
7
26
|
onChange(value: string): void;
|
|
8
27
|
searchQuery: string;
|
|
@@ -13,6 +32,12 @@ export interface SearchProps {
|
|
|
13
32
|
value: string;
|
|
14
33
|
activeSearchIndex: number;
|
|
15
34
|
totalSearchResults: number;
|
|
35
|
+
|
|
36
|
+
searchLabel?: string;
|
|
37
|
+
clearSearchLabel?: string;
|
|
38
|
+
nextMatchLabel?: string;
|
|
39
|
+
prevMatchLabel?: string;
|
|
40
|
+
searchResultsLabel?: string;
|
|
16
41
|
}
|
|
17
42
|
|
|
18
43
|
interface SearchState {
|
|
@@ -20,7 +45,7 @@ interface SearchState {
|
|
|
20
45
|
focused: boolean;
|
|
21
46
|
}
|
|
22
47
|
|
|
23
|
-
|
|
48
|
+
class SearchComponent extends Component<SearchProps, SearchState> {
|
|
24
49
|
state: SearchState = {
|
|
25
50
|
active: false,
|
|
26
51
|
focused: false
|
|
@@ -61,8 +86,8 @@ export class Search extends Component<SearchProps, SearchState> {
|
|
|
61
86
|
this.props.onChange(e.target.value);
|
|
62
87
|
};
|
|
63
88
|
|
|
64
|
-
private _onClear = (event:
|
|
65
|
-
if (
|
|
89
|
+
private _onClear = (event: OnClickEvent, byKeyboard?: boolean) => {
|
|
90
|
+
if (!byKeyboard) {
|
|
66
91
|
this._focusedByMouse = true;
|
|
67
92
|
}
|
|
68
93
|
this._inputRef?.focus();
|
|
@@ -135,7 +160,8 @@ export class Search extends Component<SearchProps, SearchState> {
|
|
|
135
160
|
</div>
|
|
136
161
|
<input
|
|
137
162
|
className={styles.searchInput}
|
|
138
|
-
|
|
163
|
+
aria-label={this.props.searchLabel}
|
|
164
|
+
placeholder={this.props.searchLabel}
|
|
139
165
|
value={searchQuery}
|
|
140
166
|
onInput={this._handleOnChange}
|
|
141
167
|
onFocus={this._onFocus}
|
|
@@ -147,72 +173,82 @@ export class Search extends Component<SearchProps, SearchState> {
|
|
|
147
173
|
}}
|
|
148
174
|
/>
|
|
149
175
|
{searchQuery && (
|
|
150
|
-
<
|
|
151
|
-
<
|
|
152
|
-
width="32px"
|
|
153
|
-
height="32px"
|
|
154
|
-
viewBox="0 0 32 32"
|
|
155
|
-
version="1.1"
|
|
156
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
157
|
-
xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
158
|
-
<g id="Icons/32/Clere" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
159
|
-
<path
|
|
160
|
-
d="M16,8 C20.418278,8 24,11.581722 24,16 C24,20.418278 20.418278,24 16,24 C11.581722,24 8,20.418278 8,16 C8,11.581722 11.581722,8 16,8 Z M19.8665357,12.1334643 C19.6885833,11.9555119 19.4000655,11.9555119 19.2221131,12.1334643 L16,15.356 L12.7778869,12.1334643 L12.7064039,12.0750737 C12.5295326,11.9582924 12.2891726,11.977756 12.1334643,12.1334643 L12.0750737,12.2049473 C11.9582924,12.3818186 11.977756,12.6221786 12.1334643,12.7778869 L15.356,16 L12.1334643,19.2221131 C11.9555119,19.4000655 11.9555119,19.6885833 12.1334643,19.8665357 C12.3114167,20.0444881 12.5999345,20.0444881 12.7778869,19.8665357 L16,16.644 L19.2221131,19.8665357 L19.2935961,19.9249263 C19.4704674,20.0417076 19.7108274,20.022244 19.8665357,19.8665357 L19.9249263,19.7950527 C20.0417076,19.6181814 20.022244,19.3778214 19.8665357,19.2221131 L16.644,16 L19.8665357,12.7778869 C20.0444881,12.5999345 20.0444881,12.3114167 19.8665357,12.1334643 Z"
|
|
161
|
-
id="Shape"
|
|
162
|
-
fill="#cccccc"></path>
|
|
163
|
-
</g>
|
|
164
|
-
</svg>
|
|
165
|
-
</button>
|
|
166
|
-
)}
|
|
167
|
-
{searchQuery && (
|
|
168
|
-
<div className={styles.searchResults}>{`${totalSearchResults > 0 ? `${activeSearchIndex}/${totalSearchResults}` : '0/0'}`}</div>
|
|
169
|
-
)}
|
|
170
|
-
<div className={styles.prevNextWrapper}>
|
|
171
|
-
{searchQuery && (
|
|
172
|
-
<button
|
|
173
|
-
tabIndex={1}
|
|
174
|
-
className={`${styles.prevNextButton} ${totalSearchResults === 0 ? styles.disabled : ''}`}
|
|
175
|
-
onClick={this._goToPrevSearchResult}>
|
|
176
|
+
<A11yWrapper onClick={this._onClear}>
|
|
177
|
+
<button className={styles.clearIcon} tabIndex={1} aria-label={this.props.clearSearchLabel}>
|
|
176
178
|
<svg
|
|
177
|
-
width="
|
|
178
|
-
height="
|
|
179
|
-
viewBox="
|
|
179
|
+
width="32px"
|
|
180
|
+
height="32px"
|
|
181
|
+
viewBox="0 0 32 32"
|
|
180
182
|
version="1.1"
|
|
181
183
|
xmlns="http://www.w3.org/2000/svg"
|
|
182
184
|
xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
183
|
-
<g id="Icons/
|
|
185
|
+
<g id="Icons/32/Clere" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
184
186
|
<path
|
|
185
|
-
d="
|
|
186
|
-
id="
|
|
187
|
-
fill="#cccccc"
|
|
188
|
-
transform="translate(8.000000, 8.519717) scale(1, -1) translate(-8.000000, -8.519717) "></path>
|
|
187
|
+
d="M16,8 C20.418278,8 24,11.581722 24,16 C24,20.418278 20.418278,24 16,24 C11.581722,24 8,20.418278 8,16 C8,11.581722 11.581722,8 16,8 Z M19.8665357,12.1334643 C19.6885833,11.9555119 19.4000655,11.9555119 19.2221131,12.1334643 L16,15.356 L12.7778869,12.1334643 L12.7064039,12.0750737 C12.5295326,11.9582924 12.2891726,11.977756 12.1334643,12.1334643 L12.0750737,12.2049473 C11.9582924,12.3818186 11.977756,12.6221786 12.1334643,12.7778869 L15.356,16 L12.1334643,19.2221131 C11.9555119,19.4000655 11.9555119,19.6885833 12.1334643,19.8665357 C12.3114167,20.0444881 12.5999345,20.0444881 12.7778869,19.8665357 L16,16.644 L19.2221131,19.8665357 L19.2935961,19.9249263 C19.4704674,20.0417076 19.7108274,20.022244 19.8665357,19.8665357 L19.9249263,19.7950527 C20.0417076,19.6181814 20.022244,19.3778214 19.8665357,19.2221131 L16.644,16 L19.8665357,12.7778869 C20.0444881,12.5999345 20.0444881,12.3114167 19.8665357,12.1334643 Z"
|
|
188
|
+
id="Shape"
|
|
189
|
+
fill="#cccccc"></path>
|
|
189
190
|
</g>
|
|
190
191
|
</svg>
|
|
191
192
|
</button>
|
|
193
|
+
</A11yWrapper>
|
|
194
|
+
)}
|
|
195
|
+
{searchQuery && (
|
|
196
|
+
<div className={styles.searchResults} aria-live="polite" aria-label={this.props.searchResultsLabel}>{`${
|
|
197
|
+
totalSearchResults > 0 ? `${activeSearchIndex}/${totalSearchResults}` : '0/0'
|
|
198
|
+
}`}</div>
|
|
199
|
+
)}
|
|
200
|
+
<div className={styles.prevNextWrapper}>
|
|
201
|
+
{searchQuery && (
|
|
202
|
+
<A11yWrapper onClick={this._goToPrevSearchResult}>
|
|
203
|
+
<button
|
|
204
|
+
tabIndex={1}
|
|
205
|
+
className={`${styles.prevNextButton} ${totalSearchResults === 0 ? styles.disabled : ''}`}
|
|
206
|
+
aria-label={this.props.prevMatchLabel}>
|
|
207
|
+
<svg
|
|
208
|
+
width="14px"
|
|
209
|
+
height="12px"
|
|
210
|
+
viewBox="1 0 14 12"
|
|
211
|
+
version="1.1"
|
|
212
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
213
|
+
xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
214
|
+
<g id="Icons/16/Arrow/-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
215
|
+
<path
|
|
216
|
+
d="M4.78325732,5.37830235 C4.43990319,4.94572127 3.81088342,4.87338855 3.37830235,5.21674268 C2.94572127,5.56009681 2.87338855,6.18911658 3.21674268,6.62169765 L7.21674268,11.6611718 C7.61710439,12.165575 8.38289561,12.165575 8.78325732,11.6611718 L12.7832573,6.62169765 C13.1266115,6.18911658 13.0542787,5.56009681 12.6216977,5.21674268 C12.1891166,4.87338855 11.5600968,4.94572127 11.2167427,5.37830235 L8,9.43097528 L4.78325732,5.37830235 Z"
|
|
217
|
+
id="Path-2"
|
|
218
|
+
fill="#cccccc"
|
|
219
|
+
transform="translate(8.000000, 8.519717) scale(1, -1) translate(-8.000000, -8.519717) "></path>
|
|
220
|
+
</g>
|
|
221
|
+
</svg>
|
|
222
|
+
</button>
|
|
223
|
+
</A11yWrapper>
|
|
192
224
|
)}
|
|
193
225
|
{searchQuery && (
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
<A11yWrapper onClick={this._goToNextSearchResult}>
|
|
227
|
+
<button
|
|
228
|
+
tabIndex={1}
|
|
229
|
+
className={`${styles.prevNextButton} ${totalSearchResults === 0 ? styles.disabled : ''}`}
|
|
230
|
+
aria-label={this.props.nextMatchLabel}>
|
|
231
|
+
<svg
|
|
232
|
+
width="14px"
|
|
233
|
+
height="12px"
|
|
234
|
+
viewBox="1 2 14 12"
|
|
235
|
+
version="1.1"
|
|
236
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
237
|
+
xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
238
|
+
<g id="Icons/16/Arrow/down" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
239
|
+
<path
|
|
240
|
+
d="M4.78325732,5.37830235 C4.43990319,4.94572127 3.81088342,4.87338855 3.37830235,5.21674268 C2.94572127,5.56009681 2.87338855,6.18911658 3.21674268,6.62169765 L7.21674268,11.6611718 C7.61710439,12.165575 8.38289561,12.165575 8.78325732,11.6611718 L12.7832573,6.62169765 C13.1266115,6.18911658 13.0542787,5.56009681 12.6216977,5.21674268 C12.1891166,4.87338855 11.5600968,4.94572127 11.2167427,5.37830235 L8,9.43097528 L4.78325732,5.37830235 Z"
|
|
241
|
+
id="Path-2"
|
|
242
|
+
fill="#cccccc"></path>
|
|
243
|
+
</g>
|
|
244
|
+
</svg>
|
|
245
|
+
</button>
|
|
246
|
+
</A11yWrapper>
|
|
213
247
|
)}
|
|
214
248
|
</div>
|
|
215
249
|
</div>
|
|
216
250
|
);
|
|
217
251
|
}
|
|
218
252
|
}
|
|
253
|
+
|
|
254
|
+
export const Search = withText(translates)(SearchComponent);
|
|
@@ -8,7 +8,13 @@ import {CaptionList} from '../caption-list';
|
|
|
8
8
|
import {HighlightedMap, CuePointData, PluginPositions} from '../../types';
|
|
9
9
|
import {CloseButton} from '../close-button';
|
|
10
10
|
import {ErrorIcon} from './error-icon';
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
const {ENTER, SPACE, TAB, ESC} = KalturaPlayer.ui.utils.KeyMap;
|
|
13
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
14
|
+
|
|
15
|
+
const translates = {
|
|
16
|
+
autoScrollLabel: <Text id="transcript.auto_scroll">Enable auto scroll</Text>
|
|
17
|
+
};
|
|
12
18
|
|
|
13
19
|
export interface TranscriptProps {
|
|
14
20
|
onSeek(time: number): void;
|
|
@@ -28,6 +34,7 @@ export interface TranscriptProps {
|
|
|
28
34
|
highlightedMap: HighlightedMap;
|
|
29
35
|
pluginMode: PluginPositions;
|
|
30
36
|
onItemClicked: (n: number) => void;
|
|
37
|
+
autoScrollLabel?: string;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
interface TranscriptState {
|
|
@@ -50,7 +57,7 @@ const initialSearch = {
|
|
|
50
57
|
|
|
51
58
|
const SEARCHBAR_HEIGHT = 38; // height of search bar with margins
|
|
52
59
|
|
|
53
|
-
export class
|
|
60
|
+
export class TranscriptComponent extends Component<TranscriptProps, TranscriptState> {
|
|
54
61
|
private _transcriptListRef: HTMLElement | null = null;
|
|
55
62
|
private _captionListRef: any = null;
|
|
56
63
|
private _skipTranscriptButtonRef: HTMLDivElement | null = null;
|
|
@@ -106,6 +113,7 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
106
113
|
role="button"
|
|
107
114
|
className={`${styles.autoscrollButton} ${isAutoScrollEnabled ? '' : styles.autoscrollButtonVisible}`}
|
|
108
115
|
tabIndex={isAutoScrollEnabled ? -1 : 1}
|
|
116
|
+
aria-label={this.props.autoScrollLabel}
|
|
109
117
|
ref={node => {
|
|
110
118
|
this._autoscrollButtonRef = node;
|
|
111
119
|
}}>
|
|
@@ -201,8 +209,13 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
201
209
|
);
|
|
202
210
|
};
|
|
203
211
|
|
|
212
|
+
private _handleClick = (event: MouseEvent | KeyboardEvent) => {
|
|
213
|
+
event.preventDefault();
|
|
214
|
+
this._autoscrollButtonRef?.focus();
|
|
215
|
+
};
|
|
216
|
+
|
|
204
217
|
private _handleKeyDown = (event: KeyboardEvent) => {
|
|
205
|
-
if (event.keyCode ===
|
|
218
|
+
if (event.keyCode === TAB && !event.shiftKey) {
|
|
206
219
|
this.setState({
|
|
207
220
|
isAutoScrollEnabled: false
|
|
208
221
|
});
|
|
@@ -211,9 +224,8 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
211
224
|
event.preventDefault();
|
|
212
225
|
captionRef.focus();
|
|
213
226
|
}
|
|
214
|
-
} else if (event.keyCode === ENTER || event.keyCode ===
|
|
215
|
-
|
|
216
|
-
this._autoscrollButtonRef?.focus();
|
|
227
|
+
} else if (event.keyCode === ENTER || event.keyCode === SPACE) {
|
|
228
|
+
this._handleClick(event);
|
|
217
229
|
}
|
|
218
230
|
};
|
|
219
231
|
|
|
@@ -226,6 +238,7 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
226
238
|
}}
|
|
227
239
|
className={styles.skipTranscriptButton}
|
|
228
240
|
onKeyDown={this._handleKeyDown}
|
|
241
|
+
onClick={this._handleClick}
|
|
229
242
|
tabIndex={1}>
|
|
230
243
|
Skip transcript
|
|
231
244
|
</div>
|
|
@@ -351,7 +364,7 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
351
364
|
};
|
|
352
365
|
|
|
353
366
|
private _handleEsc = (event: KeyboardEvent) => {
|
|
354
|
-
if (event.keyCode ===
|
|
367
|
+
if (event.keyCode === ESC) {
|
|
355
368
|
this.props.onClose();
|
|
356
369
|
}
|
|
357
370
|
};
|
|
@@ -386,3 +399,5 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
|
|
|
386
399
|
);
|
|
387
400
|
}
|
|
388
401
|
}
|
|
402
|
+
|
|
403
|
+
export const Transcript = withText(translates)(TranscriptComponent);
|
|
@@ -10,14 +10,6 @@ import {DownloadPrintMenu, downloadContent, printContent} from './components/dow
|
|
|
10
10
|
|
|
11
11
|
const {SidePanelModes, SidePanelPositions, ReservedPresetNames, ReservedPresetAreas} = ui;
|
|
12
12
|
const {get} = ObjectUtils;
|
|
13
|
-
const {Tooltip} = KalturaPlayer.ui.components;
|
|
14
|
-
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
15
|
-
|
|
16
|
-
const translates = () => ({
|
|
17
|
-
printDownloadAreaLabel: <Text id="transcript.print_download_area_label">Download or print current transcript</Text>,
|
|
18
|
-
printTranscript: <Text id="transcript.print_transcript">Print current transcript</Text>,
|
|
19
|
-
downloadTranscript: <Text id="transcript.download_transcript">Download current transcript</Text>
|
|
20
|
-
});
|
|
21
13
|
|
|
22
14
|
interface TimedMetadataEvent {
|
|
23
15
|
payload: {
|
|
@@ -120,14 +112,15 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
120
112
|
};
|
|
121
113
|
|
|
122
114
|
private _onTimedMetadataChange = ({payload}: TimedMetadataEvent) => {
|
|
123
|
-
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
124
|
-
|
|
115
|
+
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
116
|
+
.filter((cue: CuePoint) => {
|
|
117
|
+
return cue.metadata.cuePointType === ItemTypes.Caption;
|
|
125
118
|
})
|
|
126
119
|
.filter((cue, index, array) => {
|
|
127
120
|
// filter out captions that has endTime eq to next caption startTime
|
|
128
121
|
const nextCue = array[index + 1];
|
|
129
122
|
return !nextCue || cue.endTime !== nextCue.startTime;
|
|
130
|
-
|
|
123
|
+
});
|
|
131
124
|
this._activeCuePointsMap = {};
|
|
132
125
|
transcriptCuePoints.forEach(cue => {
|
|
133
126
|
this._activeCuePointsMap[cue.id] = true;
|
|
@@ -166,17 +159,14 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
166
159
|
label: 'Download or print transcript',
|
|
167
160
|
area: ReservedPresetAreas.TopBarRightControls,
|
|
168
161
|
presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live],
|
|
169
|
-
get:
|
|
162
|
+
get: () => (
|
|
170
163
|
<DownloadPrintMenu
|
|
171
164
|
onDownload={this._handleDownload}
|
|
172
165
|
onPrint={this._handlePrint}
|
|
173
166
|
downloadDisabled={getConfigValue(downloadDisabled, isBoolean, false)}
|
|
174
167
|
printDisabled={getConfigValue(printDisabled, isBoolean, false)}
|
|
175
|
-
dropdownAriaLabel={printDownloadAreaLabel}
|
|
176
|
-
printButtonAriaLabel={printTranscript}
|
|
177
|
-
downloadButtonAriaLabel={downloadTranscript}
|
|
178
168
|
/>
|
|
179
|
-
)
|
|
169
|
+
)
|
|
180
170
|
});
|
|
181
171
|
}
|
|
182
172
|
|
|
@@ -214,21 +204,18 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
214
204
|
},
|
|
215
205
|
iconComponent: ({isActive}: {isActive: boolean}) => {
|
|
216
206
|
return (
|
|
217
|
-
<
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}}
|
|
230
|
-
/>
|
|
231
|
-
</Tooltip>
|
|
207
|
+
<PluginButton
|
|
208
|
+
isActive={isActive}
|
|
209
|
+
onClick={(e: OnClickEvent, byKeyboard?: boolean) => {
|
|
210
|
+
if (this.sidePanelsManager.isItemActive(this._transcriptPanel)) {
|
|
211
|
+
this._triggeredByKeyboard = false;
|
|
212
|
+
this._handleCloseClick();
|
|
213
|
+
} else {
|
|
214
|
+
this._triggeredByKeyboard = Boolean(byKeyboard);
|
|
215
|
+
this.sidePanelsManager.activateItem(this._transcriptPanel);
|
|
216
|
+
}
|
|
217
|
+
}}
|
|
218
|
+
/>
|
|
232
219
|
);
|
|
233
220
|
},
|
|
234
221
|
presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads],
|
package/src/utils/index.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './popover-menu';
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import {h, Component, ComponentChild} from 'preact';
|
|
2
|
-
import * as styles from './popover-menu.scss';
|
|
3
|
-
|
|
4
|
-
export interface PopoverMenuItem {
|
|
5
|
-
label: string;
|
|
6
|
-
onMenuChosen: Function;
|
|
7
|
-
customRenderer?: (el: PopoverMenuItem) => ComponentChild;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface PopoverMenuProps {
|
|
11
|
-
options: Array<PopoverMenuItem>;
|
|
12
|
-
itemRenderer?: (el: PopoverMenuItem) => ComponentChild;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Popover menu renders list of options.
|
|
17
|
-
* options example:
|
|
18
|
-
* [
|
|
19
|
-
* {label: 'option_1', onMenuChosen: () => console.log('selected first')},
|
|
20
|
-
* {label: 'option_2', onMenuChosen: () => console.log('selected second')}
|
|
21
|
-
* ]
|
|
22
|
-
* In case when 'itemRenderer' properdy hasn't provided - PopoverMenu renders
|
|
23
|
-
* div with class "popover-menu-item" that contain label for the current option.
|
|
24
|
-
* Default render of options can be changed by providing 'itemRenderer' - it should be
|
|
25
|
-
* function that takes current option and returns valid 'preact' node.
|
|
26
|
-
* If some option need to be rendered with a different method - specific render
|
|
27
|
-
* method can be provided with 'customRenderer' property for the current option.
|
|
28
|
-
* option example with specific render method:
|
|
29
|
-
* { label: 'specific render', onMenuChosen: () => {}, customRenderer: el => (<span>{el.label}</span>)}
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
export class PopoverMenu extends Component<PopoverMenuProps> {
|
|
33
|
-
render(props: any) {
|
|
34
|
-
return (
|
|
35
|
-
<div className={styles.popoverMenu}>
|
|
36
|
-
{props.options.map((el: PopoverMenuItem) => {
|
|
37
|
-
if (el.customRenderer) {
|
|
38
|
-
return el.customRenderer(el);
|
|
39
|
-
}
|
|
40
|
-
if (props.itemRenderer) {
|
|
41
|
-
return props.itemRenderer(el);
|
|
42
|
-
}
|
|
43
|
-
return (
|
|
44
|
-
<div className="popover-menu-item" onClick={() => el.onMenuChosen(el)}>
|
|
45
|
-
{el.label}
|
|
46
|
-
</div>
|
|
47
|
-
);
|
|
48
|
-
})}
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
.popover-container {
|
|
2
|
-
position: relative;
|
|
3
|
-
.popover-component {
|
|
4
|
-
background-color: #222222;
|
|
5
|
-
border-radius: 4px;
|
|
6
|
-
position: absolute;
|
|
7
|
-
right: 0px;
|
|
8
|
-
font-size: 15px;
|
|
9
|
-
display: block;
|
|
10
|
-
&.visible {
|
|
11
|
-
visibility: visible;
|
|
12
|
-
opacity: 1;
|
|
13
|
-
z-index: 10;
|
|
14
|
-
}
|
|
15
|
-
&.top {
|
|
16
|
-
bottom: 100%;
|
|
17
|
-
margin-bottom: 6px;
|
|
18
|
-
}
|
|
19
|
-
&.bottom {
|
|
20
|
-
top: 100%;
|
|
21
|
-
margin-top: 6px;
|
|
22
|
-
}
|
|
23
|
-
&.right {
|
|
24
|
-
left: 0px;
|
|
25
|
-
}
|
|
26
|
-
&.left {
|
|
27
|
-
right: 0px;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import {h, Component, ComponentChild} from 'preact';
|
|
2
|
-
import * as styles from './popover.scss';
|
|
3
|
-
|
|
4
|
-
const {ENTER, Esc} = KalturaPlayer.ui.utils.KeyMap;
|
|
5
|
-
|
|
6
|
-
export enum PopoverVerticalPositions {
|
|
7
|
-
Top = 'top',
|
|
8
|
-
Bottom = 'bottom'
|
|
9
|
-
}
|
|
10
|
-
export enum PopoverHorizontalPositions {
|
|
11
|
-
Left = 'left',
|
|
12
|
-
Right = 'right'
|
|
13
|
-
}
|
|
14
|
-
export enum PopoverTriggerMode {
|
|
15
|
-
Click = 'click',
|
|
16
|
-
Hover = 'hover'
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const CLOSE_ON_HOVER_DELAY = 500;
|
|
20
|
-
|
|
21
|
-
const defaultProps = {
|
|
22
|
-
verticalPosition: PopoverVerticalPositions.Top,
|
|
23
|
-
horizontalPosition: PopoverHorizontalPositions.Left,
|
|
24
|
-
triggerMode: PopoverTriggerMode.Click,
|
|
25
|
-
className: 'popover',
|
|
26
|
-
closeOnEsc: true,
|
|
27
|
-
closeOnClick: true
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
interface PopoverProps {
|
|
31
|
-
onClose?: () => void;
|
|
32
|
-
onOpen?: () => void;
|
|
33
|
-
closeOnClick: boolean;
|
|
34
|
-
closeOnEsc: boolean;
|
|
35
|
-
verticalPosition: PopoverVerticalPositions;
|
|
36
|
-
horizontalPosition: PopoverHorizontalPositions;
|
|
37
|
-
className: string;
|
|
38
|
-
triggerMode: PopoverTriggerMode;
|
|
39
|
-
content: ComponentChild;
|
|
40
|
-
children: ComponentChild;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface PopoverState {
|
|
44
|
-
open: boolean;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class Popover extends Component<PopoverProps, PopoverState> {
|
|
48
|
-
private _closeTimeout: any = null;
|
|
49
|
-
private _controlElement: HTMLDivElement | null = null;
|
|
50
|
-
static defaultProps = {
|
|
51
|
-
...defaultProps
|
|
52
|
-
};
|
|
53
|
-
state = {
|
|
54
|
-
open: false
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
componentWillUnmount() {
|
|
58
|
-
this._removeListeners();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private _clearTimeout = () => {
|
|
62
|
-
clearTimeout(this._closeTimeout);
|
|
63
|
-
this._closeTimeout = null;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
private _handleMouseEvent = (event: MouseEvent) => {
|
|
67
|
-
if (!this._controlElement?.contains(event.target as Node | null) && this.props.closeOnClick) {
|
|
68
|
-
this._closePopover();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
private _handleKeyboardEvent = (event: KeyboardEvent) => {
|
|
73
|
-
if (this._controlElement?.contains(event.target as Node | null) && event.keyCode === ENTER) {
|
|
74
|
-
// handle Enter key pressed on Target icon to prevent triggering of _closePopover twice
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if ((this.props.closeOnEsc && event.keyCode === Esc) || event.keyCode === ENTER) {
|
|
78
|
-
// handle if ESC or Enter button presesd
|
|
79
|
-
this._closePopover();
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
private _openPopover = () => {
|
|
84
|
-
const {onOpen} = this.props;
|
|
85
|
-
this._clearTimeout();
|
|
86
|
-
this.setState({open: true}, () => {
|
|
87
|
-
this._addListeners();
|
|
88
|
-
if (onOpen) {
|
|
89
|
-
onOpen();
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
private _closePopover = () => {
|
|
95
|
-
const {onClose} = this.props;
|
|
96
|
-
this._clearTimeout();
|
|
97
|
-
this.setState({open: false}, () => {
|
|
98
|
-
this._removeListeners();
|
|
99
|
-
if (onClose) {
|
|
100
|
-
onClose();
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
private _togglePopover = (e: MouseEvent | KeyboardEvent) => {
|
|
106
|
-
if (this.state.open) {
|
|
107
|
-
this._closePopover();
|
|
108
|
-
} else {
|
|
109
|
-
this._openPopover();
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
private _handleMouseEnter = () => {
|
|
113
|
-
if (!this.state.open) {
|
|
114
|
-
this._openPopover();
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
private _handleMouseLeave = () => {
|
|
118
|
-
this._closeTimeout = setTimeout(this._closePopover, CLOSE_ON_HOVER_DELAY);
|
|
119
|
-
};
|
|
120
|
-
private _handleHoverOnPopover = () => {
|
|
121
|
-
if (this.state.open && this._closeTimeout) {
|
|
122
|
-
this._clearTimeout();
|
|
123
|
-
} else {
|
|
124
|
-
this._closePopover();
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
private _addListeners = () => {
|
|
128
|
-
document.addEventListener('click', this._handleMouseEvent);
|
|
129
|
-
document.addEventListener('keydown', this._handleKeyboardEvent);
|
|
130
|
-
};
|
|
131
|
-
private _removeListeners = () => {
|
|
132
|
-
document.removeEventListener('click', this._handleMouseEvent);
|
|
133
|
-
document.removeEventListener('keydown', this._handleKeyboardEvent);
|
|
134
|
-
};
|
|
135
|
-
private _getHoverEvents = () => {
|
|
136
|
-
if (this.props.triggerMode === PopoverTriggerMode.Hover) {
|
|
137
|
-
return {
|
|
138
|
-
targetEvents: {
|
|
139
|
-
onMouseEnter: this._handleMouseEnter,
|
|
140
|
-
onMouseLeave: this._handleMouseLeave
|
|
141
|
-
},
|
|
142
|
-
popoverEvents: {
|
|
143
|
-
onMouseEnter: this._handleHoverOnPopover,
|
|
144
|
-
onMouseLeave: this._handleHoverOnPopover
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
return {targetEvents: {onClick: this._togglePopover}, popoverEvents: {}};
|
|
149
|
-
};
|
|
150
|
-
render(props: PopoverProps) {
|
|
151
|
-
if (!props.content || !props.children) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
const {targetEvents, popoverEvents} = this._getHoverEvents();
|
|
155
|
-
return (
|
|
156
|
-
<div className={styles.popoverContainer}>
|
|
157
|
-
<div
|
|
158
|
-
className="popover-anchor-container"
|
|
159
|
-
ref={node => {
|
|
160
|
-
this._controlElement = node;
|
|
161
|
-
}}
|
|
162
|
-
{...targetEvents}
|
|
163
|
-
>
|
|
164
|
-
{props.children}
|
|
165
|
-
</div>
|
|
166
|
-
{this.state.open && (
|
|
167
|
-
<div
|
|
168
|
-
aria-expanded="true"
|
|
169
|
-
className={[props.className, styles.popoverComponent, styles[props.verticalPosition], styles[props.horizontalPosition]].join(' ')}
|
|
170
|
-
{...popoverEvents}
|
|
171
|
-
>
|
|
172
|
-
{props.content}
|
|
173
|
-
</div>
|
|
174
|
-
)}
|
|
175
|
-
</div>
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|