@playkit-js/moderation 2.0.10
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 +54 -0
- package/LICENSE +661 -0
- package/README.md +12 -0
- package/dist/playkit-moderation.js +24 -0
- package/dist/playkit-moderation.js.map +1 -0
- package/package.json +80 -0
- package/src/assets/.gitkeep +0 -0
- package/src/assets/close.svg +10 -0
- package/src/assets/down.svg +9 -0
- package/src/assets/error.svg +3 -0
- package/src/assets/flag.svg +6 -0
- package/src/assets/report.svg +3 -0
- package/src/components/.gitkeep +0 -0
- package/src/components/moderation/assets/down.svg +9 -0
- package/src/components/moderation/index.ts +1 -0
- package/src/components/moderation/moderation.scss +195 -0
- package/src/components/moderation/moderation.tsx +239 -0
- package/src/components/plugin-button/index.ts +1 -0
- package/src/components/plugin-button/plugin-button.scss +14 -0
- package/src/components/plugin-button/plugin-button.tsx +6 -0
- package/src/components/popover-menu/index.ts +1 -0
- package/src/components/popover-menu/popover-menu.scss +4 -0
- package/src/components/popover-menu/popover-menu.tsx +52 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +1 -0
- package/src/moderation-plugin.scss +13 -0
- package/src/moderation-plugin.tsx +241 -0
- package/src/variables.scss +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@playkit-js/moderation",
|
|
3
|
+
"version": "2.0.10",
|
|
4
|
+
"private": false,
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/kaltura/playkit-js-moderation/issues"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/kaltura/playkit-js-moderation#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/kaltura/playkit-js-moderation.git"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@playkit-js-contrib/cli": "1.1.0",
|
|
18
|
+
"@playkit-js-contrib/common": "^4.1.10",
|
|
19
|
+
"@playkit-js-contrib/plugin": "^4.1.12",
|
|
20
|
+
"@playkit-js-contrib/ui": "^4.1.12",
|
|
21
|
+
"classnames": "2.2.6",
|
|
22
|
+
"kaltura-typescript-client": "file:libs/kaltura-typescript-client-7.0.0-v20190324-101134.tgz",
|
|
23
|
+
"null": "^2.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@commitlint/cli": "8.3.5",
|
|
27
|
+
"@commitlint/config-conventional": "8.3.4",
|
|
28
|
+
"@types/classnames": "2.2.9",
|
|
29
|
+
"@types/node": "13.7.6",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "2.21.0",
|
|
31
|
+
"@typescript-eslint/parser": "2.21.0",
|
|
32
|
+
"husky": "3.1.0",
|
|
33
|
+
"preact": "^10.4.1",
|
|
34
|
+
"tslint": "5.20.1",
|
|
35
|
+
"typescript": "3.8.2"
|
|
36
|
+
},
|
|
37
|
+
"license": "AGPL-3.0",
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.md",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"src"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"clean": "rm -rf dist",
|
|
47
|
+
"reset": "npm run clean && rm -rf node_modules",
|
|
48
|
+
"build": "kcontrib build",
|
|
49
|
+
"build:dev": "kcontrib build --dev",
|
|
50
|
+
"bump-canary": "standard-version --prerelease canary --skip.commit=true --skip.tag=true",
|
|
51
|
+
"serve": "kcontrib serve",
|
|
52
|
+
"serve:update-modes": "kcontrib serve --update-modes",
|
|
53
|
+
"serve:update-player": "kcontrib serve --update-player",
|
|
54
|
+
"analyze": "npm run build && npx source-map-explorer dist/playkit-moderation.js",
|
|
55
|
+
"lint": "tsc --noEmit && eslint ./src --ext .ts,.tsx",
|
|
56
|
+
"lint:fix": "tsc --noEmit && eslint ./src --ext .ts,.tsx --fix",
|
|
57
|
+
"husky:pre-commit": "lint-staged",
|
|
58
|
+
"husky:commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
|
59
|
+
"deploy:prepare": "kcontrib deploy --prepare",
|
|
60
|
+
"deploy:publish-to-npm": "kcontrib deploy --publish",
|
|
61
|
+
"deploy:next:prepare": "kcontrib deploy --prepare --prerelease next",
|
|
62
|
+
"contrib:latest": "kcontrib infra --type=latest",
|
|
63
|
+
"infra:latest": "kcontrib infra --type=latest",
|
|
64
|
+
"infra:next": "kcontrib infra --type=next",
|
|
65
|
+
"infra:local": "kcontrib infra --type=local",
|
|
66
|
+
"infra:add": "kcontrib infra --add"
|
|
67
|
+
},
|
|
68
|
+
"browserslist": {
|
|
69
|
+
"production": [
|
|
70
|
+
">0.2%",
|
|
71
|
+
"not dead",
|
|
72
|
+
"not op_mini all"
|
|
73
|
+
],
|
|
74
|
+
"development": [
|
|
75
|
+
"last 1 chrome version",
|
|
76
|
+
"last 1 firefox version",
|
|
77
|
+
"last 1 safari version"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
|
4
|
+
<title>Icons/32/Close</title>
|
|
5
|
+
<desc>Created with Sketch.</desc>
|
|
6
|
+
<g id="Icons/32/Close" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
7
|
+
<rect id="Bounds" x="0" y="0" width="32" height="32"></rect>
|
|
8
|
+
<path d="M17.9113162,16 L24.6072325,9.30408374 C25.1313645,8.77995172 25.1287183,7.92687249 24.6009229,7.3990771 C24.0694478,6.86760201 23.220227,6.86845682 22.6959163,7.39276754 L16,14.0886838 L9.30408374,7.39276754 C8.77995172,6.86863552 7.92687249,6.8712817 7.3990771,7.3990771 C6.86760201,7.93055219 6.86845682,8.77977302 7.39276754,9.30408374 L14.0886838,16 L7.39276754,22.6959163 C6.86863552,23.2200483 6.8712817,24.0731275 7.3990771,24.6009229 C7.93055219,25.132398 8.77977302,25.1315432 9.30408374,24.6072325 L16,17.9113162 L22.6959163,24.6072325 C23.2200483,25.1313645 24.0731275,25.1287183 24.6009229,24.6009229 C25.132398,24.0694478 25.1315432,23.220227 24.6072325,22.6959163 L17.9113162,16 Z" id="Close" fill="rgba(255,255,255,0.8)"></path>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
|
4
|
+
<title>Icons/16/Arrow/down</title>
|
|
5
|
+
<desc>Created with Sketch.</desc>
|
|
6
|
+
<g id="Icons/16/Arrow/down" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
7
|
+
<path d="M4.78325732,5.37830235 C4.43990319,4.94572127 3.81088342,4.87338855 3.37830235,5.21674268 C2.94572127,5.56009681 2.87338855,6.18911658 3.21674268,6.62169765 L7.21674268,11.6611718 C7.61710439,12.165575 8.38289561,12.165575 8.78325732,11.6611718 L12.7832573,6.62169765 C13.1266115,6.18911658 13.0542787,5.56009681 12.6216977,5.21674268 C12.1891166,4.87338855 11.5600968,4.94572127 11.2167427,5.37830235 L8,9.43097528 L4.78325732,5.37830235 Z" id="Path-2" fill="#ffffff"></path>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
|
2
|
+
<path fill="#CCC" fill-rule="evenodd" d="M8 2c3.314 0 6 2.686 6 6s-2.686 6-6 6-6-2.686-6-6 2.686-6 6-6zm0 8c-.552 0-1 .448-1 1s.448 1 1 1 1-.448 1-1-.448-1-1-1zm0-6c-.552 0-1 .444-1 1v3c0 .513.383.936.883.993L8 9c.552 0 1-.444 1-1V5c0-.513-.383-.936-.883-.993L8 4z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<path d="M0 0H32V32H0z"/>
|
|
4
|
+
<path fill="#FFF" d="M8.378 7.084l5.175 19.314c.134.497-.126 1.005-.594 1.19l-.112.037c-.533.143-1.08-.165-1.225-.71L6.447 7.603c-.134-.497.126-1.005.594-1.19l.112-.037c.533-.143 1.08.165 1.225.71zM21.882 7c1.878 0 2.79 1.622 1.84 3.239l-1.386 2.36 2.94 3.246C26.6 17.31 25.842 19 23.868 19h-10.21c-.452 0-.848-.304-.966-.741l-2.68-10c-.17-.635.31-1.259.967-1.259h10.902zm.211 1.994l-.21.006h-9.6l2.144 8h9.196l-3.263-3.604c-.293-.324-.342-.8-.12-1.178l1.757-2.992c.114-.194.168-.23.096-.232z" opacity=".8"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
|
2
|
+
<path fill="#CCC" fill-rule="evenodd" d="M3.292 1.488L6.69 14.172c.086.32-.076.646-.371.774l-.093.032c-.35.094-.712-.122-.805-.466L2.023 1.828c-.086-.32.076-.646.371-.774l.093-.032c.35-.094.712.122.805.466zm8.868-.055c1.233 0 1.832 1.065 1.208 2.127l-.91 1.55 1.93 2.132c.87.961.373 2.071-.923 2.071H6.76c-.297 0-.558-.2-.635-.486l-1.76-6.568c-.111-.417.203-.826.635-.826h7.16z"/>
|
|
3
|
+
</svg>
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<!-- Generator: Sketch 57.1 (83088) - https://sketch.com -->
|
|
4
|
+
<title>Icons/16/Arrow/down</title>
|
|
5
|
+
<desc>Created with Sketch.</desc>
|
|
6
|
+
<g id="Icons/16/Arrow/down" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
7
|
+
<path d="M4.78325732,5.37830235 C4.43990319,4.94572127 3.81088342,4.87338855 3.37830235,5.21674268 C2.94572127,5.56009681 2.87338855,6.18911658 3.21674268,6.62169765 L7.21674268,11.6611718 C7.61710439,12.165575 8.38289561,12.165575 8.78325732,11.6611718 L12.7832573,6.62169765 C13.1266115,6.18911658 13.0542787,5.56009681 12.6216977,5.21674268 C12.1891166,4.87338855 11.5600968,4.94572127 11.2167427,5.37830235 L8,9.43097528 L4.78325732,5.37830235 Z" id="Path-2" fill="#FFFFFF"></path>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./moderation";
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
@import '../../variables.scss';
|
|
2
|
+
|
|
3
|
+
.root {
|
|
4
|
+
position: absolute;
|
|
5
|
+
top: 0;
|
|
6
|
+
left: 0;
|
|
7
|
+
width: 100%;
|
|
8
|
+
height: 100%;
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
padding: 60px 32px 32px 32px;
|
|
12
|
+
color: $white-color;
|
|
13
|
+
background-color: $root-background;
|
|
14
|
+
-webkit-backdrop-filter: blur(16px);
|
|
15
|
+
backdrop-filter: blur(16px);
|
|
16
|
+
.close-button {
|
|
17
|
+
position: absolute;
|
|
18
|
+
top: 32px;
|
|
19
|
+
right: 32px;
|
|
20
|
+
width: 32px;
|
|
21
|
+
height: 32px;
|
|
22
|
+
min-width: 32px;
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
background-image: url('../../assets/close.svg');
|
|
25
|
+
&:focus {
|
|
26
|
+
outline: 1px solid $focus-color;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
.main-wrapper {
|
|
30
|
+
height: 100%;
|
|
31
|
+
width: 400px;
|
|
32
|
+
.title {
|
|
33
|
+
margin-bottom: 8px;
|
|
34
|
+
font-size: 28px;
|
|
35
|
+
font-weight: bold;
|
|
36
|
+
text-align: center;
|
|
37
|
+
}
|
|
38
|
+
.subtitle {
|
|
39
|
+
margin-top: 16px;
|
|
40
|
+
opacity: 0.7;
|
|
41
|
+
font-size: 15px;
|
|
42
|
+
line-height: 1.6;
|
|
43
|
+
}
|
|
44
|
+
.report-popover {
|
|
45
|
+
width: 100%;
|
|
46
|
+
max-width: 280px;
|
|
47
|
+
}
|
|
48
|
+
.popover-menu-item {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
width: 100%;
|
|
52
|
+
height: 36px;
|
|
53
|
+
padding: 0 16px;
|
|
54
|
+
font-size: 15px;
|
|
55
|
+
color: $white-color;
|
|
56
|
+
&:hover {
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
background-color: $semigray-color;
|
|
59
|
+
}
|
|
60
|
+
&:focus {
|
|
61
|
+
outline: 1px solid $focus-color;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
.select-wrapper {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
width: 100%;
|
|
68
|
+
margin-top: 24px;
|
|
69
|
+
margin-bottom: 16px;
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
padding: 0;
|
|
72
|
+
background: initial;
|
|
73
|
+
color: $white-color;
|
|
74
|
+
border: none;
|
|
75
|
+
&:focus {
|
|
76
|
+
outline: 1px solid $focus-color;
|
|
77
|
+
}
|
|
78
|
+
.select {
|
|
79
|
+
opacity: 0.8;
|
|
80
|
+
font-size: 15px;
|
|
81
|
+
line-height: 1.6;
|
|
82
|
+
font-weight: bold;
|
|
83
|
+
}
|
|
84
|
+
.down-arrow {
|
|
85
|
+
width: 10px;
|
|
86
|
+
height: 10px;
|
|
87
|
+
margin-left: 10px;
|
|
88
|
+
background-image: url('../../assets/down.svg');
|
|
89
|
+
background-position: center;
|
|
90
|
+
background-repeat: no-repeat;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
.textarea {
|
|
94
|
+
resize: none;
|
|
95
|
+
width: 100%;
|
|
96
|
+
height: 72px;
|
|
97
|
+
margin-bottom: 12px;
|
|
98
|
+
padding: 7px 8px;
|
|
99
|
+
font-size: 15px;
|
|
100
|
+
color: $gray-color;
|
|
101
|
+
background-color: rgba(255, 255, 255, 0.16);
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
border: solid 1px rgba($color: $white-color, $alpha: 0.4);
|
|
104
|
+
overflow-y: auto;
|
|
105
|
+
&.active {
|
|
106
|
+
background-color: $black-color;
|
|
107
|
+
}
|
|
108
|
+
&:-ms-input-placeholderr { /* Internet Explorer 10-11 */
|
|
109
|
+
color: $gray-color !important;
|
|
110
|
+
font-size: 15px;
|
|
111
|
+
opacity: 1;
|
|
112
|
+
}
|
|
113
|
+
&::-ms-input-placeholder { /* Microsoft Edge */
|
|
114
|
+
color: $gray-color;
|
|
115
|
+
}
|
|
116
|
+
&::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
|
117
|
+
color: $gray-color;
|
|
118
|
+
opacity: 1; /* Firefox */
|
|
119
|
+
}
|
|
120
|
+
&::selection {
|
|
121
|
+
border-radius: 2px;
|
|
122
|
+
background-color: #5b80a7;
|
|
123
|
+
}
|
|
124
|
+
&::-webkit-scrollbar {
|
|
125
|
+
width: 11px;
|
|
126
|
+
}
|
|
127
|
+
&::-webkit-scrollbar-thumb {
|
|
128
|
+
border-radius: 8px;
|
|
129
|
+
background-color: rgba(255, 255, 255, 0.3);
|
|
130
|
+
border: 4px solid rgba(0, 0, 0, 0);
|
|
131
|
+
background-clip: padding-box;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
.submit-wrapper {
|
|
135
|
+
width: 100%;
|
|
136
|
+
display: flex;
|
|
137
|
+
justify-content: flex-end;
|
|
138
|
+
align-items: center;
|
|
139
|
+
.character-counter {
|
|
140
|
+
font-size: 14px;
|
|
141
|
+
text-align: right;
|
|
142
|
+
margin-right: 12px;
|
|
143
|
+
color: $character-counter-color;
|
|
144
|
+
}
|
|
145
|
+
.submit-button {
|
|
146
|
+
width: 88px;
|
|
147
|
+
height: 32px;
|
|
148
|
+
border-radius: 18px;
|
|
149
|
+
color: $white-color;
|
|
150
|
+
text-align: center;
|
|
151
|
+
font-size: 15px;
|
|
152
|
+
font-weight: bold;
|
|
153
|
+
background-color: $submit-button-color;
|
|
154
|
+
border: none;
|
|
155
|
+
outline: none !important; // overwrites player styles
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
&.disabled {
|
|
158
|
+
background-color: $semigray-color;
|
|
159
|
+
}
|
|
160
|
+
&:hover:not(.disabled) {
|
|
161
|
+
background-color: darken($color: $submit-button-color, $amount: 10);
|
|
162
|
+
}
|
|
163
|
+
&:focus {
|
|
164
|
+
box-shadow: 0px 0px 0px 1px $focus-color;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
:global {
|
|
172
|
+
.kaltura-moderation__root .playkit-tooltip {
|
|
173
|
+
width: auto;
|
|
174
|
+
}
|
|
175
|
+
.playkit-player.playkit-size-sm,
|
|
176
|
+
.playkit-player.playkit-size-xs {
|
|
177
|
+
.kaltura-moderation__close-button {
|
|
178
|
+
top: 10px;
|
|
179
|
+
right: 16px;
|
|
180
|
+
}
|
|
181
|
+
.kaltura-moderation__root {
|
|
182
|
+
padding: 14px 16px;
|
|
183
|
+
.kaltura-moderation__title {
|
|
184
|
+
margin-bottom: 16px;
|
|
185
|
+
font-size: 20px;
|
|
186
|
+
text-align: left;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
.playkit-player.playkit-size-ty {
|
|
191
|
+
.kaltura-moderation__root {
|
|
192
|
+
display: none;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import {h, Component, Fragment} from 'preact';
|
|
2
|
+
import {getContribLogger, ObjectUtils} from '@playkit-js-contrib/common';
|
|
3
|
+
import {
|
|
4
|
+
KeyboardKeys,
|
|
5
|
+
Popover,
|
|
6
|
+
PopoverHorizontalPositions,
|
|
7
|
+
PopoverVerticalPositions,
|
|
8
|
+
} from '@playkit-js-contrib/ui';
|
|
9
|
+
import {KalturaModerationFlagType} from 'kaltura-typescript-client/api/types';
|
|
10
|
+
import {PopoverMenu, PopoverMenuItem} from '../popover-menu';
|
|
11
|
+
import * as styles from './moderation.scss';
|
|
12
|
+
const {get} = ObjectUtils;
|
|
13
|
+
const {Tooltip} = KalturaPlayer.ui.components;
|
|
14
|
+
|
|
15
|
+
export interface ModerateOption {
|
|
16
|
+
id: number;
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ModerationProps {
|
|
21
|
+
onClick: () => void;
|
|
22
|
+
onSubmit: (contentType: KalturaModerationFlagType, content: string, callBack: () => void) => void;
|
|
23
|
+
reportLength: number;
|
|
24
|
+
moderateOptions: ModerateOption[];
|
|
25
|
+
subtitle: string;
|
|
26
|
+
tooltipMessage: string;
|
|
27
|
+
closeButtonSelected: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ModerationState {
|
|
31
|
+
reportContentType: number;
|
|
32
|
+
reportContent: string;
|
|
33
|
+
isTextareaActive: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const logger = getContribLogger({
|
|
37
|
+
class: 'Info',
|
|
38
|
+
module: 'info-plugin',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const DEFAULT_CONTENT_TYPE = 'Choose a reason for reporting this content';
|
|
42
|
+
|
|
43
|
+
const initialState: ModerationState = {
|
|
44
|
+
reportContent: '',
|
|
45
|
+
reportContentType: -1,
|
|
46
|
+
isTextareaActive: false,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export class Moderation extends Component<ModerationProps, ModerationState> {
|
|
50
|
+
_closeButtonNode: null | HTMLDivElement = null;
|
|
51
|
+
|
|
52
|
+
state: ModerationState = { ...initialState };
|
|
53
|
+
|
|
54
|
+
componentDidMount(): void {
|
|
55
|
+
logger.trace('Moderation plugin mount', {
|
|
56
|
+
method: 'componentDidMount',
|
|
57
|
+
});
|
|
58
|
+
if (this._closeButtonNode && this.props.closeButtonSelected) {
|
|
59
|
+
this._closeButtonNode.focus();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private _onContentTypeChange = (id: number) => {
|
|
64
|
+
this.setState({
|
|
65
|
+
reportContentType: id,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
private _onContentChange = (event: any) => {
|
|
70
|
+
this.setState({
|
|
71
|
+
reportContent: event.target.value,
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
private _handleFocus = () => {
|
|
76
|
+
this.setState({
|
|
77
|
+
isTextareaActive: true,
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
private _handleBlur = () => {
|
|
82
|
+
this.setState((state: ModerationState) => ({
|
|
83
|
+
isTextareaActive: state.reportContent.length > 0,
|
|
84
|
+
}));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
private _handleSubmit = (event: any) => {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
const {reportContent, reportContentType} = this.state;
|
|
90
|
+
logger.trace('Moderation plugin submit click', {
|
|
91
|
+
method: 'handleSubmit',
|
|
92
|
+
});
|
|
93
|
+
if (reportContentType === -1) {
|
|
94
|
+
logger.trace('Moderation User did not select reason', {
|
|
95
|
+
method: 'handleSubmit',
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.props.onSubmit(reportContentType, reportContent, () => {
|
|
100
|
+
this.setState({...initialState});
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
private _onKeyDown = (e: KeyboardEvent, callBack: Function) => {
|
|
105
|
+
if (e.keyCode !== KeyboardKeys.Enter && e.keyCode !== KeyboardKeys.Esc) {
|
|
106
|
+
// don't stopPropagation on ESC and Enter pressed as it prevent the popup closing
|
|
107
|
+
e.stopPropagation();
|
|
108
|
+
}
|
|
109
|
+
switch (e.keyCode) {
|
|
110
|
+
case 13: // Enter pressed
|
|
111
|
+
callBack();
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
private _popoverMenuItemRenderer = (el: PopoverMenuItem) => (
|
|
117
|
+
<div
|
|
118
|
+
tabIndex={1}
|
|
119
|
+
role="button"
|
|
120
|
+
onClick={() => el.onMenuChosen()}
|
|
121
|
+
onKeyDown={e => this._onKeyDown(e, el.onMenuChosen)}
|
|
122
|
+
className={styles.popoverMenuItem}>
|
|
123
|
+
{el.label}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
private _getPopoverMenuOptions = () => {
|
|
128
|
+
return this.props.moderateOptions.map(({label, id}: ModerateOption) => ({
|
|
129
|
+
label: label || '',
|
|
130
|
+
onMenuChosen: () => this._onContentTypeChange(id || -1),
|
|
131
|
+
}));
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
private _popoverContent = () => {
|
|
135
|
+
return (
|
|
136
|
+
<PopoverMenu
|
|
137
|
+
itemRenderer={this._popoverMenuItemRenderer}
|
|
138
|
+
options={this._getPopoverMenuOptions()}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
private _getContentType = () => {
|
|
144
|
+
return (
|
|
145
|
+
this.props.moderateOptions.find(
|
|
146
|
+
(moderateOption: ModerateOption) =>
|
|
147
|
+
moderateOption.id === this.state.reportContentType
|
|
148
|
+
) || {}
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
private _handleClose = (event: MouseEvent | KeyboardEvent) => {
|
|
153
|
+
if (
|
|
154
|
+
event.type === 'keypress' &&
|
|
155
|
+
get(event, 'keyCode', null) !== KeyboardKeys.Enter
|
|
156
|
+
) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.props.onClick();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
render(props: ModerationProps) {
|
|
163
|
+
const {reportLength, subtitle, tooltipMessage} = props;
|
|
164
|
+
const {reportContent, reportContentType, isTextareaActive} = this.state;
|
|
165
|
+
return (
|
|
166
|
+
<div className={[styles.root, 'kaltura-moderation__root'].join(' ')}>
|
|
167
|
+
<div
|
|
168
|
+
className={[
|
|
169
|
+
styles.closeButton,
|
|
170
|
+
'kaltura-moderation__close-button',
|
|
171
|
+
].join(' ')}
|
|
172
|
+
role="button"
|
|
173
|
+
tabIndex={1}
|
|
174
|
+
onClick={this._handleClose}
|
|
175
|
+
onKeyPress={this._handleClose}
|
|
176
|
+
ref={(node: HTMLDivElement | null) => {
|
|
177
|
+
this._closeButtonNode = node;
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
<div className={styles.mainWrapper}>
|
|
181
|
+
<div
|
|
182
|
+
className={[styles.title, 'kaltura-moderation__title'].join(' ')}>
|
|
183
|
+
What’s wrong with this content?
|
|
184
|
+
</div>
|
|
185
|
+
{subtitle ? (
|
|
186
|
+
<div className={[styles.subtitle].join(' ')}>{subtitle}</div>
|
|
187
|
+
) : null}
|
|
188
|
+
<Popover
|
|
189
|
+
className={styles.reportPopover}
|
|
190
|
+
verticalPosition={PopoverVerticalPositions.Bottom}
|
|
191
|
+
horizontalPosition={PopoverHorizontalPositions.Right}
|
|
192
|
+
content={this._popoverContent()}>
|
|
193
|
+
<Fragment>
|
|
194
|
+
<button className={styles.selectWrapper} tabIndex={1}>
|
|
195
|
+
<div className={styles.select}>
|
|
196
|
+
{reportContentType > -1
|
|
197
|
+
? get(this._getContentType(), 'label', '')
|
|
198
|
+
: DEFAULT_CONTENT_TYPE}
|
|
199
|
+
</div>
|
|
200
|
+
<div className={styles.downArrow} />
|
|
201
|
+
</button>
|
|
202
|
+
</Fragment>
|
|
203
|
+
</Popover>
|
|
204
|
+
<form onSubmit={this._handleSubmit}>
|
|
205
|
+
<textarea
|
|
206
|
+
className={[
|
|
207
|
+
styles.textarea,
|
|
208
|
+
isTextareaActive ? styles.active : '',
|
|
209
|
+
].join(' ')}
|
|
210
|
+
onInput={this._onContentChange}
|
|
211
|
+
onFocus={this._handleFocus}
|
|
212
|
+
onBlur={this._handleBlur}
|
|
213
|
+
tabIndex={1}
|
|
214
|
+
placeholder="Describe what you saw..."
|
|
215
|
+
value={reportContent}
|
|
216
|
+
maxLength={reportLength}
|
|
217
|
+
/>
|
|
218
|
+
<div className={styles.submitWrapper}>
|
|
219
|
+
<div className={styles.characterCounter}>
|
|
220
|
+
{`${reportContent.length}/${reportLength}`}
|
|
221
|
+
</div>
|
|
222
|
+
<Tooltip label={tooltipMessage} classNames={styles.tooltip}>
|
|
223
|
+
<button
|
|
224
|
+
className={[
|
|
225
|
+
styles.submitButton,
|
|
226
|
+
reportContentType === -1 ? styles.disabled : '',
|
|
227
|
+
].join(' ')}
|
|
228
|
+
tabIndex={1}
|
|
229
|
+
type="submit">
|
|
230
|
+
Report
|
|
231
|
+
</button>
|
|
232
|
+
</Tooltip>
|
|
233
|
+
</div>
|
|
234
|
+
</form>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./plugin-button";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './popover-menu';
|
|
@@ -0,0 +1,52 @@
|
|
|
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: PopoverMenuProps) {
|
|
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
|
+
}
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference path="../node_modules/@playkit-js-contrib/common/global-types/index.d.ts" />
|
|
2
|
+
|
|
3
|
+
declare module "*.scss" {
|
|
4
|
+
const content: { [className: string]: string };
|
|
5
|
+
export = content;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare module "*.svg" {
|
|
9
|
+
const content: any;
|
|
10
|
+
export default content;
|
|
11
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./moderation-plugin";
|