@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playkit-js/transcript",
|
|
3
|
-
"version": "3.0.1",
|
|
3
|
+
"version": "3.0.2-canary.1-f9e6d51",
|
|
4
4
|
"main": "dist/playkit-transcript.js",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"private": false,
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"html5 player"
|
|
76
76
|
],
|
|
77
77
|
"dependencies": {
|
|
78
|
-
"@playkit-js/common": "^1.0.
|
|
78
|
+
"@playkit-js/common": "^1.0.7",
|
|
79
79
|
"stream-browserify": "^3.0.0"
|
|
80
80
|
},
|
|
81
81
|
"kaltura": {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {Component, h} from 'preact';
|
|
2
|
+
import {A11yWrapper, OnClickEvent} from '@playkit-js/common';
|
|
2
3
|
import {secontsToTime} from '../../utils';
|
|
3
4
|
import {CuePointData} from '../../types';
|
|
4
|
-
|
|
5
|
-
const {ENTER, Space} = KalturaPlayer.ui.utils.KeyMap;
|
|
6
|
-
|
|
7
|
-
import {Component, h} from 'preact';
|
|
5
|
+
import * as styles from './caption.scss';
|
|
8
6
|
|
|
9
7
|
export interface CaptionProps {
|
|
10
8
|
showTime: boolean;
|
|
@@ -57,12 +55,10 @@ export class Caption extends Component<ExtendedCaptionProps> {
|
|
|
57
55
|
return false;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
if (keyCode === ENTER || keyCode === Space) {
|
|
58
|
+
private _handleKeyDown = (event: OnClickEvent, byKeyboard?: boolean) => {
|
|
59
|
+
if (byKeyboard) {
|
|
63
60
|
event.preventDefault();
|
|
64
61
|
this._gotoCurrentTime();
|
|
65
|
-
return;
|
|
66
62
|
}
|
|
67
63
|
};
|
|
68
64
|
|
|
@@ -117,23 +113,23 @@ export class Caption extends Component<ExtendedCaptionProps> {
|
|
|
117
113
|
const isHighlighted = Object.keys(highlighted)[0] === id;
|
|
118
114
|
|
|
119
115
|
return (
|
|
120
|
-
<
|
|
121
|
-
className={styles.caption}
|
|
122
|
-
tabIndex={1}
|
|
123
|
-
ref={node => {
|
|
124
|
-
this._hotspotRef = node;
|
|
125
|
-
}}
|
|
126
|
-
onKeyDown={this._handleKeyPress}
|
|
127
|
-
>
|
|
128
|
-
{showTime && <div className={styles.captionTime}>{secontsToTime(startTime, longerThanHour)}</div>}
|
|
116
|
+
<A11yWrapper onClick={this._handleKeyDown}>
|
|
129
117
|
<div
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
className={styles.caption}
|
|
119
|
+
tabIndex={1}
|
|
120
|
+
area-label={caption.text}
|
|
121
|
+
ref={node => {
|
|
122
|
+
this._hotspotRef = node;
|
|
123
|
+
}}
|
|
124
|
+
role="listitem">
|
|
125
|
+
{showTime && <div className={styles.captionTime}>{secontsToTime(startTime, longerThanHour)}</div>}
|
|
126
|
+
<div
|
|
127
|
+
onClick={this._handleClick}
|
|
128
|
+
className={`${styles.captionContent} ${isHighlighted ? styles.highlighted : ''} ${showTime ? '' : styles.withoutTime}`}>
|
|
129
|
+
{this._renderText(caption.text)}
|
|
130
|
+
</div>
|
|
135
131
|
</div>
|
|
136
|
-
</
|
|
132
|
+
</A11yWrapper>
|
|
137
133
|
);
|
|
138
134
|
}
|
|
139
135
|
}
|
|
@@ -4,7 +4,7 @@ import {Caption} from '../caption';
|
|
|
4
4
|
import * as styles from './captionList.scss';
|
|
5
5
|
import {HighlightedMap, CuePointData} from '../../types';
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const {END, HOME} = KalturaPlayer.ui.utils.KeyMap;
|
|
8
8
|
|
|
9
9
|
export interface CaptionProps {
|
|
10
10
|
showTime: boolean;
|
|
@@ -95,9 +95,9 @@ export class CaptionList extends Component<Props> {
|
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
private _handleKeyUp = (event: KeyboardEvent) => {
|
|
98
|
-
if (event.keyCode ===
|
|
98
|
+
if (event.keyCode === END) {
|
|
99
99
|
this._lastCaptionRef?._hotspotRef?.focus();
|
|
100
|
-
} else if (event.keyCode ===
|
|
100
|
+
} else if (event.keyCode === HOME) {
|
|
101
101
|
this._firstCaptionRef?._hotspotRef?.focus();
|
|
102
102
|
}
|
|
103
103
|
};
|
|
@@ -105,7 +105,7 @@ export class CaptionList extends Component<Props> {
|
|
|
105
105
|
render() {
|
|
106
106
|
const {data} = this.props;
|
|
107
107
|
return (
|
|
108
|
-
<div className={styles.transcriptWrapper} onKeyUp={this._handleKeyUp}>
|
|
108
|
+
<div className={styles.transcriptWrapper} onKeyUp={this._handleKeyUp} role="list">
|
|
109
109
|
{data.map((captionData, index) => {
|
|
110
110
|
const captionProps = this._getCaptionProps(captionData);
|
|
111
111
|
return (
|
|
@@ -116,7 +116,7 @@ export class CaptionList extends Component<Props> {
|
|
|
116
116
|
} else if (index === data.length - 1) {
|
|
117
117
|
this._lastCaptionRef = node;
|
|
118
118
|
}
|
|
119
|
-
if (captionProps.highlighted) {
|
|
119
|
+
if (captionProps.highlighted[captionData.id]) {
|
|
120
120
|
this._currentCaptionRef = node;
|
|
121
121
|
}
|
|
122
122
|
}}
|
|
@@ -3,14 +3,20 @@ import * as styles from './close-button.scss';
|
|
|
3
3
|
import {A11yWrapper} from '@playkit-js/common';
|
|
4
4
|
import {icons} from '../icons';
|
|
5
5
|
const {Icon} = KalturaPlayer.ui.components;
|
|
6
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
7
|
+
|
|
8
|
+
const translates = {
|
|
9
|
+
closeLabel: <Text id="transcript.hide_plugin">Hide Transcript</Text>
|
|
10
|
+
};
|
|
6
11
|
|
|
7
12
|
interface CloseButtonProps {
|
|
8
13
|
onClick: () => void;
|
|
14
|
+
closeLabel?: string;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
export const CloseButton = (props: CloseButtonProps) => (
|
|
17
|
+
export const CloseButton = withText(translates)((props: CloseButtonProps) => (
|
|
12
18
|
<A11yWrapper onClick={props.onClick}>
|
|
13
|
-
<button className={styles.closeBtn} tabIndex={1}>
|
|
19
|
+
<button className={styles.closeBtn} tabIndex={1} aria-label={props.closeLabel}>
|
|
14
20
|
<Icon
|
|
15
21
|
id="transcript-plugin-close-button"
|
|
16
22
|
height={icons.BigSize}
|
|
@@ -20,4 +26,4 @@ export const CloseButton = (props: CloseButtonProps) => (
|
|
|
20
26
|
/>
|
|
21
27
|
</button>
|
|
22
28
|
</A11yWrapper>
|
|
23
|
-
);
|
|
29
|
+
));
|
|
@@ -26,19 +26,3 @@
|
|
|
26
26
|
background-size: cover;
|
|
27
27
|
}
|
|
28
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,10 +1,11 @@
|
|
|
1
|
-
import {h, Component,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import {h, Component, VNode} from 'preact';
|
|
2
|
+
import {A11yWrapper} from '@playkit-js/common';
|
|
3
|
+
import {Popover, PopoverMenuItem} from '../popover';
|
|
4
4
|
import * as styles from './download-print-menu.scss';
|
|
5
5
|
import {DownloadIcon, PrintIcon} from './download-print-icons';
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const {Tooltip} = KalturaPlayer.ui.components;
|
|
8
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
8
9
|
|
|
9
10
|
export function downloadContent(content: string, name: string): void {
|
|
10
11
|
const blob = new Blob([content], {type: 'text/plain;charset=utf-8;'});
|
|
@@ -29,7 +30,6 @@ export function downloadContent(content: string, name: string): void {
|
|
|
29
30
|
anchor.click();
|
|
30
31
|
anchor.remove();
|
|
31
32
|
}
|
|
32
|
-
|
|
33
33
|
export function printContent(content: string): void {
|
|
34
34
|
const printWindow = window.open('', '', 'width=400,height=600');
|
|
35
35
|
if (printWindow) {
|
|
@@ -42,9 +42,9 @@ export function printContent(content: string): void {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
interface DownloadPrintMenuProps {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
printDownloadAreaLabel?: string;
|
|
46
|
+
printTranscript?: string;
|
|
47
|
+
downloadTranscript?: string;
|
|
48
48
|
onDownload: () => void;
|
|
49
49
|
onPrint: () => void;
|
|
50
50
|
downloadDisabled: boolean;
|
|
@@ -52,113 +52,77 @@ interface DownloadPrintMenuProps {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
interface ButtonProperties {
|
|
55
|
-
['aria-label']
|
|
56
|
-
buttonStyles: string;
|
|
57
|
-
onClick?: () => void;
|
|
55
|
+
['aria-label']?: string;
|
|
58
56
|
tabIndex?: number;
|
|
59
|
-
iconStyles: string;
|
|
60
57
|
icon: VNode;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
);
|
|
60
|
+
const translates = () => ({
|
|
61
|
+
printDownloadAreaLabel: <Text id="transcript.print_download_area_label">Download or print current transcript</Text>,
|
|
62
|
+
printTranscript: <Text id="transcript.print_transcript">Print current transcript</Text>,
|
|
63
|
+
downloadTranscript: <Text id="transcript.download_transcript">Download current transcript</Text>
|
|
64
|
+
});
|
|
100
65
|
|
|
101
|
-
|
|
66
|
+
export class DownloadPrintMenuComponent extends Component<DownloadPrintMenuProps> {
|
|
67
|
+
private _getPopoverMenuOptions = (): Array<PopoverMenuItem> => {
|
|
102
68
|
return [
|
|
103
69
|
{
|
|
104
|
-
label: this.props.
|
|
105
|
-
onMenuChosen: this.
|
|
70
|
+
label: this.props.downloadTranscript,
|
|
71
|
+
onMenuChosen: this.props.onDownload
|
|
106
72
|
},
|
|
107
73
|
{
|
|
108
|
-
label: this.props.
|
|
109
|
-
onMenuChosen: this.
|
|
74
|
+
label: this.props.printTranscript,
|
|
75
|
+
onMenuChosen: this.props.onPrint
|
|
110
76
|
}
|
|
111
77
|
];
|
|
112
78
|
};
|
|
113
79
|
|
|
114
|
-
private _renderIcon = ({
|
|
80
|
+
private _renderIcon = ({tabIndex = 0, icon, ...props}: ButtonProperties): VNode => {
|
|
115
81
|
return (
|
|
116
|
-
<button className={
|
|
117
|
-
<div className={
|
|
82
|
+
<button className={styles.downloadPrintButton} tabIndex={tabIndex} {...props}>
|
|
83
|
+
<div className={styles.icon}>{icon}</div>
|
|
118
84
|
</button>
|
|
119
85
|
);
|
|
120
86
|
};
|
|
121
87
|
|
|
122
|
-
private _popoverContent = () => {
|
|
123
|
-
return <PopoverMenu itemRenderer={this._popoverMenuItemRenderer} options={this._getPopoverMenuOptions()} />;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
88
|
render(props: DownloadPrintMenuProps) {
|
|
127
|
-
const {downloadDisabled, printDisabled} = props;
|
|
89
|
+
const {downloadDisabled, printDisabled, printDownloadAreaLabel, printTranscript, downloadTranscript} = props;
|
|
128
90
|
if (!downloadDisabled && !printDisabled) {
|
|
129
91
|
return (
|
|
130
|
-
<Popover
|
|
131
|
-
className="download-print-popover"
|
|
132
|
-
verticalPosition={PopoverVerticalPositions.Bottom}
|
|
133
|
-
horizontalPosition={PopoverHorizontalPositions.Left}
|
|
134
|
-
content={this._popoverContent()}>
|
|
92
|
+
<Popover label={printDownloadAreaLabel!} options={this._getPopoverMenuOptions()}>
|
|
135
93
|
{this._renderIcon({
|
|
136
|
-
['aria-label']:
|
|
137
|
-
buttonStyles: styles.downloadPrintButton,
|
|
138
|
-
iconStyles: styles.icon,
|
|
94
|
+
['aria-label']: printDownloadAreaLabel,
|
|
139
95
|
icon: <DownloadIcon />
|
|
140
96
|
})}
|
|
141
97
|
</Popover>
|
|
142
98
|
);
|
|
143
99
|
}
|
|
144
100
|
if (!downloadDisabled && printDisabled) {
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
101
|
+
return (
|
|
102
|
+
<Tooltip label={downloadTranscript} type="bottom">
|
|
103
|
+
<A11yWrapper onClick={this.props.onDownload}>
|
|
104
|
+
{this._renderIcon({
|
|
105
|
+
['aria-label']: downloadTranscript,
|
|
106
|
+
icon: <DownloadIcon />
|
|
107
|
+
})}
|
|
108
|
+
</A11yWrapper>
|
|
109
|
+
</Tooltip>
|
|
110
|
+
);
|
|
152
111
|
}
|
|
153
112
|
if (downloadDisabled && !printDisabled) {
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
113
|
+
return (
|
|
114
|
+
<Tooltip label={printTranscript} type="bottom">
|
|
115
|
+
<A11yWrapper onClick={this.props.onPrint}>
|
|
116
|
+
{this._renderIcon({
|
|
117
|
+
['aria-label']: printTranscript,
|
|
118
|
+
icon: <PrintIcon />
|
|
119
|
+
})}
|
|
120
|
+
</A11yWrapper>
|
|
121
|
+
</Tooltip>
|
|
122
|
+
);
|
|
161
123
|
}
|
|
162
124
|
return null;
|
|
163
125
|
}
|
|
164
126
|
}
|
|
127
|
+
|
|
128
|
+
export const DownloadPrintMenu = withText(translates)(DownloadPrintMenuComponent);
|
|
@@ -2,7 +2,15 @@ import {h} from 'preact';
|
|
|
2
2
|
import * as styles from './plugin-button.scss';
|
|
3
3
|
import {icons} from '../icons';
|
|
4
4
|
import {A11yWrapper, OnClick} from '@playkit-js/common';
|
|
5
|
+
|
|
5
6
|
const {Tooltip, Icon} = KalturaPlayer.ui.components;
|
|
7
|
+
const {withText, Text} = KalturaPlayer.ui.preacti18n;
|
|
8
|
+
|
|
9
|
+
const translates = ({isActive}: PluginButtonProps) => {
|
|
10
|
+
return {
|
|
11
|
+
label: isActive ? <Text id="transcript.hide_plugin">Hide Transcript</Text> : <Text id="transcript.show_plugin">Show Transcript</Text>
|
|
12
|
+
};
|
|
13
|
+
};
|
|
6
14
|
|
|
7
15
|
interface PluginButtonProps {
|
|
8
16
|
isActive: boolean;
|
|
@@ -10,7 +18,7 @@ interface PluginButtonProps {
|
|
|
10
18
|
label?: string;
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
export const PluginButton = ({isActive, onClick, label}: PluginButtonProps) => {
|
|
21
|
+
export const PluginButton = withText(translates)(({isActive, onClick, label}: PluginButtonProps) => {
|
|
14
22
|
return (
|
|
15
23
|
<Tooltip label={label} type="bottom">
|
|
16
24
|
<A11yWrapper onClick={onClick}>
|
|
@@ -26,4 +34,4 @@ export const PluginButton = ({isActive, onClick, label}: PluginButtonProps) => {
|
|
|
26
34
|
</A11yWrapper>
|
|
27
35
|
</Tooltip>
|
|
28
36
|
);
|
|
29
|
-
};
|
|
37
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./popover";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
@import '../../variables.scss';
|
|
2
|
+
|
|
3
|
+
.popover-container {
|
|
4
|
+
position: relative;
|
|
5
|
+
.popover-component {
|
|
6
|
+
background-color: #222222;
|
|
7
|
+
border-radius: 4px;
|
|
8
|
+
position: absolute;
|
|
9
|
+
right: 0px;
|
|
10
|
+
font-size: 15px;
|
|
11
|
+
display: block;
|
|
12
|
+
&.hidden {
|
|
13
|
+
display: none;
|
|
14
|
+
}
|
|
15
|
+
&.bottom {
|
|
16
|
+
top: 100%;
|
|
17
|
+
margin-top: 6px;
|
|
18
|
+
}
|
|
19
|
+
&.left {
|
|
20
|
+
right: 0px;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
.popover-menu {
|
|
25
|
+
padding-top: 6px;
|
|
26
|
+
padding-bottom: 6px;
|
|
27
|
+
}
|
|
28
|
+
.popover-menu-item {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
padding: 9px 24px 9px 16px;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
min-height: 30px;
|
|
34
|
+
line-height: 18px;
|
|
35
|
+
font-size: 15px;
|
|
36
|
+
&:hover {
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
background-color: $semigray-color;
|
|
39
|
+
}
|
|
40
|
+
&:focus {
|
|
41
|
+
outline: 1px solid $focus-color;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {h, Component, VNode} from 'preact';
|
|
2
|
+
import {A11yWrapper, OnClickEvent} from '@playkit-js/common';
|
|
3
|
+
import * as styles from './popover.scss';
|
|
4
|
+
|
|
5
|
+
const {Tooltip} = KalturaPlayer.ui.components;
|
|
6
|
+
const {ENTER, ESC, SPACE, TAB} = KalturaPlayer.ui.utils.KeyMap;
|
|
7
|
+
|
|
8
|
+
export interface PopoverMenuItem {
|
|
9
|
+
label?: string;
|
|
10
|
+
onMenuChosen: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface PopoverProps {
|
|
14
|
+
label: string;
|
|
15
|
+
options: Array<PopoverMenuItem>;
|
|
16
|
+
children: VNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface PopoverState {
|
|
20
|
+
open: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class Popover extends Component<PopoverProps, PopoverState> {
|
|
24
|
+
private _controlElementRef: HTMLDivElement | null = null;
|
|
25
|
+
private _popoverElementRef: HTMLDivElement | null = null;
|
|
26
|
+
private _firstOptionElementRef: HTMLDivElement | null = null;
|
|
27
|
+
|
|
28
|
+
state = {
|
|
29
|
+
open: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
componentWillUnmount() {
|
|
33
|
+
this._removeListeners();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private _handleMouseEvent = (event: MouseEvent) => {
|
|
37
|
+
if (!this._controlElementRef?.contains(event.target as Node | null)) {
|
|
38
|
+
this._closePopover();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
private _handleKeyboardEvent = (event: KeyboardEvent) => {
|
|
43
|
+
if (this._controlElementRef?.contains(event.target as Node | null) && [ENTER, SPACE].includes(event.keyCode)) {
|
|
44
|
+
// use handler of control element
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (this._popoverElementRef?.contains(event.target as Node | null) && event.keyCode !== ESC) {
|
|
49
|
+
// use handler of popover element
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this._closePopover();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
private _openPopover = (byKeyboard?: boolean) => {
|
|
56
|
+
this.setState({open: true});
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
// add listeners after component got re-render
|
|
59
|
+
this._addListeners();
|
|
60
|
+
if (byKeyboard && this._firstOptionElementRef) {
|
|
61
|
+
this._firstOptionElementRef.focus();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
private _closePopover = () => {
|
|
67
|
+
this._removeListeners();
|
|
68
|
+
this.setState({open: false});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
private _togglePopover = (e: MouseEvent | KeyboardEvent, byKeyboard?: boolean) => {
|
|
72
|
+
if (this.state.open) {
|
|
73
|
+
this._closePopover();
|
|
74
|
+
} else {
|
|
75
|
+
this._openPopover(byKeyboard);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
private _addListeners = () => {
|
|
79
|
+
document.addEventListener('click', this._handleMouseEvent);
|
|
80
|
+
document.addEventListener('keydown', this._handleKeyboardEvent);
|
|
81
|
+
};
|
|
82
|
+
private _removeListeners = () => {
|
|
83
|
+
document.removeEventListener('click', this._handleMouseEvent);
|
|
84
|
+
document.removeEventListener('keydown', this._handleKeyboardEvent);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
private _handleClickOnOptoin = (cb: () => void) => (event: OnClickEvent, byKeyboard?: boolean) => {
|
|
88
|
+
this._closePopover();
|
|
89
|
+
cb();
|
|
90
|
+
if (byKeyboard) {
|
|
91
|
+
setTimeout(() => {});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
render(props: PopoverProps) {
|
|
95
|
+
const {open} = this.state;
|
|
96
|
+
const content = <A11yWrapper onClick={this._togglePopover}>{props.children}</A11yWrapper>;
|
|
97
|
+
return (
|
|
98
|
+
<div className={styles.popoverContainer}>
|
|
99
|
+
<div
|
|
100
|
+
className="popover-anchor-container"
|
|
101
|
+
ref={node => {
|
|
102
|
+
this._controlElementRef = node;
|
|
103
|
+
}}>
|
|
104
|
+
{open ? (
|
|
105
|
+
content
|
|
106
|
+
) : (
|
|
107
|
+
<Tooltip label={props.label} type="bottom">
|
|
108
|
+
{content}
|
|
109
|
+
</Tooltip>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
<div
|
|
113
|
+
aria-expanded={open}
|
|
114
|
+
ref={node => {
|
|
115
|
+
this._popoverElementRef = node;
|
|
116
|
+
}}
|
|
117
|
+
className={['popover', styles.popoverComponent, styles.bottom, styles.left, open ? '' : styles.hidden].join(' ')}>
|
|
118
|
+
<div className={styles.popoverMenu} role="menu">
|
|
119
|
+
{props.options.map((el, index) => {
|
|
120
|
+
return (
|
|
121
|
+
<A11yWrapper onClick={this._handleClickOnOptoin(el.onMenuChosen)}>
|
|
122
|
+
<div
|
|
123
|
+
tabIndex={0}
|
|
124
|
+
role="menuitem"
|
|
125
|
+
aria-label={el.label}
|
|
126
|
+
className={styles.popoverMenuItem}
|
|
127
|
+
ref={node => {
|
|
128
|
+
if (index === 0) {
|
|
129
|
+
this._firstOptionElementRef = node;
|
|
130
|
+
}
|
|
131
|
+
}}>
|
|
132
|
+
{el.label}
|
|
133
|
+
</div>
|
|
134
|
+
</A11yWrapper>
|
|
135
|
+
);
|
|
136
|
+
})}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|