@playkit-js/moderation 2.1.1 → 2.2.0-canary.2-223f8a4

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +5 -5
  3. package/README.md +105 -8
  4. package/dist/3758875b5327755db91c.svg +3 -0
  5. package/dist/4e88e84bb6cd6f386467.svg +9 -0
  6. package/{src/components/moderation/assets/down.svg → dist/699b282225ecacccffd9.svg} +2 -2
  7. package/dist/f4a8c73d84c247e719f8.svg +3 -0
  8. package/dist/playkit-moderation.js +1 -23
  9. package/dist/playkit-moderation.js.map +1 -1
  10. package/package.json +49 -54
  11. package/src/components/a11y-wrapper/a11y-wrapper.tsx +26 -0
  12. package/src/components/a11y-wrapper/index.ts +1 -0
  13. package/src/components/icons/index.ts +9 -0
  14. package/src/components/moderation/moderation.scss +25 -7
  15. package/src/components/moderation/moderation.tsx +64 -104
  16. package/src/components/plugin-button/plugin-button.scss +14 -14
  17. package/src/components/plugin-button/plugin-button.tsx +26 -3
  18. package/src/components/popover/index.ts +1 -0
  19. package/src/components/popover/popover-menu.scss +4 -0
  20. package/src/components/popover/popover-menu.tsx +57 -0
  21. package/src/components/popover/popover.scss +30 -0
  22. package/src/components/popover/popover.tsx +224 -0
  23. package/src/contrib-services/common-types.ts +2 -0
  24. package/src/contrib-services/contrib-services.ts +80 -0
  25. package/src/contrib-services/contrib-utils.ts +16 -0
  26. package/src/contrib-services/events-manager.ts +59 -0
  27. package/src/contrib-services/floating-item-data.tsx +31 -0
  28. package/src/contrib-services/floating-item.tsx +116 -0
  29. package/src/contrib-services/floating-manager.tsx +224 -0
  30. package/src/contrib-services/index.ts +2 -0
  31. package/src/contrib-services/injected-component/index.ts +1 -0
  32. package/src/contrib-services/injected-component/injected-component.scss +5 -0
  33. package/src/contrib-services/injected-component/injected-component.tsx +58 -0
  34. package/src/contrib-services/kaltura-player-utils.ts +18 -0
  35. package/src/contrib-services/managed-component/_managed-component.scss +8 -0
  36. package/src/contrib-services/managed-component/index.ts +1 -0
  37. package/src/contrib-services/managed-component/managed-component.tsx +71 -0
  38. package/src/contrib-services/object-utils.ts +157 -0
  39. package/src/contrib-services/player-contrib-registry.ts +34 -0
  40. package/src/contrib-services/player-utils.ts +37 -0
  41. package/src/contrib-services/preset-item-data.tsx +43 -0
  42. package/src/contrib-services/preset-item.tsx +113 -0
  43. package/src/contrib-services/preset-manager.tsx +153 -0
  44. package/src/contrib-services/presets-utils.ts +27 -0
  45. package/src/contrib-services/toast/_toast.scss +93 -0
  46. package/src/contrib-services/toast/assets/close.svg +9 -0
  47. package/src/contrib-services/toast/index.ts +1 -0
  48. package/src/contrib-services/toast/toast.tsx +67 -0
  49. package/src/contrib-services/toast-manager.tsx +121 -0
  50. package/src/contrib-services/toasts-container/_toasts-container.scss +21 -0
  51. package/src/contrib-services/toasts-container/index.ts +1 -0
  52. package/src/contrib-services/toasts-container/toasts-container.tsx +23 -0
  53. package/src/contrib-services/ui-player-adapter/index.tsx +1 -0
  54. package/src/contrib-services/ui-player-adapter/ui-player-adapter.tsx +27 -0
  55. package/src/contrib-services/uuid.ts +6 -0
  56. package/src/global.d.ts +6 -6
  57. package/src/index.ts +13 -1
  58. package/src/moderation-plugin.scss +12 -12
  59. package/src/moderation-plugin.tsx +123 -160
  60. package/src/providers/index.ts +2 -0
  61. package/src/providers/report-loader.ts +69 -0
  62. package/src/providers/response-types/index.ts +2 -0
  63. package/src/providers/response-types/kaltura-moderation-flag-response.ts +13 -0
  64. package/src/providers/response-types/kaltura-moderation-flag.ts +51 -0
  65. package/src/variables.scss +4 -1
  66. package/src/assets/.gitkeep +0 -0
  67. package/src/assets/close.svg +0 -10
  68. package/src/assets/flag.svg +0 -6
  69. package/src/components/.gitkeep +0 -0
