diff --git a/css/_jitsi_popover.scss b/css/_jitsi_popover.scss index 77e983703a..2397b295f3 100644 --- a/css/_jitsi_popover.scss +++ b/css/_jitsi_popover.scss @@ -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; + } + } + } } diff --git a/css/_vertical_filmstrip_overrides.scss b/css/_vertical_filmstrip_overrides.scss new file mode 100644 index 0000000000..c7269f3b2f --- /dev/null +++ b/css/_vertical_filmstrip_overrides.scss @@ -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; + } + } +} diff --git a/css/main.scss b/css/main.scss index 894620bb47..43630b5714 100644 --- a/css/main.scss +++ b/css/main.scss @@ -73,5 +73,6 @@ @import 'policy'; @import 'filmstrip'; @import 'unsupported-browser/main'; +@import 'vertical_filmstrip_overrides'; /* Modules END */ diff --git a/interface_config.js b/interface_config.js index 56e44fd10f..69f7a7d763 100644 --- a/interface_config.js +++ b/interface_config.js @@ -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, diff --git a/modules/UI/UI.js b/modules/UI/UI.js index c404e2813d..b0893362e9 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -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) { diff --git a/modules/UI/util/JitsiPopover.js b/modules/UI/util/JitsiPopover.js index da6dd46221..ce6686b39e 100644 --- a/modules/UI/util/JitsiPopover.js +++ b/modules/UI/util/JitsiPopover.js @@ -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 = '
'; } + return ( - `