@playkit-js/transcript 3.0.1 → 3.1.0-canary.2-d22690c
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 +7 -0
- package/dist/playkit-transcript.js +1 -1
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +4 -3
- 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/icons/index.ts +4 -0
- package/src/components/plugin-button/plugin-button.scss +0 -19
- package/src/components/plugin-button/plugin-button.tsx +7 -9
- package/src/components/search/search.tsx +93 -57
- package/src/components/transcript/transcript.tsx +23 -9
- package/src/transcript-plugin.tsx +116 -83
- package/src/types/types-ui.ts +0 -7
- package/src/utils/index.ts +0 -1
- package/src/utils/utils.ts +34 -0
- package/src/components/download-print-menu/download-print-icons.tsx +0 -23
- package/src/components/download-print-menu/download-print-menu.scss +0 -44
- package/src/components/download-print-menu/download-print-menu.tsx +0 -164
- package/src/components/download-print-menu/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,23 +1,17 @@
|
|
|
1
1
|
import {h} from 'preact';
|
|
2
2
|
import {OnClickEvent} from '@playkit-js/common';
|
|
3
3
|
import {ui} from 'kaltura-player-js';
|
|
4
|
-
import {
|
|
4
|
+
import {UpperBarManager, SidePanelsManager} from '@playkit-js/ui-managers';
|
|
5
|
+
import {ObjectUtils, downloadContent, printContent} from './utils';
|
|
6
|
+
import {icons} from './components/icons';
|
|
5
7
|
import {PluginButton} from './components/plugin-button/plugin-button';
|
|
6
8
|
import {Transcript} from './components/transcript';
|
|
7
9
|
import {getConfigValue, isBoolean, makePlainText, prepareCuePoint} from './utils';
|
|
8
|
-
import {TranscriptConfig,
|
|
9
|
-
import {DownloadPrintMenu, downloadContent, printContent} from './components/download-print-menu';
|
|
10
|
+
import {TranscriptConfig, PluginStates, HighlightedMap, CuePointData, ItemTypes, CuePoint} from './types';
|
|
10
11
|
|
|
11
12
|
const {SidePanelModes, SidePanelPositions, ReservedPresetNames, ReservedPresetAreas} = ui;
|
|
12
|
-
const {get} = ObjectUtils;
|
|
13
|
-
const {Tooltip} = KalturaPlayer.ui.components;
|
|
14
13
|
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
|
-
});
|
|
14
|
+
const {get} = ObjectUtils;
|
|
21
15
|
|
|
22
16
|
interface TimedMetadataEvent {
|
|
23
17
|
payload: {
|
|
@@ -44,9 +38,10 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
44
38
|
private _hasError = false;
|
|
45
39
|
private _triggeredByKeyboard = false;
|
|
46
40
|
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
private
|
|
41
|
+
private _transcriptPanel = -1;
|
|
42
|
+
private _transcriptIcon = -1;
|
|
43
|
+
private _printIcon = -1;
|
|
44
|
+
private _downloadIcon = -1;
|
|
50
45
|
private _pluginState: PluginStates | null = null;
|
|
51
46
|
|
|
52
47
|
constructor(name: string, player: KalturaPlayerTypes.Player, config: TranscriptConfig) {
|
|
@@ -54,7 +49,11 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
get sidePanelsManager() {
|
|
57
|
-
return this.player.getService('sidePanelsManager') as
|
|
52
|
+
return this.player.getService('sidePanelsManager') as SidePanelsManager | undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get upperBarManager() {
|
|
56
|
+
return this.player.getService('upperBarManager') as UpperBarManager | undefined;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
get cuePointManager() {
|
|
@@ -66,8 +65,8 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
loadMedia(): void {
|
|
69
|
-
if (!this.cuePointManager || !this.sidePanelsManager) {
|
|
70
|
-
this.logger.warn("kalturaCuepoints or
|
|
68
|
+
if (!this.cuePointManager || !this.sidePanelsManager || !this.upperBarManager) {
|
|
69
|
+
this.logger.warn("kalturaCuepoints, sidePanelsManager or upperBarManager haven't registered");
|
|
71
70
|
return;
|
|
72
71
|
}
|
|
73
72
|
if (this.player.isLive()) {
|
|
@@ -86,7 +85,8 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
86
85
|
// turn on loading animation till captions added to TextTrack
|
|
87
86
|
this._isLoading = true;
|
|
88
87
|
}
|
|
89
|
-
this.
|
|
88
|
+
this._addDownloadIcon();
|
|
89
|
+
this._addPrintIcon();
|
|
90
90
|
this._addTranscriptItem();
|
|
91
91
|
}
|
|
92
92
|
});
|
|
@@ -103,7 +103,7 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
103
103
|
|
|
104
104
|
private _updateTranscriptPanel() {
|
|
105
105
|
if (this._transcriptPanel) {
|
|
106
|
-
this.sidePanelsManager
|
|
106
|
+
this.sidePanelsManager?.update(this._transcriptPanel);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -120,14 +120,15 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
private _onTimedMetadataChange = ({payload}: TimedMetadataEvent) => {
|
|
123
|
-
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
124
|
-
|
|
123
|
+
const transcriptCuePoints: Array<CuePoint> = payload.cues
|
|
124
|
+
.filter((cue: CuePoint) => {
|
|
125
|
+
return cue.metadata.cuePointType === ItemTypes.Caption;
|
|
125
126
|
})
|
|
126
127
|
.filter((cue, index, array) => {
|
|
127
128
|
// filter out captions that has endTime eq to next caption startTime
|
|
128
129
|
const nextCue = array[index + 1];
|
|
129
130
|
return !nextCue || cue.endTime !== nextCue.startTime;
|
|
130
|
-
|
|
131
|
+
});
|
|
131
132
|
this._activeCuePointsMap = {};
|
|
132
133
|
transcriptCuePoints.forEach(cue => {
|
|
133
134
|
this._activeCuePointsMap[cue.id] = true;
|
|
@@ -152,42 +153,76 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
152
153
|
return `${activeTextTrack?.language}-${activeTextTrack?.label}`;
|
|
153
154
|
};
|
|
154
155
|
|
|
155
|
-
private
|
|
156
|
-
this.
|
|
157
|
-
|
|
156
|
+
private _activatePlugin = () => {
|
|
157
|
+
this.ready.then(() => {
|
|
158
|
+
this.sidePanelsManager?.activateItem(this._transcriptPanel);
|
|
159
|
+
this._pluginState === PluginStates.OPENED;
|
|
160
|
+
this.upperBarManager?.update(this._transcriptIcon);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
private _deactivatePlugin = () => {
|
|
165
|
+
this.ready.then(() => {
|
|
166
|
+
this.sidePanelsManager?.deactivateItem(this._transcriptPanel);
|
|
167
|
+
this._pluginState = PluginStates.CLOSED;
|
|
168
|
+
this.upperBarManager?.update(this._transcriptIcon);
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
private _isPluginActive = () => {
|
|
173
|
+
return this.sidePanelsManager!.isItemActive(this._transcriptPanel);
|
|
158
174
|
};
|
|
159
175
|
|
|
160
|
-
private
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
private _handleClickOnPluginIcon = (e: OnClickEvent, byKeyboard?: boolean) => {
|
|
177
|
+
if (this._isPluginActive()) {
|
|
178
|
+
this._triggeredByKeyboard = false;
|
|
179
|
+
this._deactivatePlugin();
|
|
180
|
+
} else {
|
|
181
|
+
this._triggeredByKeyboard = Boolean(byKeyboard);
|
|
182
|
+
this._activatePlugin();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
private _addDownloadIcon(): void {
|
|
187
|
+
const {downloadDisabled} = this.config;
|
|
188
|
+
if (this._downloadIcon > 0 || downloadDisabled) {
|
|
163
189
|
return;
|
|
164
190
|
}
|
|
165
|
-
|
|
166
|
-
label:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
printDisabled={getConfigValue(printDisabled, isBoolean, false)}
|
|
175
|
-
dropdownAriaLabel={printDownloadAreaLabel}
|
|
176
|
-
printButtonAriaLabel={printTranscript}
|
|
177
|
-
downloadButtonAriaLabel={downloadTranscript}
|
|
178
|
-
/>
|
|
191
|
+
const translate = {
|
|
192
|
+
label: <Text id="transcript.download_transcript">Download current transcript</Text>
|
|
193
|
+
};
|
|
194
|
+
this._downloadIcon = this.upperBarManager!.add({
|
|
195
|
+
label: translate.label as any,
|
|
196
|
+
svgIcon: {path: icons.DOWNLOAD_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
197
|
+
onClick: this._handleDownload,
|
|
198
|
+
component: withText(translate)((props: {label: string}) => (
|
|
199
|
+
<PluginButton isActive={false} onClick={this._handleDownload} id={'download-transcript'} icon={icons.DOWNLOAD_ICON} label={props.label} />
|
|
179
200
|
))
|
|
180
|
-
});
|
|
201
|
+
}) as number;
|
|
202
|
+
}
|
|
203
|
+
private _addPrintIcon(): void {
|
|
204
|
+
const {printDisabled} = this.config;
|
|
205
|
+
if (this._printIcon > 0 || printDisabled) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const translate = {
|
|
209
|
+
label: <Text id="transcript.print_transcript">Print current transcript</Text>
|
|
210
|
+
};
|
|
211
|
+
this._printIcon = this.upperBarManager!.add({
|
|
212
|
+
label: translate.label as any,
|
|
213
|
+
svgIcon: {path: icons.PRINT_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
214
|
+
onClick: this._handlePrint,
|
|
215
|
+
component: withText(translate)((props: {label: string}) => (
|
|
216
|
+
<PluginButton isActive={false} onClick={this._handlePrint} id={'print-transcript'} icon={icons.PRINT_ICON} label={props.label} />
|
|
217
|
+
))
|
|
218
|
+
}) as number;
|
|
181
219
|
}
|
|
182
220
|
|
|
183
221
|
private _addTranscriptItem(): void {
|
|
184
|
-
const buttonLabel = 'Transcript';
|
|
185
222
|
const {expandMode, position, expandOnFirstPlay} = this.config;
|
|
186
|
-
const pluginMode: PluginPositions = [SidePanelPositions.RIGHT, SidePanelPositions.LEFT].includes(position)
|
|
187
|
-
? PluginPositions.VERTICAL
|
|
188
|
-
: PluginPositions.HORIZONTAL;
|
|
189
223
|
const {showTime, scrollOffset, searchDebounceTimeout, searchNextPrevDebounceTimeout} = this.config;
|
|
190
|
-
|
|
224
|
+
|
|
225
|
+
this._transcriptPanel = this.sidePanelsManager!.add({
|
|
191
226
|
label: 'Transcript',
|
|
192
227
|
panelComponent: () => {
|
|
193
228
|
return (
|
|
@@ -198,7 +233,6 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
198
233
|
searchNextPrevDebounceTimeout={getConfigValue(searchNextPrevDebounceTimeout, Number.isInteger, 100)}
|
|
199
234
|
highlightedMap={this._activeCuePointsMap}
|
|
200
235
|
onSeek={this._seekTo}
|
|
201
|
-
pluginMode={pluginMode}
|
|
202
236
|
onItemClicked={this._seekTo}
|
|
203
237
|
captions={this._data}
|
|
204
238
|
isLoading={this._isLoading}
|
|
@@ -206,43 +240,36 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
206
240
|
onRetryLoad={this._updateTranscriptPanel}
|
|
207
241
|
currentTime={this.player.currentTime}
|
|
208
242
|
videoDuration={this.player.duration}
|
|
209
|
-
kitchenSinkActive={
|
|
243
|
+
kitchenSinkActive={this._isPluginActive()}
|
|
210
244
|
toggledWithEnter={this._triggeredByKeyboard}
|
|
211
|
-
onClose={this.
|
|
245
|
+
onClose={this._deactivatePlugin}
|
|
212
246
|
/>
|
|
213
247
|
);
|
|
214
248
|
},
|
|
215
|
-
iconComponent: ({isActive}: {isActive: boolean}) => {
|
|
216
|
-
return (
|
|
217
|
-
<Tooltip label={buttonLabel} type="bottom">
|
|
218
|
-
<PluginButton
|
|
219
|
-
isActive={isActive}
|
|
220
|
-
label={buttonLabel}
|
|
221
|
-
onClick={(e: OnClickEvent, byKeyboard?: boolean) => {
|
|
222
|
-
if (this.sidePanelsManager.isItemActive(this._transcriptPanel)) {
|
|
223
|
-
this._triggeredByKeyboard = false;
|
|
224
|
-
this._handleCloseClick();
|
|
225
|
-
} else {
|
|
226
|
-
this._triggeredByKeyboard = Boolean(byKeyboard);
|
|
227
|
-
this.sidePanelsManager.activateItem(this._transcriptPanel);
|
|
228
|
-
}
|
|
229
|
-
}}
|
|
230
|
-
/>
|
|
231
|
-
</Tooltip>
|
|
232
|
-
);
|
|
233
|
-
},
|
|
234
249
|
presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads],
|
|
235
250
|
position: position,
|
|
236
251
|
expandMode: expandMode === SidePanelModes.ALONGSIDE ? SidePanelModes.ALONGSIDE : SidePanelModes.OVER,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
252
|
+
onDeactivate: this._deactivatePlugin
|
|
253
|
+
}) as number;
|
|
254
|
+
const translates = {
|
|
255
|
+
showTranscript: <Text id="transcript.show_plugin">Show Transcript</Text>,
|
|
256
|
+
hideTranscript: <Text id="transcript.hide_plugin">Hide Transcript</Text>
|
|
257
|
+
};
|
|
258
|
+
this._transcriptIcon = this.upperBarManager!.add({
|
|
259
|
+
label: 'Transcript',
|
|
260
|
+
svgIcon: {path: icons.PLUGIN_ICON, viewBox: `0 0 ${icons.BigSize} ${icons.BigSize}`},
|
|
261
|
+
onClick: this._handleClickOnPluginIcon as () => void,
|
|
262
|
+
component: withText(translates)((props: {showTranscript: string; hideTranscript: string}) => {
|
|
263
|
+
const isActive = this._isPluginActive();
|
|
264
|
+
const label = isActive ? props.hideTranscript : props.showTranscript;
|
|
265
|
+
return (
|
|
266
|
+
<PluginButton isActive={isActive} onClick={this._handleClickOnPluginIcon} id="transcript-icon" label={label} icon={icons.PLUGIN_ICON} />
|
|
267
|
+
);
|
|
268
|
+
})
|
|
269
|
+
}) as number;
|
|
241
270
|
|
|
242
271
|
if ((expandOnFirstPlay && !this._pluginState) || this._pluginState === PluginStates.OPENED) {
|
|
243
|
-
this.
|
|
244
|
-
this.sidePanelsManager.activateItem(this._transcriptPanel);
|
|
245
|
-
});
|
|
272
|
+
this._activatePlugin();
|
|
246
273
|
}
|
|
247
274
|
}
|
|
248
275
|
|
|
@@ -276,13 +303,19 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
|
|
|
276
303
|
|
|
277
304
|
reset(): void {
|
|
278
305
|
this.eventManager.removeAll();
|
|
279
|
-
if (this.
|
|
280
|
-
this.
|
|
281
|
-
this.
|
|
306
|
+
if (Math.max(this._transcriptPanel, this._transcriptIcon) > 0) {
|
|
307
|
+
this.sidePanelsManager?.remove(this._transcriptPanel);
|
|
308
|
+
this.upperBarManager!.remove(this._transcriptIcon);
|
|
309
|
+
this._transcriptPanel = -1;
|
|
310
|
+
this._transcriptIcon = -1;
|
|
282
311
|
}
|
|
283
|
-
if (this.
|
|
284
|
-
this.
|
|
285
|
-
this.
|
|
312
|
+
if (this._printIcon > 0) {
|
|
313
|
+
this.upperBarManager!.remove(this._printIcon);
|
|
314
|
+
this._printIcon = -1;
|
|
315
|
+
}
|
|
316
|
+
if (this._downloadIcon > 0) {
|
|
317
|
+
this.upperBarManager!.remove(this._downloadIcon);
|
|
318
|
+
this._downloadIcon = -1;
|
|
286
319
|
}
|
|
287
320
|
this._captionMap = new Map();
|
|
288
321
|
this._activeCaptionMapId = '';
|
package/src/types/types-ui.ts
CHANGED
package/src/utils/index.ts
CHANGED
package/src/utils/utils.ts
CHANGED
|
@@ -84,3 +84,37 @@ export const prepareCuePoint = (cuePoint: CuePoint): CuePointData => {
|
|
|
84
84
|
|
|
85
85
|
return itemData;
|
|
86
86
|
};
|
|
87
|
+
|
|
88
|
+
export function downloadContent(content: string, name: string): void {
|
|
89
|
+
const blob = new Blob([content], {type: 'text/plain;charset=utf-8;'});
|
|
90
|
+
const anchor = document.createElement('a');
|
|
91
|
+
const {navigator} = window as any;
|
|
92
|
+
|
|
93
|
+
if (navigator.msSaveBlob) {
|
|
94
|
+
// IE
|
|
95
|
+
navigator.msSaveOrOpenBlob(blob, name);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (navigator.userAgent.search('Firefox') !== -1) {
|
|
99
|
+
// Firefox
|
|
100
|
+
anchor.style.display = 'none';
|
|
101
|
+
anchor.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
|
|
102
|
+
} else {
|
|
103
|
+
// Chrome
|
|
104
|
+
anchor.setAttribute('href', URL.createObjectURL(blob));
|
|
105
|
+
}
|
|
106
|
+
anchor.setAttribute('target', '_blank');
|
|
107
|
+
anchor.setAttribute('download', name);
|
|
108
|
+
anchor.click();
|
|
109
|
+
anchor.remove();
|
|
110
|
+
}
|
|
111
|
+
export function printContent(content: string): void {
|
|
112
|
+
const printWindow = window.open('', '', 'width=400,height=600');
|
|
113
|
+
if (printWindow) {
|
|
114
|
+
printWindow.document.write(content);
|
|
115
|
+
printWindow.document.close();
|
|
116
|
+
printWindow.focus();
|
|
117
|
+
printWindow.print();
|
|
118
|
+
printWindow.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {h} from 'preact';
|
|
2
|
-
|
|
3
|
-
export const PrintIcon = () => (
|
|
4
|
-
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
5
|
-
<g id="Icons/32/print" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
6
|
-
<path
|
|
7
|
-
d="M21,3 C22.0543618,3 22.9181651,3.81587779 22.9945143,4.85073766 L23,5 L23,9 L26,9 C27.1045695,9 28,9.8954305 28,11 L28,21 C28,22.1045695 27.1045695,23 26,23 L23,23 L23,28 C23,29.1045695 22.1045695,30 21,30 L11,30 C9.8954305,30 9,29.1045695 9,28 L9,23 L6,23 C4.8954305,23 4,22.1045695 4,21 L4,11 C4,9.8954305 4.8954305,9 6,9 L9,9 L9,5 C9,3.9456382 9.81587779,3.08183488 10.8507377,3.00548574 L11,3 L21,3 Z M21,19 L11,19 L11,28 L21,28 L21,19 Z M26,11 L6,11 L6,21 L9,21 L9,19 C8.44771525,19 8,18.5522847 8,18 C8,17.4477153 8.44771525,17 9,17 L23,17 C23.5522847,17 24,17.4477153 24,18 C24,18.5522847 23.5522847,19 23,19 L23,21 L26,21 L26,11 Z M21,5 L11,5 L11,9 L21,9 L21,5 Z"
|
|
8
|
-
id="Combined-Shape"
|
|
9
|
-
fill="#fff"></path>
|
|
10
|
-
</g>
|
|
11
|
-
</svg>
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
export const DownloadIcon = () => (
|
|
15
|
-
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink">
|
|
16
|
-
<g id="Icons/32/Download" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
17
|
-
<path
|
|
18
|
-
d="M26,25 C26.5522847,25 27,25.4477153 27,26 C27,26.5522847 26.5522847,27 26,27 L26,27 L7,27 C6.44771525,27 6,26.5522847 6,26 C6,25.4477153 6.44771525,25 7,25 L7,25 Z M15.897,20.797 L15.817,20.73 L15.8163834,20.7298413 L8.34305882,13.7298413 C7.93997861,13.3522902 7.91928313,12.7194636 8.29683417,12.3163834 C8.67438521,11.9133032 9.30721188,11.8926077 9.71029209,12.2701587 L15.4996721,17.693 L15.5,6 C15.5,5.48716416 15.8860402,5.06449284 16.3833789,5.00672773 L16.5,5 C17.0522847,5 17.5,5.44771525 17.5,6 L17.4996721,17.694 L23.2951711,12.2699211 C23.6673663,11.9215418 24.2352038,11.9125649 24.6172049,12.230382 L24.7086128,12.3166371 C25.0860237,12.7198486 25.0651082,13.352668 24.6618968,13.7300789 L17.1833629,20.7300789 L17.1610165,20.7503813 L17.1610165,20.7503813 C17.1421868,20.7669999 17.1224361,20.7831339 17.102079,20.7985075 C17.0891381,20.8082894 17.0764369,20.8174134 17.0635772,20.826204 C17.0434306,20.8399634 17.0223437,20.8532674 17.0007451,20.8657864 C16.9872099,20.8736423 16.9734873,20.8811624 16.959633,20.8883367 L16.8877511,20.9220455 L16.8877511,20.9220455 C16.8756318,20.927087 16.8632234,20.9320132 16.8507409,20.9366814 C16.83028,20.9444208 16.8097352,20.9513578 16.7889039,20.9576336 C16.7705976,20.9630349 16.752126,20.968019 16.7335525,20.9724647 C16.6585039,20.9905214 16.5803589,21 16.5,21 C16.4170842,21 16.3365254,20.9899086 16.2594848,20.9708871 C16.2500284,20.9684434 16.2399293,20.9657886 16.2298654,20.9629733 C16.2028024,20.9554899 16.1769173,20.947049 16.1515197,20.9376057 C16.1370523,20.9321598 16.1223107,20.9262914 16.1076867,20.9200585 C16.0832011,20.9096448 16.0596143,20.8984375 16.036557,20.886357 C16.025923,20.8807972 16.0148138,20.8747205 16.0037984,20.8684173 C15.9792921,20.8543502 15.955966,20.8396537 15.9333153,20.8240474 L15.898,20.798 L15.897,20.797 Z M15.867,20.774 L15.888,20.79 L15.8735171,20.7794831 L15.8735171,20.7794831 L15.867,20.774 Z M15.817,20.73 L15.9035191,20.8027045 C15.8784859,20.7840722 15.8543541,20.7642966 15.831201,20.7434548 L15.817,20.73 Z"
|
|
19
|
-
id="Combined-Shape"
|
|
20
|
-
fill="#ffffff"></path>
|
|
21
|
-
</g>
|
|
22
|
-
</svg>
|
|
23
|
-
);
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
@import '../../variables.scss';
|
|
2
|
-
|
|
3
|
-
.download-print-button {
|
|
4
|
-
display: inline-block;
|
|
5
|
-
width: 36px;
|
|
6
|
-
height: 36px;
|
|
7
|
-
color: $white-color;
|
|
8
|
-
border: none;
|
|
9
|
-
cursor: pointer;
|
|
10
|
-
padding: 0;
|
|
11
|
-
background: none;
|
|
12
|
-
border-radius: 4px;
|
|
13
|
-
outline-offset: 0 -1px;
|
|
14
|
-
&:hover,
|
|
15
|
-
&:active {
|
|
16
|
-
border-radius: 4px;
|
|
17
|
-
background-color: rgba(0, 0, 0, 0.8);
|
|
18
|
-
}
|
|
19
|
-
&:focus {
|
|
20
|
-
outline: 1px solid $focus-color;
|
|
21
|
-
}
|
|
22
|
-
.icon {
|
|
23
|
-
width: 100%;
|
|
24
|
-
height: 100%;
|
|
25
|
-
background-position: center;
|
|
26
|
-
background-size: cover;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
.popover-menu-item {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
padding: 9px 24px 9px 16px;
|
|
33
|
-
white-space: nowrap;
|
|
34
|
-
min-height: 30px;
|
|
35
|
-
line-height: 18px;
|
|
36
|
-
font-size: 15px;
|
|
37
|
-
&:hover {
|
|
38
|
-
cursor: pointer;
|
|
39
|
-
background-color: $semigray-color;
|
|
40
|
-
}
|
|
41
|
-
&:focus {
|
|
42
|
-
outline: 1px solid $focus-color;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import {h, Component, ComponentChild, VNode} from 'preact';
|
|
2
|
-
import {Popover, PopoverHorizontalPositions, PopoverVerticalPositions} from '../../utils';
|
|
3
|
-
import {PopoverMenu, PopoverMenuItem} from '../popover-menu';
|
|
4
|
-
import * as styles from './download-print-menu.scss';
|
|
5
|
-
import {DownloadIcon, PrintIcon} from './download-print-icons';
|
|
6
|
-
|
|
7
|
-
const {ENTER, Esc} = KalturaPlayer.ui.utils.KeyMap;
|
|
8
|
-
|
|
9
|
-
export function downloadContent(content: string, name: string): void {
|
|
10
|
-
const blob = new Blob([content], {type: 'text/plain;charset=utf-8;'});
|
|
11
|
-
const anchor = document.createElement('a');
|
|
12
|
-
const {navigator} = window as any;
|
|
13
|
-
|
|
14
|
-
if (navigator.msSaveBlob) {
|
|
15
|
-
// IE
|
|
16
|
-
navigator.msSaveOrOpenBlob(blob, name);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
if (navigator.userAgent.search('Firefox') !== -1) {
|
|
20
|
-
// Firefox
|
|
21
|
-
anchor.style.display = 'none';
|
|
22
|
-
anchor.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
|
|
23
|
-
} else {
|
|
24
|
-
// Chrome
|
|
25
|
-
anchor.setAttribute('href', URL.createObjectURL(blob));
|
|
26
|
-
}
|
|
27
|
-
anchor.setAttribute('target', '_blank');
|
|
28
|
-
anchor.setAttribute('download', name);
|
|
29
|
-
anchor.click();
|
|
30
|
-
anchor.remove();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function printContent(content: string): void {
|
|
34
|
-
const printWindow = window.open('', '', 'width=400,height=600');
|
|
35
|
-
if (printWindow) {
|
|
36
|
-
printWindow.document.write(content);
|
|
37
|
-
printWindow.document.close();
|
|
38
|
-
printWindow.focus();
|
|
39
|
-
printWindow.print();
|
|
40
|
-
printWindow.close();
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface DownloadPrintMenuProps {
|
|
45
|
-
dropdownAriaLabel: string;
|
|
46
|
-
printButtonAriaLabel: string;
|
|
47
|
-
downloadButtonAriaLabel: string;
|
|
48
|
-
onDownload: () => void;
|
|
49
|
-
onPrint: () => void;
|
|
50
|
-
downloadDisabled: boolean;
|
|
51
|
-
printDisabled: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface ButtonProperties {
|
|
55
|
-
['aria-label']: string;
|
|
56
|
-
buttonStyles: string;
|
|
57
|
-
onClick?: () => void;
|
|
58
|
-
tabIndex?: number;
|
|
59
|
-
iconStyles: string;
|
|
60
|
-
icon: VNode;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface DownloadPrintMenuState {
|
|
64
|
-
popoverOpen: boolean;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class DownloadPrintMenu extends Component<DownloadPrintMenuProps, DownloadPrintMenuState> {
|
|
68
|
-
state: DownloadPrintMenuState = {
|
|
69
|
-
popoverOpen: false
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
private _onDownloadClicked = () => {
|
|
73
|
-
this.props.onDownload();
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
private _onPrintClicked = () => {
|
|
77
|
-
this.props.onPrint();
|
|
78
|
-
};
|
|
79
|
-
private _onKeyDown = (e: KeyboardEvent, callBack: Function) => {
|
|
80
|
-
if (e.keyCode !== ENTER && e.keyCode !== Esc) {
|
|
81
|
-
// don't stopPropagation on ESC and Enter pressed as it prevent the popup closing
|
|
82
|
-
e.stopPropagation();
|
|
83
|
-
}
|
|
84
|
-
switch (e.keyCode) {
|
|
85
|
-
case 13: // Enter pressed
|
|
86
|
-
callBack();
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
private _popoverMenuItemRenderer = (el: PopoverMenuItem) => (
|
|
91
|
-
<div
|
|
92
|
-
tabIndex={1}
|
|
93
|
-
role="button"
|
|
94
|
-
onClick={() => el.onMenuChosen()}
|
|
95
|
-
onKeyDown={(e: KeyboardEvent) => this._onKeyDown(e, el.onMenuChosen)}
|
|
96
|
-
className={styles.popoverMenuItem}>
|
|
97
|
-
{el.label}
|
|
98
|
-
</div>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
private _getPopoverMenuOptions = () => {
|
|
102
|
-
return [
|
|
103
|
-
{
|
|
104
|
-
label: this.props.downloadButtonAriaLabel,
|
|
105
|
-
onMenuChosen: this._onDownloadClicked
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
label: this.props.printButtonAriaLabel,
|
|
109
|
-
onMenuChosen: this._onPrintClicked
|
|
110
|
-
}
|
|
111
|
-
];
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
private _renderIcon = ({buttonStyles, tabIndex = 0, iconStyles, icon, ...props}: ButtonProperties): ComponentChild => {
|
|
115
|
-
return (
|
|
116
|
-
<button className={buttonStyles} tabIndex={tabIndex} {...props}>
|
|
117
|
-
<div className={iconStyles}>{icon}</div>
|
|
118
|
-
</button>
|
|
119
|
-
);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
private _popoverContent = () => {
|
|
123
|
-
return <PopoverMenu itemRenderer={this._popoverMenuItemRenderer} options={this._getPopoverMenuOptions()} />;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
render(props: DownloadPrintMenuProps) {
|
|
127
|
-
const {downloadDisabled, printDisabled} = props;
|
|
128
|
-
if (!downloadDisabled && !printDisabled) {
|
|
129
|
-
return (
|
|
130
|
-
<Popover
|
|
131
|
-
className="download-print-popover"
|
|
132
|
-
verticalPosition={PopoverVerticalPositions.Bottom}
|
|
133
|
-
horizontalPosition={PopoverHorizontalPositions.Left}
|
|
134
|
-
content={this._popoverContent()}>
|
|
135
|
-
{this._renderIcon({
|
|
136
|
-
['aria-label']: props.dropdownAriaLabel,
|
|
137
|
-
buttonStyles: styles.downloadPrintButton,
|
|
138
|
-
iconStyles: styles.icon,
|
|
139
|
-
icon: <DownloadIcon />
|
|
140
|
-
})}
|
|
141
|
-
</Popover>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
if (!downloadDisabled && printDisabled) {
|
|
145
|
-
return this._renderIcon({
|
|
146
|
-
['aria-label']: props.downloadButtonAriaLabel,
|
|
147
|
-
buttonStyles: styles.downloadPrintButton,
|
|
148
|
-
iconStyles: styles.icon,
|
|
149
|
-
onClick: this._onDownloadClicked,
|
|
150
|
-
icon: <DownloadIcon />
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
if (downloadDisabled && !printDisabled) {
|
|
154
|
-
return this._renderIcon({
|
|
155
|
-
['aria-label']: props.printButtonAriaLabel,
|
|
156
|
-
buttonStyles: styles.downloadPrintButton,
|
|
157
|
-
iconStyles: styles.icon,
|
|
158
|
-
onClick: this._onPrintClicked,
|
|
159
|
-
icon: <PrintIcon />
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './download-print-menu';
|
|
@@ -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
|
-
}
|