@@ -0,0 +1,30 @@
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
+ }
@@ -0,0 +1,224 @@
1
+ import {h, Component, ComponentChild} from 'preact';
2
+ import * as styles from './popover.scss';
3
+
4
+ export enum PopoverVerticalPositions {
5
+ Top = 'top',
6
+ Bottom = 'bottom',
7
+ }
8
+ export enum PopoverHorizontalPositions {
9
+ Left = 'left',
10
+ Right = 'right',
11
+ }
12
+ export enum PopoverTriggerMode {
13
+ Click = 'click',
14
+ Hover = 'hover',
15
+ }
16
+
17
+ export enum KeyboardKeys {
18
+ Esc = 27,
19
+ Enter = 13,
20
+ Tab = 9,
21
+ Space = 32,
22
+ End = 35,
23
+ Home = 36,
24
+ }
25
+
26
+ const CLOSE_ON_HOVER_DELAY = 500;
27
+
28
+ const defaultProps = {
29
+ verticalPosition: PopoverVerticalPositions.Top,
30
+ horizontalPosition: PopoverHorizontalPositions.Left,
31
+ triggerMode: PopoverTriggerMode.Click,
32
+ className: 'popover',
33
+ closeOnEsc: true,
34
+ closeOnClick: true,
35
+ };
36
+
37
+ interface PopoverProps {
38
+ onClose?: () => void;
39
+ onOpen?: () => void;
40
+ closeOnClick: boolean;
41
+ closeOnEsc: boolean;
42
+ verticalPosition: PopoverVerticalPositions;
43
+ horizontalPosition: PopoverHorizontalPositions;
44
+ className: string;
45
+ triggerMode: PopoverTriggerMode;
46
+ content: ComponentChild;
47
+ children: ComponentChild;
48
+ }
49
+
50
+ interface PopoverState {
51
+ open: boolean;
52
+ }
53
+
54
+ /**
55
+ * Popover renders popup with a target.
56
+ * Properties description:
57
+ * onOpen - function that will be executed when popover opens;
58
+ * onClose - function that will be executed when popover closes;
59
+ * closeOnClick - close the popover on mouse click;
60
+ * verticalPosition - vertical position of popover relative to target ("top" or "bottom"), default - "top";
61
+ * horizontalPosition - horizontal position of popover relative to target ("left" or "right"), default - "left";
62
+ * !!! known limitation of popover positions: if popover opens beyond the visible area of the player - the popover will be overlapped by player
63
+ * className - popover class, can be use for popover styling, default - 'popover';
64
+ * closeOnEsc - handle ESC keyboard pressed event and close popover, default - true;
65
+ * triggerMode - popover support 2 ways for opening: click (keyPress) and mouse hover ("click" or "hover"), default - "click";
66
+ * content - content of popover. Can be any valid Preact node, ex:
67
+ * <select>
68
+ * <option>Option 1</option>
69
+ * ...
70
+ * <option>Option n</option>
71
+ * </select>
72
+ * children - popover target. Can be any valid Preact node, ex:
73
+ * <button>
74
+ * <i className="icon" />
75
+ * </button>
76
+ */
77
+
78
+ export class Popover extends Component<PopoverProps, PopoverState> {
79
+ private _closeTimeout: any = null;
80
+ private _controlElement = null;
81
+ static defaultProps = {
82
+ ...defaultProps,
83
+ };
84
+ state = {
85
+ open: false,
86
+ };
87
+
88
+ componentWillUnmount() {
89
+ this._removeListeners();
90
+ }
91
+
92
+ private _clearTimeout = () => {
93
+ clearTimeout(this._closeTimeout);
94
+ this._closeTimeout = null;
95
+ };
96
+
97
+ private _handleMouseEvent = (event: MouseEvent) => {
98
+ if (
99
+ // @ts-ignore
100
+ !this._controlElement.contains(event.target) &&
101
+ this.props.closeOnClick
102
+ ) {
103
+ this._closePopover();
104
+ }
105
+ };
106
+
107
+ private _handleKeyboardEvent = (event: KeyboardEvent) => {
108
+ if (
109
+ // @ts-ignore
110
+ this._controlElement.contains(event.target) &&
111
+ event.keyCode === KeyboardKeys.Enter
112
+ ) {
113
+ // handle Enter key pressed on Target icon to prevent triggering of _closePopover twice
114
+ return;
115
+ }
116
+ if (
117
+ (this.props.closeOnEsc && event.keyCode === KeyboardKeys.Esc) ||
118
+ event.keyCode === KeyboardKeys.Enter
119
+ ) {
120
+ // handle if ESC or Enter button presesd
121
+ this._closePopover();
122
+ }
123
+ };
124
+
125
+ private _openPopover = () => {
126
+ const {onOpen} = this.props;
127
+ this._clearTimeout();
128
+ this.setState({open: true}, () => {
129
+ this._addListeners();
130
+ if (onOpen) {
131
+ onOpen();
132
+ }
133
+ });
134
+ };
135
+
136
+ private _closePopover = () => {
137
+ const {onClose} = this.props;
138
+ this._clearTimeout();
139
+ this.setState({open: false}, () => {
140
+ this._removeListeners();
141
+ if (onClose) {
142
+ onClose();
143
+ }
144
+ });
145
+ };
146
+
147
+ private _togglePopover = (e: MouseEvent | KeyboardEvent) => {
148
+ if (this.state.open) {
149
+ this._closePopover();
150
+ } else {
151
+ this._openPopover();
152
+ }
153
+ };
154
+ private _handleMouseEnter = () => {
155
+ if (!this.state.open) {
156
+ this._openPopover();
157
+ }
158
+ };
159
+ private _handleMouseLeave = () => {
160
+ this._closeTimeout = setTimeout(this._closePopover, CLOSE_ON_HOVER_DELAY);
161
+ };
162
+ private _handleHoverOnPopover = () => {
163
+ if (this.state.open && this._closeTimeout) {
164
+ this._clearTimeout();
165
+ } else {
166
+ this._closePopover();
167
+ }
168
+ };
169
+ private _addListeners = () => {
170
+ document.addEventListener('click', this._handleMouseEvent);
171
+ document.addEventListener('keydown', this._handleKeyboardEvent);
172
+ };
173
+ private _removeListeners = () => {
174
+ document.removeEventListener('click', this._handleMouseEvent);
175
+ document.removeEventListener('keydown', this._handleKeyboardEvent);
176
+ };
177
+ private _getHoverEvents = () => {
178
+ if (this.props.triggerMode === PopoverTriggerMode.Hover) {
179
+ return {
180
+ targetEvents: {
181
+ onMouseEnter: this._handleMouseEnter,
182
+ onMouseLeave: this._handleMouseLeave,
183
+ },
184
+ popoverEvents: {
185
+ onMouseEnter: this._handleHoverOnPopover,
186
+ onMouseLeave: this._handleHoverOnPopover,
187
+ },
188
+ };
189
+ }
190
+ return {targetEvents: {onClick: this._togglePopover}, popoverEvents: {}};
191
+ };
192
+ render(props: PopoverProps) {
193
+ if (!props.content || !props.children) {
194
+ return null;
195
+ }
196
+ const {targetEvents, popoverEvents} = this._getHoverEvents();
197
+ return (
198
+ <div className={styles.popoverContainer}>
199
+ <div
200
+ className="popover-anchor-container"
201
+ ref={node => {
202
+ // @ts-ignore
203
+ this._controlElement = node;
204
+ }}
205
+ {...targetEvents}>
206
+ {props.children}
207
+ </div>
208
+ {this.state.open && (
209
+ <div
210
+ aria-expanded="true"
211
+ className={[
212
+ props.className,
213
+ styles.popoverComponent,
214
+ styles[props.verticalPosition],
215
+ styles[props.horizontalPosition],
216
+ ].join(' ')}
217
+ {...popoverEvents}>
218
+ {props.content}
219
+ </div>
220
+ )}
221
+ </div>
222
+ );
223
+ }
224
+ }
@@ -0,0 +1,2 @@
1
+ export type PlayerSize = {width: number; height: number};
2
+ export type VideoSize = {width: number; height: number};
@@ -0,0 +1,80 @@
1
+ import {PlayerContribRegistry} from './player-contrib-registry';
2
+ import {PresetManager} from './preset-manager';
3
+ import {ToastManager} from './toast-manager';
4
+ import {FloatingManager} from './floating-manager';
5
+
6
+ export interface ContribServicesOptions {
7
+ kalturaPlayer: KalturaPlayerTypes.Player;
8
+ }
9
+
10
+ export function getPlayerContribRegistry(kalturaPlayer: KalturaPlayerTypes.Player): PlayerContribRegistry {
11
+ return PlayerContribRegistry.get(kalturaPlayer);
12
+ }
13
+
14
+ export class ContribServices {
15
+ static get(options: ContribServicesOptions): ContribServices {
16
+ const playerContribRegistry = getPlayerContribRegistry(options.kalturaPlayer);
17
+ return playerContribRegistry.register('ContribServices', () => {
18
+ return new ContribServices(playerContribRegistry, options);
19
+ });
20
+ }
21
+
22
+ private _toastManager: ToastManager;
23
+ private _floatingManager: FloatingManager;
24
+ private _presetManager: PresetManager;
25
+
26
+ constructor(private _playerContribRegistry: PlayerContribRegistry, private _options: ContribServicesOptions) {
27
+ const kalturaPlayer = this._options.kalturaPlayer;
28
+
29
+ const presetManager = new PresetManager({
30
+ kalturaPlayer
31
+ });
32
+ const floatingManager = new FloatingManager({
33
+ kalturaPlayer,
34
+ presetManager
35
+ });
36
+ const toastManager = new ToastManager({
37
+ floatingManager
38
+ });
39
+ this._toastManager = toastManager;
40
+ this._floatingManager = floatingManager;
41
+ this._presetManager = presetManager;
42
+
43
+ this._addListeners();
44
+
45
+ }
46
+
47
+ private _addListeners = (): void => {
48
+ const kalturaPlayer = this._options.kalturaPlayer;
49
+ kalturaPlayer.addEventListener(kalturaPlayer.Event.PLAYER_RESET, this.reset);
50
+ kalturaPlayer.addEventListener(kalturaPlayer.Event.PLAYER_DESTROY, this._removeListeners);
51
+ };
52
+
53
+ private _removeListeners = (): void => {
54
+ const kalturaPlayer = this._options.kalturaPlayer;
55
+ kalturaPlayer.removeEventListener(kalturaPlayer.Event.PLAYER_RESET, this.reset);
56
+ kalturaPlayer.removeEventListener(kalturaPlayer.Event.PLAYER_DESTROY, this._removeListeners);
57
+ };
58
+
59
+ public get presetManager(): PresetManager {
60
+ return this._presetManager;
61
+ }
62
+
63
+ public get floatingManager(): FloatingManager {
64
+ return this._floatingManager;
65
+ }
66
+
67
+ public get toastManager(): ToastManager {
68
+ return this._toastManager;
69
+ }
70
+
71
+ reset = (): void => {
72
+ this._toastManager.reset();
73
+ this._floatingManager.reset();
74
+ };
75
+
76
+ public getPlayerKS(): string | null {
77
+ const {session} = this._options.kalturaPlayer.config;
78
+ return session && session.ks ? session.ks : null;
79
+ }
80
+ }
@@ -0,0 +1,16 @@
1
+ import {KalturaPlayerUtils} from './kaltura-player-utils';
2
+
3
+ export function getContribConfig<T>(
4
+ player: KalturaPlayerTypes.Player,
5
+ path: string,
6
+ baseConfig: T,
7
+ options?: {explicitMerge: string[]}
8
+ ): T {
9
+ return KalturaPlayerUtils.getPlayerConfig(
10
+ player,
11
+ `contrib.${path}`,
12
+ baseConfig,
13
+ // @ts-ignore
14
+ options
15
+ );
16
+ }
@@ -0,0 +1,59 @@
1
+ export type Handler<T> = (event: T) => void;
2
+ export type WildcardHandler = (event: any) => void;
3
+
4
+ export interface Event {
5
+ type: string;
6
+ }
7
+
8
+ export class EventsManager<T extends Event> {
9
+ private _eventListeners: Record<string, Handler<any>[]> = {};
10
+ private _wildcardEventListeners: WildcardHandler[] = [];
11
+
12
+ on<TEvent extends Event>(
13
+ type: TEvent['type'],
14
+ handler: Handler<TEvent>
15
+ ): void;
16
+ on(type: '*', handler: WildcardHandler): void;
17
+ on(type: string, handler: any): void {
18
+ if (type === '*') {
19
+ this._wildcardEventListeners.push(handler);
20
+ return;
21
+ }
22
+
23
+ (this._eventListeners[type] || (this._eventListeners[type] = [])).push(
24
+ handler
25
+ );
26
+ }
27
+
28
+ off<TEvent extends Event>(
29
+ type: TEvent['type'],
30
+ handler: Handler<TEvent>
31
+ ): void;
32
+ off(type: '*', handler: WildcardHandler): void;
33
+ off(type: string, handler: any): void {
34
+ if (type === '*') {
35
+ this._wildcardEventListeners.splice(
36
+ this._wildcardEventListeners.indexOf(handler) >>> 0,
37
+ 1
38
+ );
39
+ return;
40
+ }
41
+
42
+ const eventListeners = this._eventListeners[type];
43
+
44
+ if (!eventListeners) {
45
+ return;
46
+ }
47
+
48
+ eventListeners.splice(eventListeners.indexOf(handler) >>> 0, 1);
49
+ }
50
+
51
+ emit(event: T): void {
52
+ (this._eventListeners[event.type] || []).slice().map(handler => {
53
+ handler(event);
54
+ });
55
+ this._wildcardEventListeners.slice().map(handler => {
56
+ handler(event);
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,31 @@
1
+ import {ComponentChild, Ref} from 'preact';
2
+ import {PlayerSize, VideoSize} from './common-types';
3
+
4
+ export enum FloatingUIModes {
5
+ MediaLoaded = 'MediaLoaded',
6
+ OnDemand = 'OnDemand',
7
+ Immediate = 'Immediate',
8
+ FirstPlay = 'FirstPlay',
9
+ }
10
+
11
+ export enum FloatingPositions {
12
+ VideoArea = 'VideoArea',
13
+ PresetArea = 'PresetArea',
14
+ InteractiveArea = 'InteractiveArea',
15
+ }
16
+
17
+ export interface FloatingItemData {
18
+ label: string;
19
+ mode: FloatingUIModes;
20
+ renderContent: (floatingItemProps: FloatingItemProps) => ComponentChild;
21
+ className?: string;
22
+ position: FloatingPositions;
23
+ }
24
+
25
+ export interface FloatingItemProps {
26
+ currentTime: number;
27
+ canvas: {
28
+ playerSize: PlayerSize;
29
+ videoSize: VideoSize;
30
+ };
31
+ }
@@ -0,0 +1,116 @@
1
+ import {h} from 'preact';
2
+ import {
3
+ FloatingItemData,
4
+ FloatingItemProps,
5
+ FloatingUIModes,
6
+ } from './floating-item-data';
7
+ import {ManagedComponent} from './managed-component';
8
+
9
+ export interface FloatingItemOptions {
10
+ kalturaPlayer: KalturaPlayerTypes.Player;
11
+ data: FloatingItemData;
12
+ }
13
+
14
+ export class FloatingItem {
15
+ private _destroyed = false;
16
+ private _options: FloatingItemOptions;
17
+ private _isShown = false;
18
+ private _componentRef: ManagedComponent | null = null;
19
+
20
+ constructor(options: FloatingItemOptions) {
21
+ this._options = options;
22
+
23
+ this._addPlayerBindings();
24
+ }
25
+
26
+ get data(): FloatingItemData {
27
+ return this._options.data;
28
+ }
29
+
30
+ remove = (): void => {
31
+ this._isShown = false;
32
+ if (!this._componentRef) {
33
+ return;
34
+ }
35
+
36
+ this._componentRef.update();
37
+ };
38
+
39
+ add = (): void => {
40
+ this._isShown = true;
41
+ if (!this._componentRef) {
42
+ return;
43
+ }
44
+
45
+ this._componentRef.update();
46
+ };
47
+
48
+ public update() {
49
+ if (!this._componentRef) {
50
+ return;
51
+ }
52
+
53
+ this._componentRef.update();
54
+ }
55
+
56
+ /**
57
+ * destory the ui item
58
+ */
59
+ destroy(): void {
60
+ this._destroyed = true;
61
+ this.remove();
62
+ }
63
+
64
+ renderFloatingChild(props: FloatingItemProps) {
65
+ const {label} = this._options.data;
66
+
67
+ return (
68
+ <ManagedComponent
69
+ label={label}
70
+ renderChildren={() => this._options.data.renderContent(props)}
71
+ isShown={() => this._isShown}
72
+ ref={(ref: any) => (this._componentRef = ref)}
73
+ />
74
+ );
75
+ }
76
+
77
+ private _handleMediaLoaded = () => {
78
+ const {kalturaPlayer} = this._options;
79
+ kalturaPlayer.removeEventListener(
80
+ kalturaPlayer.Event.MEDIA_LOADED,
81
+ this._handleMediaLoaded
82
+ );
83
+ this.add();
84
+ };
85
+
86
+ private _handleFirstPlay = () => {
87
+ const {kalturaPlayer} = this._options;
88
+ kalturaPlayer.removeEventListener(
89
+ kalturaPlayer.Event.FIRST_PLAY,
90
+ this._handleFirstPlay
91
+ );
92
+ this.add();
93
+ };
94
+
95
+ private _addPlayerBindings() {
96
+ const {kalturaPlayer, data} = this._options;
97
+
98
+ if (data.mode === FloatingUIModes.MediaLoaded) {
99
+ kalturaPlayer.addEventListener(
100
+ kalturaPlayer.Event.MEDIA_LOADED,
101
+ this._handleMediaLoaded
102
+ );
103
+ }
104
+
105
+ if (data.mode === FloatingUIModes.FirstPlay) {
106
+ kalturaPlayer.addEventListener(
107
+ kalturaPlayer.Event.FIRST_PLAY,
108
+ this._handleFirstPlay
109
+ );
110
+ }
111
+
112
+ if (data.mode === FloatingUIModes.Immediate) {
113
+ this.add();
114
+ }
115
+ }
116
+ }