@playkit-js/transcript 3.5.31 → 3.5.32-canary.0-6a8848a

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playkit-js/transcript",
3
- "version": "3.5.31",
3
+ "version": "3.5.32-canary.0-6a8848a",
4
4
  "main": "dist/playkit-transcript.js",
5
5
  "license": "AGPL-3.0",
6
6
  "private": false,
@@ -14,8 +14,8 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@playkit-js/kaltura-player-js": "3.17.9",
17
- "@playkit-js/playkit-js-ui": "0.78.0",
18
- "@playkit-js/ui-managers": "1.5.4-canary.0-9261ea6",
17
+ "@playkit-js/playkit-js-ui": "0.79.13",
18
+ "@playkit-js/ui-managers": "1.6.2-canary.0-9634d47",
19
19
  "@types/sanitize-html": "^2.9.3",
20
20
  "conventional-github-releaser": "3.1.3",
21
21
  "copyfiles": "^2.4.1",
@@ -77,7 +77,7 @@
77
77
  "html5 player"
78
78
  ],
79
79
  "dependencies": {
80
- "@playkit-js/common": "1.5.17",
80
+ "@playkit-js/common": "1.5.21",
81
81
  "sanitize-html": "^2.11.0",
82
82
  "stream-browserify": "^3.0.0"
83
83
  },
@@ -0,0 +1,108 @@
1
+ import {A11yWrapper, OnClickEvent} from '@playkit-js/common/dist/hoc/a11y-wrapper';
2
+ import {h} from 'preact';
3
+ import {useState} from 'preact/hooks';
4
+ import {Icon} from '@playkit-js/common/dist/icon';
5
+
6
+ import * as styles from './popover-menu.scss';
7
+
8
+ export interface PopoverMenuItemData {
9
+ testId: string;
10
+ label: string;
11
+ onClick?: () => void;
12
+ isDisabled?: boolean;
13
+ isSelected?: boolean;
14
+ items?: Array<PopoverMenuItemData>;
15
+ }
16
+
17
+ interface PopoverMenuItemProps {
18
+ item: PopoverMenuItemData;
19
+ index: number;
20
+ setRef?: (index: number, ref: HTMLDivElement | null) => void;
21
+ onKeyUp: (index: number) => void;
22
+ onKeyDown: (index: number) => void;
23
+ onClick?: () => void;
24
+ }
25
+
26
+ export const PopoverMenuItem = (props: PopoverMenuItemProps) => {
27
+ const {item, index, setRef, onKeyUp, onKeyDown, onClick} = props;
28
+ const {isDisabled, isSelected, items, testId, label} = item;
29
+ const [isChildOpen, setIsChildOpen] = useState(false);
30
+
31
+ const getAddonAfter = () => {
32
+ if (items) {
33
+ return <Icon name="chevronRight" />;
34
+ }
35
+ if (isSelected) {
36
+ return <Icon name="check" />;
37
+ }
38
+ return null;
39
+ };
40
+
41
+ const handleOnClick = (event: OnClickEvent) => {
42
+ if (isDisabled) {
43
+ return;
44
+ }
45
+ if (items) {
46
+ event.stopPropagation();
47
+ setIsChildOpen(!isChildOpen);
48
+ return;
49
+ }
50
+ onClick?.();
51
+ };
52
+
53
+ const renderChildItems = () => {
54
+ if (!items) {
55
+ return null;
56
+ }
57
+ return (
58
+ <div className={[styles.popoverComponent, styles.childItem].join(' ')} role="menu" aria-expanded={isChildOpen} id="popoverContent">
59
+ {isChildOpen
60
+ ? items.map((item, index) => (
61
+ <PopoverMenuItem
62
+ key={index}
63
+ item={item}
64
+ index={index}
65
+ onKeyDown={() => {}}
66
+ onKeyUp={() => {}}
67
+ onClick={() => {
68
+ item.onClick?.();
69
+ onClick?.();
70
+ setIsChildOpen(false);
71
+ }}
72
+ />
73
+ ))
74
+ : null}
75
+ </div>
76
+ );
77
+ };
78
+
79
+ return (
80
+ <A11yWrapper
81
+ role="menuitem"
82
+ onClick={handleOnClick}
83
+ onDownKeyPressed={() => {
84
+ if (!isDisabled) {
85
+ onKeyDown(index);
86
+ }
87
+ }}
88
+ onUpKeyPressed={() => {
89
+ if (!isDisabled) {
90
+ onKeyUp(index);
91
+ }
92
+ }}>
93
+ <div
94
+ tabIndex={isDisabled ? -1 : 0}
95
+ role="menuitem"
96
+ aria-selected={isSelected}
97
+ className={`${styles.popoverMenuItem} ${isDisabled ? styles.popoverMenuItemDisabled : ''}`}
98
+ data-testid={testId}
99
+ ref={node => {
100
+ setRef?.(index, node);
101
+ }}>
102
+ {label}
103
+ {getAddonAfter()}
104
+ {renderChildItems()}
105
+ </div>
106
+ </A11yWrapper>
107
+ );
108
+ };
@@ -1,17 +1,19 @@
1
1
  @import './variables.scss';
