diff options
Diffstat (limited to 'client/src/assets/player/videojs-components/settings-menu-button.ts')
-rw-r--r-- | client/src/assets/player/videojs-components/settings-menu-button.ts | 191 |
1 files changed, 71 insertions, 120 deletions
diff --git a/client/src/assets/player/videojs-components/settings-menu-button.ts b/client/src/assets/player/videojs-components/settings-menu-button.ts index b700f4be6..eae628e7d 100644 --- a/client/src/assets/player/videojs-components/settings-menu-button.ts +++ b/client/src/assets/player/videojs-components/settings-menu-button.ts | |||
@@ -1,43 +1,52 @@ | |||
1 | // Author: Yanko Shterev | 1 | // Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu |
2 | // Thanks https://github.com/yshterev/videojs-settings-menu | ||
3 | |||
4 | // FIXME: something weird with our path definition in tsconfig and typings | ||
5 | // @ts-ignore | ||
6 | import * as videojs from 'video.js' | ||
7 | |||
8 | import { SettingsMenuItem } from './settings-menu-item' | 2 | import { SettingsMenuItem } from './settings-menu-item' |
9 | import { VideoJSComponentInterface, videojsUntyped } from '../peertube-videojs-typings' | ||
10 | import { toTitleCase } from '../utils' | 3 | import { toTitleCase } from '../utils' |
4 | import videojs, { VideoJsPlayer } from 'video.js' | ||
5 | |||
6 | import { SettingsDialog } from './settings-dialog' | ||
7 | import { SettingsPanel } from './settings-panel' | ||
8 | import { SettingsPanelChild } from './settings-panel-child' | ||
11 | 9 | ||
12 | const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') | 10 | const Button = videojs.getComponent('Button') |
13 | const Menu: VideoJSComponentInterface = videojsUntyped.getComponent('Menu') | 11 | const Menu = videojs.getComponent('Menu') |
14 | const Component: VideoJSComponentInterface = videojsUntyped.getComponent('Component') | 12 | const Component = videojs.getComponent('Component') |
13 | |||
14 | export interface SettingsButtonOptions extends videojs.ComponentOptions { | ||
15 | entries: any[] | ||
16 | setup?: { | ||
17 | maxHeightOffset: number | ||
18 | } | ||
19 | } | ||
15 | 20 | ||
16 | class SettingsButton extends Button { | 21 | class SettingsButton extends Button { |
17 | playerComponent = videojs.Player | 22 | dialog: SettingsDialog |
18 | dialog: any | 23 | dialogEl: HTMLElement |
19 | dialogEl: any | 24 | menu: videojs.Menu |
20 | menu: any | 25 | panel: SettingsPanel |
21 | panel: any | 26 | panelChild: SettingsPanelChild |
22 | panelChild: any | 27 | |
23 | 28 | addSettingsItemHandler: typeof SettingsButton.prototype.onAddSettingsItem | |
24 | addSettingsItemHandler: Function | 29 | disposeSettingsItemHandler: typeof SettingsButton.prototype.onDisposeSettingsItem |
25 | disposeSettingsItemHandler: Function | 30 | playerClickHandler: typeof SettingsButton.prototype.onPlayerClick |
26 | playerClickHandler: Function | 31 | userInactiveHandler: typeof SettingsButton.prototype.onUserInactive |
27 | userInactiveHandler: Function | 32 | |
28 | 33 | private settingsButtonOptions: SettingsButtonOptions | |
29 | constructor (player: videojs.Player, options: any) { | 34 | |
35 | constructor (player: VideoJsPlayer, options?: SettingsButtonOptions) { | ||
30 | super(player, options) | 36 | super(player, options) |
31 | 37 | ||
32 | this.playerComponent = player | 38 | this.settingsButtonOptions = options |
33 | this.dialog = this.playerComponent.addChild('settingsDialog') | 39 | |
34 | this.dialogEl = this.dialog.el_ | 40 | this.controlText('Settings') |
41 | |||
42 | this.dialog = this.player().addChild('settingsDialog') | ||
43 | this.dialogEl = this.dialog.el() as HTMLElement | ||
35 | this.menu = null | 44 | this.menu = null |
36 | this.panel = this.dialog.addChild('settingsPanel') | 45 | this.panel = this.dialog.addChild('settingsPanel') |
37 | this.panelChild = this.panel.addChild('settingsPanelChild') | 46 | this.panelChild = this.panel.addChild('settingsPanelChild') |
38 | 47 | ||
39 | this.addClass('vjs-settings') | 48 | this.addClass('vjs-settings') |
40 | this.el_.setAttribute('aria-label', 'Settings Button') | 49 | this.el().setAttribute('aria-label', 'Settings Button') |
41 | 50 | ||
42 | // Event handlers | 51 | // Event handlers |
43 | this.addSettingsItemHandler = this.onAddSettingsItem.bind(this) | 52 | this.addSettingsItemHandler = this.onAddSettingsItem.bind(this) |
@@ -84,7 +93,7 @@ class SettingsButton extends Button { | |||
84 | 93 | ||
85 | this.hideDialog() | 94 | this.hideDialog() |
86 | 95 | ||
87 | if (this.options_.entries.length === 0) { | 96 | if (this.settingsButtonOptions.entries.length === 0) { |
88 | this.addClass('vjs-hidden') | 97 | this.addClass('vjs-hidden') |
89 | } | 98 | } |
90 | } | 99 | } |
@@ -103,10 +112,10 @@ class SettingsButton extends Button { | |||
103 | } | 112 | } |
104 | 113 | ||
105 | bindEvents () { | 114 | bindEvents () { |
106 | this.playerComponent.on('click', this.playerClickHandler) | 115 | this.player().on('click', this.playerClickHandler) |
107 | this.playerComponent.on('addsettingsitem', this.addSettingsItemHandler) | 116 | this.player().on('addsettingsitem', this.addSettingsItemHandler) |
108 | this.playerComponent.on('disposesettingsitem', this.disposeSettingsItemHandler) | 117 | this.player().on('disposesettingsitem', this.disposeSettingsItemHandler) |
109 | this.playerComponent.on('userinactive', this.userInactiveHandler) | 118 | this.player().on('userinactive', this.userInactiveHandler) |
110 | } | 119 | } |
111 | 120 | ||
112 | buildCSSClass () { | 121 | buildCSSClass () { |
@@ -122,9 +131,9 @@ class SettingsButton extends Button { | |||
122 | } | 131 | } |
123 | 132 | ||
124 | showDialog () { | 133 | showDialog () { |
125 | this.player_.peertube().onMenuOpen() | 134 | this.player().peertube().onMenuOpen(); |
126 | 135 | ||
127 | this.menu.el_.style.opacity = '1' | 136 | (this.menu.el() as HTMLElement).style.opacity = '1' |
128 | this.dialog.show() | 137 | this.dialog.show() |
129 | 138 | ||
130 | this.setDialogSize(this.getComponentSize(this.menu)) | 139 | this.setDialogSize(this.getComponentSize(this.menu)) |
@@ -134,23 +143,24 @@ class SettingsButton extends Button { | |||
134 | this.player_.peertube().onMenuClosed() | 143 | this.player_.peertube().onMenuClosed() |
135 | 144 | ||
136 | this.dialog.hide() | 145 | this.dialog.hide() |
137 | this.setDialogSize(this.getComponentSize(this.menu)) | 146 | this.setDialogSize(this.getComponentSize(this.menu)); |
138 | this.menu.el_.style.opacity = '1' | 147 | (this.menu.el() as HTMLElement).style.opacity = '1' |
139 | this.resetChildren() | 148 | this.resetChildren() |
140 | } | 149 | } |
141 | 150 | ||
142 | getComponentSize (element: any) { | 151 | getComponentSize (element: videojs.Component | HTMLElement) { |
143 | let width: number = null | 152 | let width: number = null |
144 | let height: number = null | 153 | let height: number = null |
145 | 154 | ||
146 | // Could be component or just DOM element | 155 | // Could be component or just DOM element |
147 | if (element instanceof Component) { | 156 | if (element instanceof Component) { |
148 | width = element.el_.offsetWidth | 157 | const el = element.el() as HTMLElement |
149 | height = element.el_.offsetHeight | 158 | |
159 | width = el.offsetWidth | ||
160 | height = el.offsetHeight; | ||
150 | 161 | ||
151 | // keep width/height as properties for direct use | 162 | (element as any).width = width; |
152 | element.width = width | 163 | (element as any).height = height |
153 | element.height = height | ||
154 | } else { | 164 | } else { |
155 | width = element.offsetWidth | 165 | width = element.offsetWidth |
156 | height = element.offsetHeight | 166 | height = element.offsetHeight |
@@ -164,15 +174,17 @@ class SettingsButton extends Button { | |||
164 | return | 174 | return |
165 | } | 175 | } |
166 | 176 | ||
167 | const offset = this.options_.setup.maxHeightOffset | 177 | const offset = this.settingsButtonOptions.setup.maxHeightOffset |
168 | const maxHeight = this.playerComponent.el_.offsetHeight - offset | 178 | const maxHeight = (this.player().el() as HTMLElement).offsetHeight - offset // FIXME: typings |
179 | |||
180 | const panelEl = this.panel.el() as HTMLElement | ||
169 | 181 | ||
170 | if (height > maxHeight) { | 182 | if (height > maxHeight) { |
171 | height = maxHeight | 183 | height = maxHeight |
172 | width += 17 | 184 | width += 17 |
173 | this.panel.el_.style.maxHeight = `${height}px` | 185 | panelEl.style.maxHeight = `${height}px` |
174 | } else if (this.panel.el_.style.maxHeight !== '') { | 186 | } else if (panelEl.style.maxHeight !== '') { |
175 | this.panel.el_.style.maxHeight = '' | 187 | panelEl.style.maxHeight = '' |
176 | } | 188 | } |
177 | 189 | ||
178 | this.dialogEl.style.width = `${width}px` | 190 | this.dialogEl.style.width = `${width}px` |
@@ -182,7 +194,7 @@ class SettingsButton extends Button { | |||
182 | buildMenu () { | 194 | buildMenu () { |
183 | this.menu = new Menu(this.player()) | 195 | this.menu = new Menu(this.player()) |
184 | this.menu.addClass('vjs-main-menu') | 196 | this.menu.addClass('vjs-main-menu') |
185 | const entries = this.options_.entries | 197 | const entries = this.settingsButtonOptions.entries |
186 | 198 | ||
187 | if (entries.length === 0) { | 199 | if (entries.length === 0) { |
188 | this.addClass('vjs-hidden') | 200 | this.addClass('vjs-hidden') |
@@ -191,7 +203,7 @@ class SettingsButton extends Button { | |||
191 | } | 203 | } |
192 | 204 | ||
193 | for (const entry of entries) { | 205 | for (const entry of entries) { |
194 | this.addMenuItem(entry, this.options_) | 206 | this.addMenuItem(entry, this.settingsButtonOptions) |
195 | } | 207 | } |
196 | 208 | ||
197 | this.panelChild.addChild(this.menu) | 209 | this.panelChild.addChild(this.menu) |
@@ -199,15 +211,17 @@ class SettingsButton extends Button { | |||
199 | 211 | ||
200 | addMenuItem (entry: any, options: any) { | 212 | addMenuItem (entry: any, options: any) { |
201 | const openSubMenu = function (this: any) { | 213 | const openSubMenu = function (this: any) { |
202 | if (videojsUntyped.dom.hasClass(this.el_, 'open')) { | 214 | if (videojs.dom.hasClass(this.el_, 'open')) { |
203 | videojsUntyped.dom.removeClass(this.el_, 'open') | 215 | videojs.dom.removeClass(this.el_, 'open') |
204 | } else { | 216 | } else { |
205 | videojsUntyped.dom.addClass(this.el_, 'open') | 217 | videojs.dom.addClass(this.el_, 'open') |
206 | } | 218 | } |
207 | } | 219 | } |
208 | 220 | ||
209 | options.name = toTitleCase(entry) | 221 | options.name = toTitleCase(entry) |
210 | const settingsMenuItem = new SettingsMenuItem(this.player(), options, entry, this as any) | 222 | |
223 | const newOptions = Object.assign({}, options, { entry, menuButton: this }) | ||
224 | const settingsMenuItem = new SettingsMenuItem(this.player(), newOptions) | ||
211 | 225 | ||
212 | this.menu.addChild(settingsMenuItem) | 226 | this.menu.addChild(settingsMenuItem) |
213 | 227 | ||
@@ -221,7 +235,7 @@ class SettingsButton extends Button { | |||
221 | 235 | ||
222 | resetChildren () { | 236 | resetChildren () { |
223 | for (const menuChild of this.menu.children()) { | 237 | for (const menuChild of this.menu.children()) { |
224 | menuChild.reset() | 238 | (menuChild as SettingsMenuItem).reset() |
225 | } | 239 | } |
226 | } | 240 | } |
227 | 241 | ||
@@ -230,75 +244,12 @@ class SettingsButton extends Button { | |||
230 | */ | 244 | */ |
231 | hideChildren () { | 245 | hideChildren () { |
232 | for (const menuChild of this.menu.children()) { | 246 | for (const menuChild of this.menu.children()) { |
233 | menuChild.hideSubMenu() | 247 | (menuChild as SettingsMenuItem).hideSubMenu() |
234 | } | 248 | } |
235 | } | 249 | } |
236 | 250 | ||
237 | } | 251 | } |
238 | 252 | ||
239 | class SettingsPanel extends Component { | ||
240 | constructor (player: videojs.Player, options: any) { | ||
241 | super(player, options) | ||
242 | } | ||
243 | |||
244 | createEl () { | ||
245 | return super.createEl('div', { | ||
246 | className: 'vjs-settings-panel', | ||
247 | innerHTML: '', | ||
248 | tabIndex: -1 | ||
249 | }) | ||
250 | } | ||
251 | } | ||
252 | |||
253 | class SettingsPanelChild extends Component { | ||
254 | constructor (player: videojs.Player, options: any) { | ||
255 | super(player, options) | ||
256 | } | ||
257 | |||
258 | createEl () { | ||
259 | return super.createEl('div', { | ||
260 | className: 'vjs-settings-panel-child', | ||
261 | innerHTML: '', | ||
262 | tabIndex: -1 | ||
263 | }) | ||
264 | } | ||
265 | } | ||
266 | |||
267 | class SettingsDialog extends Component { | ||
268 | constructor (player: videojs.Player, options: any) { | ||
269 | super(player, options) | ||
270 | this.hide() | ||
271 | } | ||
272 | |||
273 | /** | ||
274 | * Create the component's DOM element | ||
275 | * | ||
276 | * @return {Element} | ||
277 | * @method createEl | ||
278 | */ | ||
279 | createEl () { | ||
280 | const uniqueId = this.id_ | ||
281 | const dialogLabelId = 'TTsettingsDialogLabel-' + uniqueId | ||
282 | const dialogDescriptionId = 'TTsettingsDialogDescription-' + uniqueId | ||
283 | |||
284 | return super.createEl('div', { | ||
285 | className: 'vjs-settings-dialog vjs-modal-overlay', | ||
286 | innerHTML: '', | ||
287 | tabIndex: -1 | ||
288 | }, { | ||
289 | 'role': 'dialog', | ||
290 | 'aria-labelledby': dialogLabelId, | ||
291 | 'aria-describedby': dialogDescriptionId | ||
292 | }) | ||
293 | } | ||
294 | |||
295 | } | ||
296 | |||
297 | SettingsButton.prototype.controlText_ = 'Settings' | ||
298 | |||
299 | Component.registerComponent('SettingsButton', SettingsButton) | 253 | Component.registerComponent('SettingsButton', SettingsButton) |
300 | Component.registerComponent('SettingsDialog', SettingsDialog) | ||
301 | Component.registerComponent('SettingsPanel', SettingsPanel) | ||
302 | Component.registerComponent('SettingsPanelChild', SettingsPanelChild) | ||
303 | 254 | ||
304 | export { SettingsButton, SettingsDialog, SettingsPanel, SettingsPanelChild } | 255 | export { SettingsButton } |