diff --git a/core/modules/responsive_preview/css/responsive-preview.module.css b/core/modules/responsive_preview/css/responsive-preview.module.css index 6dd4dcc..5e529b2 100644 --- a/core/modules/responsive_preview/css/responsive-preview.module.css +++ b/core/modules/responsive_preview/css/responsive-preview.module.css @@ -76,17 +76,12 @@ } .responsive-preview-modal-background { bottom: 0; - height: 100%; left: 0; - position: static; + position: absolute; right: 0; top: 0; - width: 100%; z-index: 1; } -.responsive-preview.active .responsive-preview-modal-background { - position: fixed; -} /** * Preview iframe. @@ -96,10 +91,29 @@ z-index: 100; } .responsive-preview-frame-container iframe { + display: block; position: relative; } /** + * Scroll container. + */ +#responsive-preview-scroll-track { + bottom: 0; + left: 0; + overflow: visible; + position: absolute; + right: 0; + top: 0; + z-index: 1; +} +#responsive-preview-scroll-pane { + min-height: 100%; + position: relative; + width: 100%; +} + +/** * Override Toolbar styling in the preview iframe. */ body.toolbar-tray-open.responsive-preview-frame { diff --git a/core/modules/responsive_preview/css/responsive-preview.theme.css b/core/modules/responsive_preview/css/responsive-preview.theme.css index 6632bde..b915d89 100644 --- a/core/modules/responsive_preview/css/responsive-preview.theme.css +++ b/core/modules/responsive_preview/css/responsive-preview.theme.css @@ -27,11 +27,13 @@ } .toolbar-tab-responsive-preview .responsive-preview-trigger:hover { background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%); + background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%); background-image: linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%); } .toolbar-tab-responsive-preview .responsive-preview-trigger.active, .toolbar-tab-responsive-preview .responsive-preview-trigger.active:hover { background-image: -webkit-linear-gradient(top, rgb(78,159,234) 0%, rgb(69,132,221) 100%); + background-image: -moz-linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%); background-image: linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%); } .toolbar-tab-responsive-preview .responsive-preview-trigger, @@ -152,6 +154,7 @@ background-color: black; background-color: rgba(0,0,0,0.92); background-image: -webkit-linear-gradient(left, rgb(20,20,20),rgb(50,50,50) 25%, rgb(100,100,100) 40%, rgb(100,100,100) 60%, rgb(50,50,50) 75%, rgb(20,20,20)); + background-image: -moz-linear-gradient(left, rgb(20,20,20),rgb(50,50,50) 25%, rgb(100,100,100) 40%, rgb(100,100,100) 60%, rgb(50,50,50) 75%, rgb(20,20,20)); background-image: linear-gradient(left, rgb(20,20,20),rgb(50,50,50) 25%, rgb(100,100,100) 40%, rgb(100,100,100) 60%, rgb(50,50,50) 75%, rgb(20,20,20)); } @@ -180,19 +183,19 @@ right: 0; } .responsive-preview-device-label { - bottom: 0; color: #bbbbbb; cursor: pointer; font-family: sans-serif; font-weight: normal; left: 30px; - line-height: 2.6667; + line-height: 2; margin: 0; overflow: hidden; position: absolute; right: 30px; text-align: center; text-overflow: ellipsis; + top: 0; white-space: nowrap; } .responsive-preview-device-label-text:after { @@ -237,11 +240,9 @@ box-shadow: 0 0 0px 1px #777, 1px 1px 60px 0px #000; - -webkit-transition: all 150ms ease-out; - -moz-transition: all 150ms ease-out; - -o-transition: all 150ms ease-out; - transition: all 150ms ease-out; - margin-top: 2em; + -webkit-transition: left 150ms ease-out; + -moz-transition: left 150ms ease-out; + transition: left 150ms ease-out; } .responsive-preview-frame-container iframe { box-shadow: 0 0 0 1px #808080; diff --git a/core/modules/responsive_preview/js/responsive-preview.js b/core/modules/responsive_preview/js/responsive-preview.js index 048917e..6cf1b5f 100644 --- a/core/modules/responsive_preview/js/responsive-preview.js +++ b/core/modules/responsive_preview/js/responsive-preview.js @@ -533,6 +533,7 @@ Drupal.responsivePreview = Drupal.responsivePreview || { events: { 'click #responsive-preview-close': 'shutdown', 'click #responsive-preview-modal-background': 'shutdown', + 'click #responsive-preview-scroll-pane': 'shutdown', 'click #responsive-preview-orientation': 'rotate', 'click #responsive-preview-frame-label': 'revealDetails' }, @@ -603,6 +604,7 @@ Drupal.responsivePreview = Drupal.responsivePreview || { */ rotate: function (event) { this.model.set('isRotated', !this.model.get('isRotated')); + event.stopPropagation(); }, /** @@ -612,6 +614,7 @@ Drupal.responsivePreview = Drupal.responsivePreview || { */ revealDetails: function (event) { this.model.set('isDetailsExpanded', !this.model.get('isDetailsExpanded')); + event.stopPropagation(); }, /** @@ -636,6 +639,16 @@ Drupal.responsivePreview = Drupal.responsivePreview || { .on('load.responsivepreview', $.proxy(this._refresh, this)) // Add the frame to the preview container. .appendTo($frameContainer); + // Wrap the frame container in a pair of divs that will allow for + // scrolling. + $frameContainer = $frameContainer.wrap(Drupal.theme('responsivePreviewScrollContainer')) + .closest('#responsive-preview-scroll-track'); + // Apply padding to the scroll pane. + $frameContainer.find('#responsive-preview-scroll-pane') + .css({ + 'padding-bottom': this.bleed, + 'padding-top': this.bleed + }); // Insert the container into the DOM. this.$el .css({ @@ -658,7 +671,8 @@ Drupal.responsivePreview = Drupal.responsivePreview || { var isRotated = this.model.get('isRotated'); var $deviceLink = $('[data-responsive-preview-name="' + this.model.get('activeDevice') + '"]').eq(0); var $container = this.$el.find('#responsive-preview-frame-container'); - var $frame = $container.find('> iframe'); + var $frame = $container.find('#responsive-preview-frame'); + var $scrollPane = this.$el.find('#responsive-preview-scroll-pane'); var offsets = this.envModel.get('offsets'); // Get the static state. @@ -668,7 +682,6 @@ Drupal.responsivePreview = Drupal.responsivePreview || { // Get current (dynamic) state. var dimensions = this.model.get('dimensions'); var viewportWidth = this.envModel.get('viewportWidth') - (offsets.left + offsets.right); - var viewportHeight = this.envModel.get('viewportHeight') - (offsets.top + offsets.bottom); // Calculate preview width & height. If the preview is rotated, swap width // and height. @@ -680,7 +693,6 @@ Drupal.responsivePreview = Drupal.responsivePreview || { // Get the container padding and border width for both dimensions. var bleed = this.bleed; var widthSpread = width + (bleed * 2); - var heightSpread = height + (bleed * 2); // Calculate how much space is required to the right and left of the // preview container in order to center it. @@ -692,68 +704,91 @@ Drupal.responsivePreview = Drupal.responsivePreview || { // area for that dimension. The spread is how much room the preview // needs for that dimension. width = Math.ceil((viewportWidth - (gutter * 2) < widthSpread) ? viewportWidth - (gutter * 2) - (bleed * 2) : width); - // Use one gutter unit instead of two so that the preview element will - // appear closer to the top/bottom screen elements than it does to the - // left/right screen elements. - height = Math.ceil((viewportHeight - this.gutter < heightSpread) ? viewportHeight - this.gutter - (bleed * 2) : height); // Updated the state of the rotated icon. this.$el.find('.responsive-preview-control.responsive-preview-orientation').toggleClass('rotated', isRotated); - // Resize & reposition the iframe. + // Reposition the preview root. this.$el.css({ - 'top': offsets.top, - 'right': offsets.right, - 'left': offsets.left - }); + top: offsets.top, + right: offsets.right, + left: offsets.left, + height: document.documentElement.clientHeight - (offsets.top + offsets.bottom) + }); + + // Position the frame. var position = {}; - position[edge] = (gutter > minGutter) ? gutter : minGutter; // Depends on text direction. + // Position depends on text direction. + position[edge] = (gutter > minGutter) ? gutter : minGutter; $frame .css({ width: width, height: height }); - $container - .css(position); + + // Position the frame container. + $container.css(position); + + // Resize the scroll pane. + var paneHeight = height + (this.bleed * 2); + // If the height of the pane that contains the preview frame is higher + // than the available viewport area, then make it scroll. + if (paneHeight > (document.documentElement.clientHeight - offsets.top - offsets.bottom)) { + $scrollPane + .css({ + height: paneHeight + }) + // Select the parent container that constrains the overflow. + .parent() + .css({ + overflow: 'scroll' + }); + } + // If the height of the viewport area is sufficient to display the preview + // frame, remove the scroll styling. + else { + $scrollPane.css({ + height: 'auto' + }) + // Select the parent container that constrains the overflow. + .parent() + .css({ + overflow: 'visible' + }); + } // Scale if not responsive. this._scaleIfNotResponsive(); - // Update the device label. + // Update the text in the device label. var $label = $container.find('.responsive-preview-device-label'); - // Expose the details if the user has expanded the label. - var isDetailsExpanded = this.model.get('isDetailsExpanded'); - $label - .toggleClass('responsive-preview-expanded', isDetailsExpanded) - .find('.responsive-preview-device-label-details') - .toggleClass('visually-hidden', !isDetailsExpanded); - // Attach the device label. $label .find('.responsive-preview-device-label-text') .text(Drupal.t('@label', { '@label': $deviceLink.text() })); + // The device details are appended to the device label node in a separate // node so that their presentation can be varied independent of the label. $label .find('.responsive-preview-device-label-details') - .text(Drupal.t('@displayWidth@width by @displayHeight@height, @dpi, @orientation', { + .text(Drupal.t('@displayWidth@width by @displayHeight, @dpi, @orientation', { '@displayWidth': displayWidth + 'px', // If the width of the preview element is not equivalent to the // configured display width, display the actual width of the preview // in parentheses. '@width': (displayWidth !== Math.floor(width * dimensions.dppx)) ? ' (' + (Math.floor(width * dimensions.dppx)) + 'px)' : '', '@displayHeight': displayHeight + 'px', - // If the height of the preview element is not equivalent to the - // configured display height, display the actual height of the preview - // in parentheses. - '@height': (displayHeight !== Math.floor(height * dimensions.dppx)) ? ' (' + (Math.floor(height * dimensions.dppx)) + 'px)' : '', '@dpi': dimensions.dppx + 'ppx', '@orientation': (isRotated) ? this.strings.landscape : this.strings.portrait })); - // Update the positioning of the modal background. - this.$el.find('.responsive-preview-modal-background').css(offsets); + // Expose the details if the user has expanded the label. + var isDetailsExpanded = this.model.get('isDetailsExpanded'); + $label + .toggleClass('responsive-preview-expanded', isDetailsExpanded) + .find('.responsive-preview-device-label-details') + .toggleClass('visually-hidden', !isDetailsExpanded); }, /** @@ -988,20 +1023,30 @@ $.extend(Drupal.theme, { */ responsivePreviewFrameContainer: function (strings) { return '
' + - '' + - '' + - '' + + '' + + '' + + '' + '
'; }, /** + * Theme function for the scrolling wrapper of the preview container. + * + * @return + * The corresponding HTML. + */ + responsivePreviewScrollContainer: function () { + return '
'; + }, + + /** * Theme function for a responsive preview iframe element. * * @return