feat(vertical-filmstrip): Initial implementation
- Add a class to the body when in vertical filmstrip mode - Override styles as necessary to support the mode - Add an option to make tooltips display from the left - Move the HD Label to the bottom left - Move the remote video menu to the bottom left, move the mute icons to the bottom right - Scale the local video's height and width to fit the filmstrip
This commit is contained in:
parent
82ecfac4ee
commit
aabe641047
|
@ -44,4 +44,28 @@
|
|||
border-width: 5px;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default "top" styles to support popovers appearing from the
|
||||
* left of the popover trigger element.
|
||||
*/
|
||||
&.left {
|
||||
margin-left: -$popoverMenuPadding;
|
||||
margin-top: 0;
|
||||
|
||||
.arrow {
|
||||
border-color: transparent transparent transparent $popoverBg;
|
||||
border-width: 5px 0px 5px 5px;
|
||||
margin-left: 0;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.jitsipopover {
|
||||
&__menu-padding {
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
width: $popoverMenuPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Override other styles to support vertical filmstrip mode.
|
||||
*/
|
||||
.vertical-filmstrip {
|
||||
.filmstrip {
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
|
||||
/**
|
||||
* Hide videos by making them slight to the right.
|
||||
*/
|
||||
.filmstrip__videos {
|
||||
right: 0;
|
||||
transition: right 2s;
|
||||
|
||||
&.hidden {
|
||||
bottom: auto;
|
||||
right: -196px;
|
||||
}
|
||||
}
|
||||
|
||||
#filmstripLocalVideo {
|
||||
height: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecssary padding that is normally used to prevent horizontal
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
#filmstripRemoteVideos {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#filmstripRemoteVideos {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
overflow-x: hidden !important;
|
||||
|
||||
.remote-videos-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate the hide filmstrip icon so it points towards the right edge
|
||||
* of the screen.
|
||||
*/
|
||||
&__toolbar {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the remote video menu trigger to the bottom left of the
|
||||
* video thumbnail.
|
||||
*/
|
||||
.remotevideomenu {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: auto;
|
||||
right: auto;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
#remoteVideos {
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.videocontainer {
|
||||
/**
|
||||
* Move status icons to the bottom right of the thumbnail.
|
||||
*/
|
||||
&__toolbar {
|
||||
text-align: right;
|
||||
|
||||
.toolbar-icon {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video-state-indicator {
|
||||
bottom: 30px;
|
||||
left: 30px;
|
||||
right: auto;
|
||||
top: auto;
|
||||
|
||||
/**
|
||||
* Move the label to the bottom left of the screen
|
||||
*/
|
||||
&#videoResolutionLabel {
|
||||
left: 60px;
|
||||
z-index: $poweredByZ;
|
||||
|
||||
/**
|
||||
* Open the menu above the label.
|
||||
*/
|
||||
.video-state-indicator-menu {
|
||||
bottom: calc(100% - 15px);
|
||||
left: -10px;
|
||||
|
||||
/**
|
||||
* Create padding for mouse travel on hover.
|
||||
*/
|
||||
padding-bottom: 20px;
|
||||
right: auto;
|
||||
top: auto;
|
||||
|
||||
.video-state-indicator-menu-options {
|
||||
margin: 0;
|
||||
|
||||
/**
|
||||
* The menu arrow should point down
|
||||
*/
|
||||
&::after {
|
||||
border-color: $popoverBg transparent transparent;
|
||||
bottom: -10px;
|
||||
left: 15px;
|
||||
right: auto;
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&#recordingLabel {
|
||||
left: 110px;
|
||||
z-index: $poweredByZ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move toastr closer to the bottom of the screen and move left to avoid
|
||||
* overlapping of videos when they are configured at default height.
|
||||
*/
|
||||
#toast-container {
|
||||
&.notification-bottom-right {
|
||||
bottom: 25px;
|
||||
right: 130 + 2 * ($thumbnailVideoMargin + $thumbnailsBorder) + $thumbnailVideoBorder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -73,5 +73,6 @@
|
|||
@import 'policy';
|
||||
@import 'filmstrip';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'vertical_filmstrip_overrides';
|
||||
|
||||
/* Modules END */
|
||||
|
|
|
@ -55,6 +55,12 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
|||
* Whether to only show the filmstrip (and hide the toolbar).
|
||||
*/
|
||||
filmStripOnly: false,
|
||||
|
||||
/**
|
||||
* Whether to show thumbnails in filmstrip as a column instead of as a row.
|
||||
*/
|
||||
VERTICAL_FILMSTRIP: false,
|
||||
|
||||
//A html text to be shown to guests on the close page, false disables it
|
||||
CLOSE_PAGE_GUEST_HINT: false,
|
||||
RANDOM_AVATAR_URL_PREFIX: false,
|
||||
|
|
|
@ -339,6 +339,10 @@ UI.start = function () {
|
|||
JitsiPopover.enabled = false;
|
||||
}
|
||||
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
$("body").addClass("vertical-filmstrip");
|
||||
}
|
||||
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
|
|
|
@ -1,4 +1,74 @@
|
|||
/* global $ */
|
||||
|
||||
const positionConfigurations = {
|
||||
left: {
|
||||
|
||||
// Align the popover's right side to the target element.
|
||||
my: 'right',
|
||||
|
||||
// Align the popover to the left side of the target element.
|
||||
at: 'left',
|
||||
|
||||
// Force the popover to fit within the viewport.
|
||||
collision: 'fit',
|
||||
|
||||
/**
|
||||
* Callback invoked by jQuery UI tooltip.
|
||||
*
|
||||
* @param {Object} position - The top and bottom position the popover
|
||||
* element should be set at.
|
||||
* @param {Object} element. - Additional size and position information
|
||||
* about the popover element and target.
|
||||
* @param {Object} elements.element - Has position and size related data
|
||||
* for the popover element itself.
|
||||
* @param {Object} elements.target - Has position and size related data
|
||||
* for the target element the popover displays from.
|
||||
*/
|
||||
using: function setPositionLeft(position, elements) {
|
||||
const { element, target } = elements;
|
||||
|
||||
$('.jitsipopover').css({
|
||||
display: 'table',
|
||||
left: position.left,
|
||||
top: position.top
|
||||
});
|
||||
|
||||
// Move additional padding to the right edge of the popover and
|
||||
// allow css to take care of width. The padding is used to maintain
|
||||
// a hover state between the target and the popover.
|
||||
$('.jitsipopover > .jitsipopover__menu-padding').css({
|
||||
left: element.width
|
||||
});
|
||||
|
||||
// Find the distance from the top of the popover to the center of
|
||||
// the target and use that value to position the arrow to point to
|
||||
// it.
|
||||
const verticalCenterOfTarget = target.height / 2;
|
||||
const verticalDistanceFromTops = target.top - element.top;
|
||||
const verticalPositionOfTargetCenter
|
||||
= verticalDistanceFromTops + verticalCenterOfTarget;
|
||||
|
||||
$('.jitsipopover > .arrow').css({
|
||||
left: element.width,
|
||||
top: verticalPositionOfTargetCenter
|
||||
});
|
||||
}
|
||||
},
|
||||
top: {
|
||||
my: "bottom",
|
||||
at: "top",
|
||||
collision: "fit",
|
||||
using: function setPositionTop(position, elements) {
|
||||
var calcLeft = elements.target.left - elements.element.left +
|
||||
elements.target.width/2;
|
||||
$(".jitsipopover").css(
|
||||
{top: position.top, left: position.left, display: "table"});
|
||||
$(".jitsipopover > .arrow").css({left: calcLeft});
|
||||
$(".jitsipopover > .jitsipopover__menu-padding").css(
|
||||
{left: calcLeft - 50});
|
||||
}
|
||||
}
|
||||
};
|
||||
var JitsiPopover = (function () {
|
||||
/**
|
||||
* The default options
|
||||
|
@ -7,7 +77,8 @@ var JitsiPopover = (function () {
|
|||
skin: 'white',
|
||||
content: '',
|
||||
hasArrow: true,
|
||||
onBeforePosition: undefined
|
||||
onBeforePosition: undefined,
|
||||
position: 'top'
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -21,7 +92,6 @@ var JitsiPopover = (function () {
|
|||
function JitsiPopover(element, options)
|
||||
{
|
||||
this.options = Object.assign({}, defaultOptions, options);
|
||||
|
||||
this.elementIsHovered = false;
|
||||
this.popoverIsHovered = false;
|
||||
this.popoverShown = false;
|
||||
|
@ -45,12 +115,15 @@ var JitsiPopover = (function () {
|
|||
* Returns template for popover
|
||||
*/
|
||||
JitsiPopover.prototype.getTemplate = function () {
|
||||
const { hasArrow, position, skin } = this.options;
|
||||
|
||||
let arrow = '';
|
||||
if (this.options.hasArrow) {
|
||||
if (hasArrow) {
|
||||
arrow = '<div class="arrow"></div>';
|
||||
}
|
||||
|
||||
return (
|
||||
`<div class="jitsipopover ${this.options.skin}">
|
||||
`<div class="jitsipopover ${skin} ${position}">
|
||||
${arrow}
|
||||
<div class="jitsipopover__content"></div>
|
||||
<div class="jitsipopover__menu-padding"></div>
|
||||
|
@ -129,21 +202,14 @@ var JitsiPopover = (function () {
|
|||
* Refreshes the position of the popover.
|
||||
*/
|
||||
JitsiPopover.prototype.refreshPosition = function () {
|
||||
$(".jitsipopover").position({
|
||||
my: "bottom",
|
||||
at: "top",
|
||||
collision: "fit",
|
||||
of: this.element,
|
||||
using: function (position, elements) {
|
||||
var calcLeft = elements.target.left - elements.element.left +
|
||||
elements.target.width/2;
|
||||
$(".jitsipopover").css(
|
||||
{top: position.top, left: position.left, display: "table"});
|
||||
$(".jitsipopover > .arrow").css({left: calcLeft});
|
||||
$(".jitsipopover > .jitsipopover__menu-padding").css(
|
||||
{left: calcLeft - 50});
|
||||
const positionOptions = Object.assign(
|
||||
{},
|
||||
positionConfigurations[this.options.position],
|
||||
{
|
||||
of: this.element
|
||||
}
|
||||
});
|
||||
);
|
||||
$(".jitsipopover").position(positionOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global $, APP */
|
||||
/* global $, APP, interfaceConfig */
|
||||
/* jshint -W101 */
|
||||
|
||||
import JitsiPopover from "../util/JitsiPopover";
|
||||
|
@ -309,7 +309,8 @@ ConnectionIndicator.prototype.create = function () {
|
|||
this.popover = new JitsiPopover($(element), {
|
||||
content: popoverContent,
|
||||
skin: "black",
|
||||
onBeforePosition: el => APP.translation.translateElement(el)
|
||||
onBeforePosition: el => APP.translation.translateElement(el),
|
||||
position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
|
||||
});
|
||||
|
||||
// override popover show method to make sure we will update the content
|
||||
|
|
|
@ -178,7 +178,10 @@ const Filmstrip = {
|
|||
* @returns {number} height
|
||||
*/
|
||||
getFilmstripHeight() {
|
||||
if (this.isFilmstripVisible()) {
|
||||
// FIXME Make it more clear the getFilmstripHeight check is used in
|
||||
// horizontal film strip mode for calculating how tall large video
|
||||
// display should be.
|
||||
if (this.isFilmstripVisible() && !interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
return $(`.${this.filmstripContainerClassName}`).outerHeight();
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -365,13 +368,27 @@ const Filmstrip = {
|
|||
(remoteLocalWidthRatio * numberRemoteThumbs + 1), availableHeight *
|
||||
interfaceConfig.LOCAL_THUMBNAIL_RATIO);
|
||||
const h = lW / interfaceConfig.LOCAL_THUMBNAIL_RATIO;
|
||||
|
||||
const removeVideoWidth = lW * remoteLocalWidthRatio;
|
||||
|
||||
let localVideo;
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
// scale both width and height
|
||||
localVideo = {
|
||||
thumbWidth: removeVideoWidth,
|
||||
thumbHeight: h * remoteLocalWidthRatio
|
||||
};
|
||||
} else {
|
||||
localVideo = {
|
||||
thumbWidth: lW,
|
||||
thumbHeight: h
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
localVideo:{
|
||||
thumbWidth: lW,
|
||||
thumbHeight: h
|
||||
},
|
||||
localVideo,
|
||||
remoteVideo: {
|
||||
thumbWidth: lW * remoteLocalWidthRatio,
|
||||
thumbWidth: removeVideoWidth,
|
||||
thumbHeight: h
|
||||
}
|
||||
};
|
||||
|
@ -407,10 +424,15 @@ const Filmstrip = {
|
|||
}));
|
||||
}
|
||||
promises.push(new Promise((resolve) => {
|
||||
this.filmstrip.animate({
|
||||
// adds 2 px because of small video 1px border
|
||||
height: remote.thumbHeight + 2
|
||||
}, this._getAnimateOptions(animate, resolve));
|
||||
// Let CSS take care of height in vertical filmstrip mode.
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
resolve();
|
||||
} else {
|
||||
this.filmstrip.animate({
|
||||
// adds 2 px because of small video 1px border
|
||||
height: remote.thumbHeight + 2
|
||||
}, this._getAnimateOptions(animate, resolve));
|
||||
}
|
||||
}));
|
||||
|
||||
promises.push(new Promise(() => {
|
||||
|
|
|
@ -91,7 +91,8 @@ RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
|
|||
content: popupMenuElement.outerHTML,
|
||||
skin: "black",
|
||||
hasArrow: false,
|
||||
onBeforePosition: el => APP.translation.translateElement(el)
|
||||
onBeforePosition: el => APP.translation.translateElement(el),
|
||||
position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
|
||||
};
|
||||
let element = $("#" + this.videoSpanId + " .remotevideomenu");
|
||||
this.popover = new JitsiPopover(element, options);
|
||||
|
|
Loading…
Reference in New Issue