123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- // Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu
- import { SettingsMenuItem } from './settings-menu-item'
- import { toTitleCase } from '../utils'
- import videojs, { VideoJsPlayer } from 'video.js'
- import { SettingsDialog } from './settings-dialog'
- import { SettingsPanel } from './settings-panel'
- import { SettingsPanelChild } from './settings-panel-child'
- const Button = videojs.getComponent('Button')
- const Menu = videojs.getComponent('Menu')
- const Component = videojs.getComponent('Component')
- export interface SettingsButtonOptions extends videojs.ComponentOptions {
- entries: any[]
- setup?: {
- maxHeightOffset: number
- }
- }
- class SettingsButton extends Button {
- dialog: SettingsDialog
- dialogEl: HTMLElement
- menu: videojs.Menu
- panel: SettingsPanel
- panelChild: SettingsPanelChild
- addSettingsItemHandler: typeof SettingsButton.prototype.onAddSettingsItem
- disposeSettingsItemHandler: typeof SettingsButton.prototype.onDisposeSettingsItem
- playerClickHandler: typeof SettingsButton.prototype.onPlayerClick
- userInactiveHandler: typeof SettingsButton.prototype.onUserInactive
- private settingsButtonOptions: SettingsButtonOptions
- constructor (player: VideoJsPlayer, options?: SettingsButtonOptions) {
- super(player, options)
- this.settingsButtonOptions = options
- this.controlText('Settings')
- this.dialog = this.player().addChild('settingsDialog')
- this.dialogEl = this.dialog.el() as HTMLElement
- this.menu = null
- this.panel = this.dialog.addChild('settingsPanel')
- this.panelChild = this.panel.addChild('settingsPanelChild')
- this.addClass('vjs-settings')
- this.el().setAttribute('aria-label', 'Settings Button')
- // Event handlers
- this.addSettingsItemHandler = this.onAddSettingsItem.bind(this)
- this.disposeSettingsItemHandler = this.onDisposeSettingsItem.bind(this)
- this.playerClickHandler = this.onPlayerClick.bind(this)
- this.userInactiveHandler = this.onUserInactive.bind(this)
- this.buildMenu()
- this.bindEvents()
- // Prepare the dialog
- this.player().one('play', () => this.hideDialog())
- }
- onPlayerClick (event: MouseEvent) {
- const element = event.target as HTMLElement
- if (element.classList.contains('vjs-settings') || element.parentElement.classList.contains('vjs-settings')) {
- return
- }
- if (!this.dialog.hasClass('vjs-hidden')) {
- this.hideDialog()
- }
- }
- onDisposeSettingsItem (event: any, name: string) {
- if (name === undefined) {
- const children = this.menu.children()
- while (children.length > 0) {
- children[0].dispose()
- this.menu.removeChild(children[0])
- }
- this.addClass('vjs-hidden')
- } else {
- const item = this.menu.getChild(name)
- if (item) {
- item.dispose()
- this.menu.removeChild(item)
- }
- }
- this.hideDialog()
- if (this.settingsButtonOptions.entries.length === 0) {
- this.addClass('vjs-hidden')
- }
- }
- onAddSettingsItem (event: any, data: any) {
- const [ entry, options ] = data
- this.addMenuItem(entry, options)
- this.removeClass('vjs-hidden')
- }
- onUserInactive () {
- if (!this.dialog.hasClass('vjs-hidden')) {
- this.hideDialog()
- }
- }
- bindEvents () {
- this.player().on('click', this.playerClickHandler)
- this.player().on('addsettingsitem', this.addSettingsItemHandler)
- this.player().on('disposesettingsitem', this.disposeSettingsItemHandler)
- this.player().on('userinactive', this.userInactiveHandler)
- }
- buildCSSClass () {
- return `vjs-icon-settings ${super.buildCSSClass()}`
- }
- handleClick () {
- if (this.dialog.hasClass('vjs-hidden')) {
- this.showDialog()
- } else {
- this.hideDialog()
- }
- }
- showDialog () {
- this.player().peertube().onMenuOpen();
- (this.menu.el() as HTMLElement).style.opacity = '1'
- this.dialog.show()
- this.setDialogSize(this.getComponentSize(this.menu))
- }
- hideDialog () {
- this.player_.peertube().onMenuClosed()
- this.dialog.hide()
- this.setDialogSize(this.getComponentSize(this.menu));
- (this.menu.el() as HTMLElement).style.opacity = '1'
- this.resetChildren()
- }
- getComponentSize (element: videojs.Component | HTMLElement) {
- let width: number = null
- let height: number = null
- // Could be component or just DOM element
- if (element instanceof Component) {
- const el = element.el() as HTMLElement
- width = el.offsetWidth
- height = el.offsetHeight;
- (element as any).width = width;
- (element as any).height = height
- } else {
- width = element.offsetWidth
- height = element.offsetHeight
- }
- return [ width, height ]
- }
- setDialogSize ([ width, height ]: number[]) {
- if (typeof height !== 'number') {
- return
- }
- const offset = this.settingsButtonOptions.setup.maxHeightOffset
- const maxHeight = (this.player().el() as HTMLElement).offsetHeight - offset // FIXME: typings
- const panelEl = this.panel.el() as HTMLElement
- if (height > maxHeight) {
- height = maxHeight
- width += 17
- panelEl.style.maxHeight = `${height}px`
- } else if (panelEl.style.maxHeight !== '') {
- panelEl.style.maxHeight = ''
- }
- this.dialogEl.style.width = `${width}px`
- this.dialogEl.style.height = `${height}px`
- }
- buildMenu () {
- this.menu = new Menu(this.player())
- this.menu.addClass('vjs-main-menu')
- const entries = this.settingsButtonOptions.entries
- if (entries.length === 0) {
- this.addClass('vjs-hidden')
- this.panelChild.addChild(this.menu)
- return
- }
- for (const entry of entries) {
- this.addMenuItem(entry, this.settingsButtonOptions)
- }
- this.panelChild.addChild(this.menu)
- }
- addMenuItem (entry: any, options: any) {
- const openSubMenu = function (this: any) {
- if (videojs.dom.hasClass(this.el_, 'open')) {
- videojs.dom.removeClass(this.el_, 'open')
- } else {
- videojs.dom.addClass(this.el_, 'open')
- }
- }
- options.name = toTitleCase(entry)
- const newOptions = Object.assign({}, options, { entry, menuButton: this })
- const settingsMenuItem = new SettingsMenuItem(this.player(), newOptions)
- this.menu.addChild(settingsMenuItem)
- // Hide children to avoid sub menus stacking on top of each other
- // or having multiple menus open
- settingsMenuItem.on('click', videojs.bind(this, this.hideChildren))
- // Whether to add or remove selected class on the settings sub menu element
- settingsMenuItem.on('click', openSubMenu)
- }
- resetChildren () {
- for (const menuChild of this.menu.children()) {
- (menuChild as SettingsMenuItem).reset()
- }
- }
- /**
- * Hide all the sub menus
- */
- hideChildren () {
- for (const menuChild of this.menu.children()) {
- (menuChild as SettingsMenuItem).hideSubMenu()
- }
- }
- }
- Component.registerComponent('SettingsButton', SettingsButton)
- export { SettingsButton }
|