import { $, isIOS, isMobile } from '@nusaplayer/core'
import { Icons } from '../functions/icons'
import { controllerHidden, icon, off, on, tooltip, fullscreenSafeArea } from '../style'
import { formatTime, screenShot } from '../utils'
import renderVolumeBar from './VolumeBar'
import renderProgress from './Progress'
import { isFullscreen, isWebFullscreen } from '../listeners/fullscreen'
import type { UIInterface } from '../types'
import {
controllers,
dropdown,
dropdownHoverable,
expand,
time,
live,
withIcon,
centerProgressWrap,
textIcon
} from './ControllerBottom.style'
import { progress } from './Progress.style'
export const controllerBottomWrap = $.css({
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
'z-index': 7,
padding: isMobile ? '0 0.3em' : '0 0.5em',
transition: 'transform 0.3s ease, padding 0.3s ease',
'min-height': 'var(--control-bar-height)',
'&::before': {
position: 'absolute',
content: "''",
width: '100%',
display: 'block',
bottom: 0,
left: 0,
'z-index': -1,
top: '-2.75em',
transition: 'opacity 0.3s ease',
'pointer-events': 'none',
'background-image': 'linear-gradient(transparent, rgba(0, 0, 0, .72))'
},
...fullscreenSafeArea({ bottom: '0px', x: isMobile ? '0.3em' : '0.5em' }),
[`@global .${controllerHidden} &`]: {
'pointer-events': 'none',
transform: 'translateY(calc(100% - 0.55em))',
padding: 0,
'&::before': { opacity: 0 }
},
[`@global .${controllerHidden} .${progress}`]: {
'padding-left': 0,
'padding-right': 0
}
})
const render = (it: UIInterface, $el: HTMLDivElement) => {
const { player, config } = it
const el = $.render($.create(`div.${controllerBottomWrap}`), $el)
if (!config.theme.progress?.mini || player.options.isLive) {
$.css({
[`@global .${controllerHidden} .${controllerBottomWrap}`]: {
transform: 'translateY(100%)'
}
})
}
const [
playLabel,
pauseLabel,
screenshotLabel,
pipLabel,
fullscreenLabel,
webFullscreenLabel,
previousLabel,
nextLabel
] = [
player.locales.get('Play'),
player.locales.get('Pause'),
player.locales.get('Screenshot'),
player.locales.get('Picture in Picture'),
player.locales.get('Fullscreen'),
player.locales.get('WebFullscreen'),
player.locales.get('Previous'),
player.locales.get('Next')
]
const showWebFullscreen = Boolean(
config.fullscreen && config.webFullscreen !== false && player.isFullscreenEnabled
)
const fullscreenButtonLabel = player.isFullscreenEnabled ? fullscreenLabel : webFullscreenLabel
const [previousSvg, nextSvg] = [Icons.get('previous') || '', Icons.get('next') || '']
const $dom = (it.$controllerBottom = $.create(
`div.${controllers}`,
{},
`
${
previousSvg &&
``
}
${nextSvg && ``}
${
player.options.isLive
? ``
: `${
player.options.isLive || player.$video.preload == 'none' ? '00:00' : '00:00 / --:--'
}`
}
${!isIOS ? `
` : ''}
${
!config.theme.controller?.header && config.screenshot
? `
`
: ''
}
${
!config.theme.controller?.header && config.pictureInPicture && player.isPipEnabled
? `
`
: ''
}
${
showWebFullscreen
? `
`
: ''
}
${
config.fullscreen
? `
`
: ''
}
`
))
if (
(config.theme.progress?.position == 'auto' && isMobile) ||
config.theme.progress?.position == 'center'
) {
renderProgress(it, $dom.querySelector(`.${centerProgressWrap}`)!)
} else {
renderProgress(it, el)
}
const $volume = $dom.querySelector('button[aria-label=Volume]')!
// IOS只能使用物理按键控制音量大小
if (!isIOS) renderVolumeBar(player, $volume.nextElementSibling! as HTMLDivElement)
const $play = $dom.querySelector(`button[aria-label=${playLabel}]`)!
const $time = $dom.querySelector('.' + time)!
const $fullscreen = $dom.querySelector(
`button[aria-label="${fullscreenButtonLabel}"]`
)
const $webFullscreen = $dom.querySelector(
`button[aria-label="${webFullscreenLabel}"]`
)
const $pip = $dom.querySelector(`button[aria-label="${pipLabel}"]`)
const switcher = (el: HTMLElement, display: boolean) => {
el.classList.add(display ? on : off)
el.classList.remove(display ? off : on)
}
if (config.fullscreen) {
player.on('fullscreenchange', () =>
setTimeout(() => {
if ($webFullscreen) switcher($webFullscreen, isWebFullscreen(player))
if ($fullscreen) {
switcher(
$fullscreen,
player.isFullscreenEnabled ? player.isFullScreen : isFullscreen(player)
)
}
})
)
}
if (config.pictureInPicture && $pip) {
player.on(['enterpictureinpicture', 'leavepictureinpicture'], () => switcher($pip, player.isInPip))
}
player.on(['play', 'pause', 'videosourcechange'], () => {
$play.setAttribute('aria-label', player.isPlaying ? pauseLabel : playLabel)
switcher($play, player.isPlaying)
})
const LIVE = player.locales.get('Live')
player.on('volumechange', () => switcher($volume, player.isMuted))
player.on(['durationchange', 'timeupdate', 'seeking', 'seeked'], () => {
if (player.options.isLive) {
$time.innerText = `${LIVE} ${formatTime(player.currentTime < 0 ? player.duration : player.currentTime)}`
return
}
$time.innerText = `${formatTime(player.currentTime)} / ${formatTime(player.duration)}`
})
player.on('videosourcechange', () => {
$time.innerText = player.options.isLive || player.$video.preload == 'none' ? '00:00' : '00:00 / --:--'
})
$dom.addEventListener('click', (e: Event) => {
const target = e.target! as HTMLDivElement
const label = target.getAttribute('aria-label')
switch (label) {
case playLabel:
case pauseLabel:
return player.togglePlay()
case 'Volume':
if (isMobile && !isIOS) {
const wrap = $volume.closest(`.${dropdown}`)
if (wrap) {
const isOpen = wrap.getAttribute('data-open') == 'true'
player.$root.querySelectorAll('[data-open="true"]').forEach((el) => {
if (el !== wrap) el.removeAttribute('data-open')
})
if (isOpen) wrap.removeAttribute('data-open')
else wrap.setAttribute('data-open', 'true')
}
break
}
if (player.isMuted) {
player.unmute()
} else {
player.mute()
}
break
case pipLabel:
return player.togglePip()
case webFullscreenLabel:
player.emit('fullscreenchange', { isWeb: true })
return
case fullscreenLabel:
if (!player.isFullscreenEnabled) {
player.emit('fullscreenchange', { isWeb: true })
} else {
if (isWebFullscreen(player)) {
player.emit('fullscreenchange', { isWeb: true })
}
player.toggleFullScreen()
}
return
case screenshotLabel:
screenShot(player)
break
case nextLabel:
player.emit('next')
break
case previousLabel:
player.emit('previous')
break
default:
break
}
})
$.render($dom, el)
}
export default render