2
2
 
3
+ $child-item-width: 170px;
4
+
3
5
  .popover-anchor-container {
4
6
  cursor: pointer;
5
7
  border-radius: $roundness-1;
6
-
8
+
7
9
  &:hover {
8
10
  background-color: $tone-4-color;
9
11
  }
10
-
12
+
11
13
  &.active {
12
14
  background-color: $tone-6-color;
13
15
  }
14
-
16
+
15
17
  .popover-anchor {
16
18
  pointer-events: none;
17
19
  }
@@ -19,7 +21,7 @@
19
21
 
20
22
  .popover-container {
21
23
  position: relative;
22
-
24
+
23
25
  .popover-component {
24
26
  background-color: $tone-7-color;
25
27
  border-radius: $roundness-1;
@@ -29,11 +31,19 @@
29
31
  position: absolute;
30
32
  right: 0px;
31
33
  z-index: 1;
34
+ &.child-item {
35
+ margin-top: 0;
36
+ top: 0;
37
+ width: $child-item-width;
38
+ left: calc(-7px - #{$child-item-width});
39
+ }
32
40
  }
33
41
  }
34
42
  .popover-menu-item {
43
+ position: relative;
35
44
  align-items: center;
36
45
  display: flex;
46
+ justify-content: space-between;
37
47
  font-size: 15px;
38
48
  line-height: 18px;
39
49
  min-height: 30px;
@@ -44,13 +54,13 @@
44
54
  &.popover-menu-item-disabled {
45
55
  color: $tone-4-color;
46
56
  }
47
-
57
+
48
58
  &:hover:not(.popover-menu-item-disabled) {
49
59
  background-color: $tone-6-color;
50
60
  border-radius: $roundness-1;
51
61
  cursor: pointer;
52
62
  }
53
-
63
+
54
64
  &:focus {
55
65
  outline: 1px solid #222;
56
66
  }
@@ -60,4 +70,3 @@
60
70
  padding-top: 6px;
61
71
  padding-bottom: 6px;
62
72
  }
63
-
@@ -1,21 +1,15 @@
1
1
  import {A11yWrapper} from '@playkit-js/common/dist/hoc/a11y-wrapper';
2
2
  import {h, Component, VNode} from 'preact';
3
+ import {PopoverMenuItem, PopoverMenuItemData} from './popover-menu-item';
4
+ import {ui} from '@playkit-js/kaltura-player-js';
5
+ const {Tooltip} = ui.components;
6
+ const {withText, Text} = ui.preacti18n;
3
7
 
4
- const {Tooltip} = KalturaPlayer.ui.components;
5
- const {withText, Text} = KalturaPlayer.ui.preacti18n;
6
-
7
- const {withEventManager} = KalturaPlayer.ui.Event;
8
- const {TAB} = KalturaPlayer.ui.utils.KeyMap;
8
+ const {withEventManager} = ui.Event;
9
+ const {TAB} = ui.utils.KeyMap;
9
10
 
10
11
  import * as styles from './popover-menu.scss';
11
12
 
12
- interface PopoverMenuItemData {
13
- testId: string;
14
- label: string;
15
- onClick: () => void;
16
- isDisabled?: boolean;
17
- }
18
-
19
13
  interface PopoverMenuProps {
20
14
  moreOptionsLabel?: string;
21
15
  eventManager?: any;
@@ -42,23 +36,25 @@ class PopoverMenu extends Component<PopoverMenuProps, PopoverMenuState> {
42
36
 
43
37
  constructor(props: PopoverMenuProps) {
44
38
  super(props);
45
- this.state = {isOpen: false};
39
+ this.state = {
40
+ isOpen: false
41
+ };
46
42
 
47
- this.props.eventManager?.listen(document, 'click', this.handleMouseEvent);
48
- this.props.eventManager?.listen(document, 'keydown', this.handleKeydownEvent);
43
+ this.props.eventManager?.listen(document, 'click', this._handleMouseEvent);
44
+ this.props.eventManager?.listen(document, 'keydown', this._handleKeydownEvent);
49
45
  }
50
46
 
51
47
  componentWillUnmount() {
52
48
  this._itemsRefMap = new Map();
53
49
  }
54
50
 
55
- private handleMouseEvent = (event: MouseEvent) => {
51
+ private _handleMouseEvent = (event: MouseEvent) => {
56
52
  if (!this._controlElementRef?.contains(event.target as Node | null)) {
57
- this.closePopover();
53
+ this._closePopover();
58
54
  }
59
55
  };
60
56
 
61
- private handleKeydownEvent = (event: KeyboardEvent) => {
57
+ private _handleKeydownEvent = (event: KeyboardEvent) => {
62
58
  const eventTarget = event.target as Node | null;
63
59
  if (
64
60
  this.state.isOpen &&
@@ -67,7 +63,7 @@ class PopoverMenu extends Component<PopoverMenuProps, PopoverMenuState> {
67
63
  !this._popoverElementRef?.contains(eventTarget) &&
68
64
  eventTarget !== this._controlElementRef
69
65
  ) {
70
- this.closePopover();
66
+ this._closePopover();
71
67
  }
72
68
  };
73
69
 
@@ -79,11 +75,11 @@ class PopoverMenu extends Component<PopoverMenuProps, PopoverMenuState> {
79
75
  this._getItemRef(currentIndex + 1)?.focus();
80
76
  };
81
77
 
82
- private closePopover() {
78
+ private _closePopover() {
83
79
  this.setState({isOpen: false});
84
80
  }
85
81
 
86
- private togglePopover = () => {
82
+ private _togglePopover = () => {
87
83
  const isOpen = !this.state.isOpen;
88
84
 
89
85
  this.setState({isOpen}, () => {
@@ -117,7 +113,7 @@ class PopoverMenu extends Component<PopoverMenuProps, PopoverMenuState> {
117
113
  <A11yWrapper
118
114
  onClick={e => {
119
115
  e.stopPropagation();
120
- this.togglePopover();
116
+ this._togglePopover();
121
117
  }}>
122
118
  <div
123
119
  tabIndex={0}
@@ -143,41 +139,22 @@ class PopoverMenu extends Component<PopoverMenuProps, PopoverMenuState> {
143
139
  this._popoverElementRef = node;
144
140
  }}>
145
141
  {this.state.isOpen
146
- ? items.map(({label, onClick, testId, isDisabled}, index) => {
147
- return (
148
- <A11yWrapper
149
- role="menuitem"
150
- onClick={() => {
151
- if (!isDisabled) {
152
- this.closePopover();
153
- onClick();
154
- }
155
- }}
156
- onDownKeyPressed={() => {
157
- if (!isDisabled) {
158
- this._handleDownKeyPressed(index);
159
- }
160
- }}
161
- onUpKeyPressed={() => {
162
- if (!isDisabled) {
163
- this._handleUpKeyPressed(index);
164
- }
165
- }}>
166
- {
167
- <div
168
- tabIndex={isDisabled ? -1 : 0}
169
- role="menuitem"
170
- className={`${styles.popoverMenuItem} ${isDisabled ? styles.popoverMenuItemDisabled : ''}`}
171
- data-testid={testId}
172
- ref={node => {
173
- this._setItemRef(index, node);
174
- }}>
175
- {label}
176
- </div>
142
+ ? items.map((item, index) => (
143
+ <PopoverMenuItem
144
+ key={index}
145
+ item={item}
146
+ index={index}
147
+ onKeyDown={this._handleDownKeyPressed}
148
+ onKeyUp={this._handleUpKeyPressed}
149
+ setRef={this._setItemRef}
150
+ onClick={() => {
151
+ this._closePopover();
152
+ if (!item.items) {
153
+ item.onClick?.();
177
154
  }
178
- </A11yWrapper>
179
- );
180
- })
155
+ }}
156
+ />
157
+ ))
181
158
  : null}
182
159
  </div>
183
160
  </div>
@@ -41,7 +41,7 @@ export interface SearchProps {
41
41
  prevMatchLabel?: string;
42
42
  searchResultsLabel?: string;
43
43
  eventManager?: any
44
- focusPluginButton: () => void;
44
+ focusPluginButton: (event: KeyboardEvent) => void;
45
45
  player?: any;
46
46
  }
47
47
 
@@ -52,15 +52,19 @@ class SearchComponent extends Component<SearchProps> {
52
52
 
53
53
  constructor(props: SearchProps) {
54
54
  super(props);
55
- this.props.eventManager?.listen(this.props.player, this.props.player.Event.FIRST_PLAY, () => {
55
+ if (this.props.player._firstPlay){
56
+ this.props.eventManager?.listen(this.props.player, this.props.player.Event.FIRST_PLAY, () => {
57
+ this.props.eventManager?.listen(document, 'keydown', this.handleKeydownEvent);
58
+ })
59
+ } else {
56
60
  this.props.eventManager?.listen(document, 'keydown', this.handleKeydownEvent);
57
- })
61
+ }
58
62
  }
59
63
 
60
64
  private handleKeydownEvent = (event: KeyboardEvent) => {
61
65
  if (event.keyCode === TAB && event.shiftKey && document.activeElement === this._inputField?.base?.childNodes[0]){
62
- event.preventDefault();
63
- this.props.focusPluginButton();
66
+ //@ts-ignore
67
+ this.props.focusPluginButton(event);
64
68
  }
65
69
  };
66
70
 
@@ -1,5 +1,5 @@
1
1
  import {h, Component} from 'preact';
2
- import {ui} from '@playkit-js/kaltura-player-js';
2
+ import {ui, core} from '@playkit-js/kaltura-player-js';
3
3
  import {debounce} from '../../utils';
4
4
  import * as styles from './transcript.scss';
5
5
  import {Spinner} from '../spinner';
@@ -70,7 +70,9 @@ export interface TranscriptProps {
70
70
  isMobile?: boolean;
71
71
  playerWidth?: number;
72
72
  onJumpToSearchMatch: () => void;
73
- focusPluginButton: () => void;
73
+ focusPluginButton: (event: KeyboardEvent) => void;
74
+ textTracks: Array<core.TextTrack>;
75
+ changeLanguage: (textTrack: core.TextTrack) => void;
74
76
  }
75
77
 
76
78
  interface TranscriptState {
@@ -303,7 +305,9 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
303
305
  attachTranscript,
304
306
  detachTranscript,
305
307
  onAttach,
306
- onDetach
308
+ onDetach,
309
+ textTracks,
310
+ changeLanguage
307
311
  } = this.props;
308
312
  const {search, activeSearchIndex, totalSearchResults} = this.state;
309
313
 
@@ -329,7 +333,9 @@ export class Transcript extends Component<TranscriptProps, TranscriptState> {
329
333
  focusPluginButton={this.props.focusPluginButton}
330
334
  />
331
335
  {this._renderJumpToSearchButton()}
332
- <TranscriptMenu {...{downloadDisabled, onDownload, printDisabled, onPrint, isLoading, detachMenuItem, kitchenSinkDetached}} />
336
+ <TranscriptMenu
337
+ {...{downloadDisabled, onDownload, printDisabled, onPrint, isLoading, detachMenuItem, kitchenSinkDetached, textTracks, changeLanguage}}
338
+ />
333
339
  {!detachMenuItem && this._renderDetachButton()}
334
340
  {!kitchenSinkDetached && (
335
341
  <div data-testid="transcriptCloseButton">
@@ -1,6 +1,8 @@
1
1
  import {Component, h} from 'preact';
2
+ import {core} from '@playkit-js/kaltura-player-js';
2
3
  import {PopoverMenu} from '../popover-menu';
3
4
  import {PopoverMenuItemData} from '../popover-menu';
5
+ import {capitalizeFirstLetter} from '../../utils';
4
6
 
5
7
  import {Button, ButtonType} from '@playkit-js/common/dist/components/button';
6
8
  const {withText, Text} = KalturaPlayer.ui.preacti18n;
@@ -15,6 +17,8 @@ interface TranscriptMenuProps {
15
17
  printDisabled?: boolean;
16
18
  isLoading?: boolean;
17
19
  kitchenSinkDetached: boolean;
20
+ textTracks: Array<core.TextTrack>;
21
+ changeLanguage: (textTrack: core.TextTrack) => void;
18
22
  detachMenuItem?: {
19
23
  testId: string;
20
24
  label: string;
@@ -45,10 +49,28 @@ class TranscriptMenu extends Component<TranscriptMenuProps, TranscriptMenuState>
45
49
  downloadTranscript,
46
50
  isLoading,
47
51
  detachMenuItem,
48
- kitchenSinkDetached
52
+ kitchenSinkDetached,
53
+ textTracks,
54
+ changeLanguage
49
55
  } = this.props;
50
56
  const items = [];
51
57
 
58
+ if (textTracks?.length > 1) {
59
+ const activeTextTrack = textTracks.find(track => track.active);
60
+ items.push({
61
+ testId: 'language-change-menu-item-active',
62
+ label: capitalizeFirstLetter(activeTextTrack?.label!),
63
+ isDisabled: isLoading,
64
+ items: textTracks.map(track => ({
65
+ testId: `language-change-menu-item-${track.label}`,
66
+ label: capitalizeFirstLetter(track.label!),
67
+ isDisabled: isLoading,
68
+ isSelected: activeTextTrack?.label === track.label,
69
+ onClick: () => changeLanguage(track)
70
+ }))
71
+ });
72
+ }
73
+
52
74
  if (detachMenuItem) {
53
75
  items.push(detachMenuItem);
54
76
  }
@@ -1,7 +1,7 @@
1
1
  import * as sanitizeHtml from 'sanitize-html';
2
2
  import {h} from 'preact';
3
3
  import {OnClickEvent} from '@playkit-js/common/dist/hoc/a11y-wrapper';
4
- import {ui} from '@playkit-js/kaltura-player-js';
4
+ import {ui, core} from '@playkit-js/kaltura-player-js';
5
5
  import {UpperBarManager, SidePanelsManager} from '@playkit-js/ui-managers';
6
6
  import {ObjectUtils, downloadContent, printContent, decodeString} from './utils';
7
7
  import {icons} from './components/icons';
@@ -14,7 +14,7 @@ import {AttachPlaceholder} from './components/attach-placeholder';
14
14
 
15
15
  export const pluginName: string = 'playkit-js-transcript';
16
16
 
17
- const {SidePanelModes, SidePanelPositions, ReservedPresetNames, ReservedPresetAreas} = ui;
17
+ const {SidePanelModes, SidePanelPositions, ReservedPresetNames} = ui;
18
18
  const {withText, Text} = KalturaPlayer.ui.preacti18n;
19
19
  const {get} = ObjectUtils;
20
20
 
@@ -78,6 +78,10 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
78
78
  return this._captionMap.get(this._activeCaptionMapId) || [];
79
79
  }
80
80
 
81
+ private get _state() {
82
+ return ui.redux.useStore().getState();
83
+ }
84
+
81
85
  loadMedia(): void {
82
86
  if (!this.cuePointManager || !this.sidePanelsManager || !this.upperBarManager) {
83
87
  this.logger.warn("kalturaCuepoints, sidePanelsManager or upperBarManager haven't registered");
@@ -285,6 +289,12 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
285
289
  this.dispatchEvent(TranscriptEvents.TRANSCRIPT_TO_SEARCH_MATCH);
286
290
  };
287
291
 
292
+ private _changeLanguage = (textTrack: core.TextTrack) => {
293
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
294
+ // @ts-expect-error - Property 'selectTrack' does not exist on type 'Player'
295
+ this.player.selectTrack(textTrack);
296
+ };
297
+
288
298
  private _addTranscriptItem(): void {
289
299
  if (Math.max(this._transcriptPanel, this._transcriptIcon, this._audioPlayerIconId) > 0) {
290
300
  // transcript panel or icon already exist
@@ -336,11 +346,15 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
336
346
  }}
337
347
  onJumpToSearchMatch={this._toSearchMatch}
338
348
  //@ts-ignore
339
- focusPluginButton={() => this.upperBarManager!.focusPluginButton(this._transcriptIcon)}
349
+ focusPluginButton={(event: KeyboardEvent) => this.upperBarManager!.focusPluginButton(this._transcriptIcon, event)}
350
+ textTracks={this._getTextTracks()}
351
+ changeLanguage={this._changeLanguage}
340
352
  />
341
353
  ) as any;
342
354
  },
343
- presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads, 'MiniAudioUI'],
355
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
356
+ // @ts-expect-error - Property 'MiniAudioUI' does not exist
357
+ presets: [ReservedPresetNames.Playback, ReservedPresetNames.Live, ReservedPresetNames.Ads, ReservedPresetNames.MiniAudioUI],
344
358
  position: position,
345
359
  expandMode: expandMode === SidePanelModes.ALONGSIDE ? SidePanelModes.ALONGSIDE : SidePanelModes.OVER
346
360
  }) as number;
@@ -348,11 +362,10 @@ export class TranscriptPlugin extends KalturaPlayer.core.BasePlugin {
348
362
  showTranscript: <Text id="transcript.show_plugin">Show Transcript</Text>,
349
363
  hideTranscript: <Text id="transcript.hide_plugin">Hide Transcript</Text>
350
364
  };
351
- // @ts-ignore
352
- if (ui.redux.useStore().getState().shell['activePresetName'] !== ReservedPresetNames.MiniAudioUI) {
365
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
366
+ // @ts-expect-error - Property 'MiniAudioUI' does not exist
367
+ if (this._state.shell['activePresetName'] !== ReservedPresetNames.MiniAudioUI) {
353
368
  this._transcriptIcon = this.upperBarManager!.add({
354
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
355
- // @ts-ignore
356
369
  displayName: 'Transcript',
357
370
  ariaLabel: 'Transcript',
358
371
  order: 30,
@@ -116,3 +116,7 @@ export function printContent(content: string): void {
116
116
  printWindow.close();
117
117
  }
118
118
  }
119
+
120
+ export function capitalizeFirstLetter(string: string): string {
121
+ return string.charAt(0).toUpperCase() + string.slice(1);
122
+ }