@playkit-js/transcript 3.6.0-canary.0-a940bfb → 3.6.0-canary.0-baa8e71
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 +61 -2
- package/dist/playkit-transcript.js +1 -1
- package/dist/playkit-transcript.js.map +1 -1
- package/package.json +9 -8
- package/src/components/attach-placeholder/attach-placeholder.tsx +2 -2
- package/src/components/caption/caption.scss +1 -1
- package/src/components/caption/caption.tsx +81 -19
- package/src/components/caption-list/captionList.tsx +31 -27
- package/src/components/plugin-button/plugin-button.tsx +1 -0
- package/src/components/popover-menu/popover-menu-item.tsx +108 -0
- package/src/components/popover-menu/popover-menu.scss +18 -7
- package/src/components/popover-menu/popover-menu.tsx +58 -68
- package/src/components/search/search.tsx +29 -0
- package/src/components/small-screen-slate/small-screen-slate.scss +1 -0
- package/src/components/transcript/transcript.scss +23 -2
- package/src/components/transcript/transcript.tsx +163 -42
- package/src/components/transcript-menu/transcript-menu.tsx +60 -12
- package/src/events/events.ts +4 -1
- package/src/transcript-plugin.tsx +85 -35
- package/src/utils/index.ts +0 -1
- package/src/utils/utils.ts +4 -0
- package/translations/ca_es.i18n.json +31 -0
- package/translations/en.i18n.json +11 -5
- package/src/utils/debounce.ts +0 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playkit-js/transcript",
|
|
3
|
-
"version": "3.6.0-canary.0-
|
|
3
|
+
"version": "3.6.0-canary.0-baa8e71",
|
|
4
4
|
"main": "dist/playkit-transcript.js",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"private": false,
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@playkit-js/kaltura-player-js": "3.17.9",
|
|
17
|
-
"@playkit-js/playkit-js-ui": "0.
|
|
18
|
-
"@playkit-js/ui-managers": "1.
|
|
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",
|
|
22
22
|
"cross-env": "^7.0.3",
|
|
23
23
|
"css-loader": "^6.7.1",
|
|
24
|
-
"cypress": "
|
|
24
|
+
"cypress": "13.13.1",
|
|
25
25
|
"playwright-webkit": "^1.33.0",
|
|
26
26
|
"prettier": "^2.6.2",
|
|
27
27
|
"rimraf": "^5.0.5",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"clean": "rimraf ./dist",
|
|
43
43
|
"prebuild": "npm run clean",
|
|
44
44
|
"build": "webpack --mode production",
|
|
45
|
-
"dev": "webpack
|
|
45
|
+
"dev": "webpack serve --open --mode development",
|
|
46
46
|
"watch": "webpack --progress --color --watch --mode development",
|
|
47
47
|
"cy:open": "cypress open",
|
|
48
48
|
"cy:run": "yarn run cy:run:chrome && yarn run cy:run:firefox && yarn run cy:run:edge && yarn run cy:run:safari",
|
|
@@ -77,15 +77,16 @@
|
|
|
77
77
|
"html5 player"
|
|
78
78
|
],
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@playkit-js/common": "1.5.
|
|
80
|
+
"@playkit-js/common": "1.5.23",
|
|
81
|
+
"@playkit-js/webpack-common": "^1.0.1-canary.0-dfd24a9",
|
|
81
82
|
"sanitize-html": "^2.11.0",
|
|
82
83
|
"stream-browserify": "^3.0.0"
|
|
83
84
|
},
|
|
84
85
|
"kaltura": {
|
|
85
86
|
"name": "playkit-js-transcript",
|
|
86
87
|
"dependencies": {
|
|
87
|
-
"playkit-kaltura-cuepoints": "3.0.
|
|
88
|
-
"playkit-ui-managers": "1.
|
|
88
|
+
"playkit-kaltura-cuepoints": "3.0.16",
|
|
89
|
+
"playkit-ui-managers": "1.7.0"
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -22,10 +22,10 @@ export const AttachPlaceholder = ({onAttach, onClose}: AttachPlaceholderProps) =
|
|
|
22
22
|
</div>
|
|
23
23
|
<div className={styles.attachContent}>
|
|
24
24
|
<div className={styles.attachText}>
|
|
25
|
-
<Text id="transcript.
|
|
25
|
+
<Text id="transcript.attach_transcript_text">Transcript popped out</Text>
|
|
26
26
|
</div>
|
|
27
27
|
<Button type={ButtonType.primary} size={ButtonSize.medium} className={styles.attachButton} onClick={onAttach} testId="transcriptAttachButton">
|
|
28
|
-
<Text id="transcript.
|
|
28
|
+
<Text id="transcript.attach_transcript_button">Bring it back</Text>
|
|
29
29
|
</Button>
|
|
30
30
|
</div>
|
|
31
31
|
</div>
|
|
@@ -4,18 +4,33 @@ import {secondsToTime} from '../../utils';
|
|
|
4
4
|
import {CuePointData} from '../../types';
|
|
5
5
|
import * as styles from './caption.scss';
|
|
6
6
|
|
|
7
|
+
import {TranscriptEvents} from '../../events/events';
|
|
8
|
+
|
|
9
|
+
//@ts-ignore
|
|
10
|
+
const {getDurationAsText} = KalturaPlayer.ui.utils;
|
|
11
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
12
|
+
const {withEventManager} = KalturaPlayer.ui.Event;
|
|
13
|
+
const {withPlayer} = KalturaPlayer.ui.components;
|
|
14
|
+
|
|
7
15
|
export interface CaptionProps {
|
|
8
16
|
showTime: boolean;
|
|
9
17
|
searchLength: number;
|
|
10
18
|
scrollTo(el: HTMLElement): void;
|
|
11
19
|
scrollToSearchMatch(el: HTMLElement): void;
|
|
12
20
|
videoDuration: number;
|
|
21
|
+
eventManager?: any;
|
|
22
|
+
player?: any;
|
|
23
|
+
captionLabel?: string;
|
|
24
|
+
moveToSearch?: string;
|
|
25
|
+
navigationInstruction?: string;
|
|
26
|
+
timeLabel?: string;
|
|
27
|
+
setTextToRead: (textToRead: string, delay?: number) => void;
|
|
13
28
|
}
|
|
14
29
|
|
|
15
30
|
interface ExtendedCaptionProps extends CaptionProps {
|
|
16
31
|
caption: CuePointData;
|
|
17
32
|
onClick: () => void;
|
|
18
|
-
|
|
33
|
+
isHighlighted: boolean;
|
|
19
34
|
shouldScroll: boolean;
|
|
20
35
|
shouldScrollToSearchMatch: boolean;
|
|
21
36
|
indexMap: Record<string, number> | undefined;
|
|
@@ -24,26 +39,67 @@ interface ExtendedCaptionProps extends CaptionProps {
|
|
|
24
39
|
isAutoScrollEnabled: boolean;
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
const translates = {
|
|
43
|
+
captionLabel: <Text id="transcript.caption_label">Jump to this point in video</Text>,
|
|
44
|
+
moveToSearch: <Text id="transcript.move_to_search">Click to jump to search result</Text>,
|
|
45
|
+
navigationInstruction: (
|
|
46
|
+
<Text id="transcript.navigation_instruction">
|
|
47
|
+
Press Home to navigate to the beginning of the transcript. Press End to jump to the end of the transcript.
|
|
48
|
+
</Text>
|
|
49
|
+
),
|
|
50
|
+
timeLabel: <Text id="transcript.time_label">Timestamp</Text>
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
@withText(translates)
|
|
54
|
+
@withEventManager
|
|
55
|
+
@withPlayer
|
|
27
56
|
export class Caption extends Component<ExtendedCaptionProps> {
|
|
28
|
-
private
|
|
57
|
+
private _captionRef: HTMLElement | null = null;
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
if (this.
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
59
|
+
get indexArray() {
|
|
60
|
+
if (!this.props.indexMap) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return Object.keys(this.props.indexMap).sort((a, b) => Number(a) - Number(b));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
componentDidUpdate(previousProps: Readonly<ExtendedCaptionProps>) {
|
|
67
|
+
if (this._captionRef && this.props.shouldScroll) {
|
|
68
|
+
this.props.scrollTo(this._captionRef);
|
|
69
|
+
} else if (this._captionRef && this.props.shouldScrollToSearchMatch) {
|
|
70
|
+
this.props.scrollToSearchMatch(this._captionRef);
|
|
71
|
+
}
|
|
72
|
+
if (this.props.indexMap && previousProps.activeSearchIndex !== this.props.activeSearchIndex) {
|
|
73
|
+
if (this._hasSearchMatch()) {
|
|
74
|
+
this.props.setTextToRead(this.props.caption.text);
|
|
75
|
+
}
|
|
35
76
|
}
|
|
36
77
|
}
|
|
37
78
|
|
|
79
|
+
componentDidMount(): void {
|
|
80
|
+
const {eventManager, player} = this.props;
|
|
81
|
+
eventManager?.listen(player, TranscriptEvents.TRANSCRIPT_TO_SEARCH_MATCH, () => {
|
|
82
|
+
if (this._hasSearchMatch()) {
|
|
83
|
+
this._captionRef?.focus();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
eventManager?.listen(player, TranscriptEvents.TRANSCRIPT_SCROLLING, () => {
|
|
88
|
+
if (this._captionRef && this._hasSearchMatch()) {
|
|
89
|
+
this.props.scrollTo(this._captionRef);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
38
94
|
shouldComponentUpdate(nextProps: ExtendedCaptionProps) {
|
|
39
|
-
const {indexMap,
|
|
95
|
+
const {indexMap, isHighlighted, isAutoScrollEnabled, activeSearchIndex, longerThanHour} = this.props;
|
|
40
96
|
if (longerThanHour !== nextProps.longerThanHour) {
|
|
41
97
|
return true;
|
|
42
98
|
}
|
|
43
|
-
if (
|
|
99
|
+
if (isHighlighted !== nextProps.isHighlighted) {
|
|
44
100
|
return true;
|
|
45
101
|
}
|
|
46
|
-
if (
|
|
102
|
+
if (isHighlighted && isAutoScrollEnabled !== nextProps.isAutoScrollEnabled) {
|
|
47
103
|
return true;
|
|
48
104
|
}
|
|
49
105
|
if (indexMap !== nextProps.indexMap) {
|
|
@@ -66,12 +122,16 @@ export class Caption extends Component<ExtendedCaptionProps> {
|
|
|
66
122
|
}
|
|
67
123
|
};
|
|
68
124
|
|
|
125
|
+
private _hasSearchMatch = () => {
|
|
126
|
+
if (!this.props.indexMap) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return Boolean(this.indexArray.find((el: string) => Number(el) === this.props.activeSearchIndex));
|
|
130
|
+
};
|
|
131
|
+
|
|
69
132
|
private _renderText = (text: string) => {
|
|
70
133
|
const {activeSearchIndex, searchLength, indexMap} = this.props;
|
|
71
|
-
|
|
72
|
-
if (indexMap) {
|
|
73
|
-
indexArray = Object.keys(indexMap).sort((a, b) => Number(a) - Number(b));
|
|
74
|
-
}
|
|
134
|
+
const indexArray = this.indexArray;
|
|
75
135
|
if (text?.length === 0) {
|
|
76
136
|
return null;
|
|
77
137
|
}
|
|
@@ -99,15 +159,17 @@ export class Caption extends Component<ExtendedCaptionProps> {
|
|
|
99
159
|
};
|
|
100
160
|
|
|
101
161
|
render() {
|
|
102
|
-
const {caption,
|
|
103
|
-
|
|
104
|
-
const
|
|
162
|
+
const {caption, isHighlighted, showTime, longerThanHour, indexMap, captionLabel, moveToSearch, navigationInstruction, timeLabel, player} =
|
|
163
|
+
this.props;
|
|
164
|
+
const {startTime} = caption;
|
|
105
165
|
const time = showTime ? secondsToTime(startTime, longerThanHour) : '';
|
|
106
166
|
|
|
107
167
|
const captionA11yProps: Record<string, any> = {
|
|
108
168
|
ariaCurrent: isHighlighted,
|
|
109
169
|
tabIndex: 0,
|
|
110
|
-
ariaLabel: `${
|
|
170
|
+
ariaLabel: `${showTime ? `${timeLabel} ${getDurationAsText(Math.floor(startTime), player?.config.ui.locale, true)} ` : ''}${caption.text} ${
|
|
171
|
+
indexMap ? moveToSearch : captionLabel
|
|
172
|
+
}. ${navigationInstruction}`,
|
|
111
173
|
role: 'button'
|
|
112
174
|
};
|
|
113
175
|
|
|
@@ -116,7 +178,7 @@ export class Caption extends Component<ExtendedCaptionProps> {
|
|
|
116
178
|
<div
|
|
117
179
|
className={styles.caption}
|
|
118
180
|
ref={node => {
|
|
119
|
-
this.
|
|
181
|
+
this._captionRef = node;
|
|
120
182
|
}}
|
|
121
183
|
{...captionA11yProps}>
|
|
122
184
|
{showTime && (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {h, Component} from 'preact';
|
|
2
|
+
import {ScreenReaderContext} from '@playkit-js/common/dist/hoc/sr-wrapper';
|
|
2
3
|
import {HOUR} from '../../utils';
|
|
3
4
|
import {Caption} from '../caption';
|
|
4
5
|
import * as styles from './captionList.scss';
|
|
@@ -19,7 +20,6 @@ export interface Props {
|
|
|
19
20
|
onSeek: (n: CuePointData) => void;
|
|
20
21
|
autoScroll?: boolean;
|
|
21
22
|
onScroll: (n: number) => void;
|
|
22
|
-
widgetWidth: number;
|
|
23
23
|
highlightedMap: HighlightedMap;
|
|
24
24
|
showItemsIcons: boolean;
|
|
25
25
|
listDataContainCaptions?: boolean;
|
|
@@ -84,12 +84,12 @@ export class CaptionList extends Component<Props> {
|
|
|
84
84
|
key: id,
|
|
85
85
|
onClick: this._handleClick(captionData),
|
|
86
86
|
caption: captionData,
|
|
87
|
-
|
|
87
|
+
isHighlighted: Boolean(highlightedMap[id]),
|
|
88
88
|
longerThanHour: captionProps.videoDuration >= HOUR,
|
|
89
89
|
shouldScroll: this._getShouldScroll(id),
|
|
90
90
|
shouldScrollToSearchMatch: this._getShouldScrollToSearchMatch(id),
|
|
91
91
|
isAutoScrollEnabled,
|
|
92
|
-
searchCaption: this.props.searchMap[
|
|
92
|
+
searchCaption: this.props.searchMap[id],
|
|
93
93
|
...this._getSearchProps(id)
|
|
94
94
|
};
|
|
95
95
|
return newCaptionProps;
|
|
@@ -97,41 +97,45 @@ export class CaptionList extends Component<Props> {
|
|
|
97
97
|
|
|
98
98
|
private _handleKeyUp = (event: KeyboardEvent) => {
|
|
99
99
|
if (event.keyCode === END) {
|
|
100
|
-
this._lastCaptionRef?.
|
|
100
|
+
this._lastCaptionRef?.base.focus();
|
|
101
101
|
} else if (event.keyCode === HOME) {
|
|
102
|
-
this._firstCaptionRef?.
|
|
102
|
+
this._firstCaptionRef?.base.focus();
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
render() {
|
|
107
|
-
const {data} = this.props;
|
|
108
|
-
let
|
|
107
|
+
const {data, highlightedMap} = this.props;
|
|
108
|
+
let isSearchCaptionInList = false;
|
|
109
109
|
return (
|
|
110
110
|
<div className={styles.transcriptWrapper} onKeyUp={this._handleKeyUp}>
|
|
111
111
|
{data.map((captionData, index) => {
|
|
112
112
|
const captionProps = this._getCaptionProps(captionData);
|
|
113
|
+
let isSearchCaption = false;
|
|
114
|
+
if (captionProps.searchCaption && Object.keys(captionProps.searchCaption).some(key => parseInt(key) === this.props.activeSearchIndex)) {
|
|
115
|
+
isSearchCaptionInList = true;
|
|
116
|
+
isSearchCaption = true;
|
|
117
|
+
}
|
|
113
118
|
return (
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
isSearchCaption
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
119
|
+
<ScreenReaderContext.Consumer key={captionData.id}>
|
|
120
|
+
{(setTextToRead: (textToRead: string, delay?: number | undefined) => void) => {
|
|
121
|
+
return (
|
|
122
|
+
<Caption
|
|
123
|
+
setTextToRead={setTextToRead}
|
|
124
|
+
ref={node => {
|
|
125
|
+
if (index === 0) {
|
|
126
|
+
this._firstCaptionRef = node;
|
|
127
|
+
} else if (index === data.length - 1) {
|
|
128
|
+
this._lastCaptionRef = node;
|
|
129
|
+
}
|
|
130
|
+
if (isSearchCaption || (!isSearchCaptionInList && captionProps.isHighlighted)) {
|
|
131
|
+
this._currentCaptionRef = node;
|
|
132
|
+
}
|
|
133
|
+
}}
|
|
134
|
+
{...captionProps}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
132
137
|
}}
|
|
133
|
-
|
|
134
|
-
/>
|
|
138
|
+
</ScreenReaderContext.Consumer>
|
|
135
139
|
);
|
|
136
140
|
})}
|
|
137
141
|
</div>
|
|
@@ -24,6 +24,7 @@ export const PluginButton = ({isActive, label, id, icon, dataTestId, setRef}: Pl
|
|
|
24
24
|
setRef(node);
|
|
25
25
|
}
|
|
26
26
|
}}
|
|
27
|
+
tabIndex={0}
|
|
27
28
|
type="button"
|
|
28
29
|
aria-label={label}
|
|
29
30
|
className={[ui.style.upperBarIcon, styles.pluginButton, isActive ? styles.active : ''].join(' ')}
|
|
@@ -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,21 @@
|
|
|
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
|
+
max-height: calc(100% * 5.8); // height - almost 6 menu items like in player caption menu
|
|
40
|
+
overflow-y: auto;
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
.popover-menu-item {
|
|
45
|
+
position: relative;
|
|
35
46
|
align-items: center;
|
|
36
47
|
display: flex;
|
|
48
|
+
justify-content: space-between;
|
|
37
49
|
font-size: 15px;
|
|
38
50
|
line-height: 18px;
|
|
39
51
|
min-height: 30px;
|
|
@@ -44,13 +56,13 @@
|
|
|
44
56
|
&.popover-menu-item-disabled {
|
|
45
57
|
color: $tone-4-color;
|
|
46
58
|
}
|
|
47
|
-
|
|
59
|
+
|
|
48
60
|
&:hover:not(.popover-menu-item-disabled) {
|
|
49
61
|
background-color: $tone-6-color;
|
|
50
62
|
border-radius: $roundness-1;
|
|
51
63
|
cursor: pointer;
|
|
52
64
|
}
|
|
53
|
-
|
|
65
|
+
|
|
54
66
|
&:focus {
|
|
55
67
|
outline: 1px solid #222;
|
|
56
68
|
}
|
|
@@ -60,4 +72,3 @@
|
|
|
60
72
|
padding-top: 6px;
|
|
61
73
|
padding-bottom: 6px;
|
|
62
74
|
}
|
|
63
|
-
|