Viewing 2 posts - 1 through 2 (of 2 total)
  • Author
    Posts
  • doorknob Friend
    #196050

    I found a problem with responsive layouts. I chose the ‘centre content’ layout with a column either side of the content block. On many pages there is no content in either of the side columns, the ‘no-sidebar’ block is used and the layout is fine. However, if the right column only is populated, the ‘one-sidebar-right’ block is selected and the layout is fine so long as the module position is populated. I configured the responsive layout to display the right column if the screen size is ‘lg’ or ‘md’ but hide it if the size is ‘sm’ or ‘xs’. This is where it all goes pear shaped. The module is displayed and hidden at the right time but the width of the content block is wrong when the module is hidden. This is because, while the column is managed using the ‘hidden-sm’ and ‘hidden-xs’ classes, no change is made to the classes used by the content block. As a result, when the screen size is ‘sm’ the right column is hidden (correctly) but the centent block only fills 75% of the available width. To fix this, the class names used for the content block also need to be adjusted to take account of whether the column is hidden. I did this by adding a couple of new functions to the T3Template class (includes/core/template.php)


    /**
    * Add additional classes to main content div to manage the width when another columns may be selectively hidden.
    * The class name supplied in the $classes1 array is only inserted if the corresponding screen size has no hidden
    * element in the alternative column. This allows the content column to fill the width of the screen if there is no
    * alternative content. If a second collapible column is also present then a further three parameter values may be
    * used to provide the class names needed for either column or neither column to be collapsed.
    *
    * @param string|array/string $name1 The position name or an array of position names in column 1
    * @param array/string $classes1 Associative array of one or more class names for each of the screen width
    * formats (e.g. 'xs'=>'col-xs-12', 'sm'=>'col-sm-8 col-sm-push-4') if
    * column 1 is the only column populated
    * @param string|array/string $name2 The position name or an array of position names in column 2 (optional)
    * @param array/string $classes2 Associative array of one or more class names for each of the screen width
    * formats (e.g. 'xs'=>'col-xs-12', 'sm'=>'col-sm-8 col-sm-push-4') if
    * column 2 is the only column populated (optional)
    * @param array/string $classes3 Associative array of one or more class names for each of the screen width
    * formats (e.g. 'xs'=>'col-xs-12', 'sm'=>'col-sm-8 col-sm-push-4') if
    * columns 1 and 2 are both populated (optional)
    *
    * @return string The combined class string to use for the content div
    */
    public function getLayoutClasses($name1, $classes1, $name2='', $classes2=array(), $classes3=array())
    {
    $data = '';
    // Build arrays of hidden column indicators
    $hidden1 = $this->getHiddenCols($name1);
    $hidden2 = $this->getHiddenCols($name2);

    foreach ($hidden1 as $device => $hidden) {
    // Include classes for non-hidden screen sizes
    if (!$hidden && !$hidden2[$device]) {
    // Both columns are populated use the class names from array 3
    $classes = 'classes3';
    } elseif (!$hidden) {
    // Only column 1 is populated
    $classes = 'classes1';
    } elseif (!$hidden2[$device]) {
    // Only column 2 is populated
    $classes = 'classes2';
    } else {
    // Neither column is populated
    $classes = '';
    }

    // Insert the class names into the output string
    if (!empty($classes) && isset(${$classes}[$device]) && !empty(${$classes}[$device])) {
    $data .= ' ' . trim(${$classes}[$device]);
    }
    }

    return $data;
    }

    /**
    * Returns an associative array to indicate whether all elements in a column are hidden for each of the screen
    * widths. This takes into accout whether the position is designated as 'hidden' in the template configuration
    * and also whether any modules are currently published into the position.
    *
    * @param string|array/string $name The position name or an array of position names
    *
    * @return array
    */
    protected function getHiddenCols($name)
    {
    // Build an array of position parameter objects
    if (is_array($name)) {
    foreach ($name as $n) {
    $params[] = $this->getLayoutSetting($n, '');
    }
    } else {
    $params[] = $this->getLayoutSetting($name, '');
    }

    // Find out if each screen size has any modules published into positions not designated 'hidden'
    $sizes = array('xs', 'sm', 'md', 'lg');
    $hidden = array_fill_keys($sizes, true);

    foreach ($params as $param) {
    if (!empty($param)) {
    foreach ($this->maxcol as $device => $span) {
    // Determine whether the screen size is hidden
    if (empty($param->$device) || strpos(' ' . (string) $param->$device . ' ', ' hidden ') === false) {
    $hidden[$device] = false;
    }
    }
    }
    }

    return $hidden;
    }

    Simple usage of the function getLayoutClasses() is for the situation where there is a single column with potentail content (suitable for all content blocks except ‘two-sidebar.php’). The function is called with a single module position name or an array of names and a second parameter containing the class names to apply if content is found. For example, in one-sidebar-right the line of code was previously

    <!-- MAIN CONTENT -->
    <div id="t3-content" class="t3-content col-xs-12 col-sm-8 col-md-9">

    I have replaced that with

    <!-- MAIN CONTENT -->
    <?php
    $content_classes = $this->getLayoutClasses(
    $vars['sidebar'],
    array('xs=>'col-xs-12', 'sm'=>'col-sm-8', 'md'=>'col-md-9'));
    ?>
    <div id="t3-content" class="t3-content<?phpecho $content_classes ?>">

    and now the layout works regardless of whether the column is selectively hidden for some screen widths

    The most complex case is for ‘two-sidebar.php’ where there are two columns that may be hidden or populated independently of each other. To handle this situation, three more parameters may be specified. The first two extra parameters mirror the first two and apply to the second column. However, in each case, the class names only apply if the corresponding column is the only column populated. If both columns are populated, the class names used are taken from the fifth parameter (a third array of class names). For example, in two-sidebars the line of code was previously

    <!-- MAIN CONTENT -->
    <!-- SIDEBAR 1 -->
    <div class="t3-sidebar t3-sidebar-1 col-xs-6 col-md-3 col-md-pull-6 <?php $this->_c($vars['sidebar1']) ?>"> <div id="t3-content" class="t3-content col-xs-12 col-md-6 col-md-push-3">

    I replaced that with

    <!-- MAIN CONTENT -->
    <?php
    $content_classes = $this->getLayoutClasses(
    $vars['sidebar1'],
    array('xs'=>'col-xs-12', 'sm'=>'col-sm-8 col-sm-push-4', 'md'=>'col-md-9 col-md-push-3', 'lg'=>'col-lg-9 col-lg-push-3'),// sidebar1 populated; sidebar2 hidden
    $vars['sidebar2'],
    array('xs'=>'col-xs-12', 'sm'=>'col-sm-8', 'md'=>'col-md-9', 'lg'=>'col-lg-9'),// sidebar1 hidden; sidebar2 populated
    array('xs'=>'col-xs-12', 'sm'=>'col-sm-12', 'md'=>'col-md-6 col-md-push-3', 'lg'=>'col-lg-6 col-lg-push-3'));// sidebar1 populated; sidebar2 populated
    ?>
    <div id="t3-content" class="t3-content <?php echo $content_classes ?>">

    The main content now works correctly but the two sidebars still only work correctly if they are both present or both hidden. To fix that, the same function is used like this:

    <!-- SIDEBAR 1 -->
    <div class="t3-sidebar t3-sidebar-1 col-xs-6 col-md-3 col-md-pull-6 <?php $this->_c($vars['sidebar1']) ?>">

    becomes

    <!-- SIDEBAR 1 -->
    <?php
    $content_classes = $this->getLayoutClasses(
    $vars['sidebar1'],
    array('xs'=>'col-xs-12', 'sm'=>'col-sm-4 col-sm-pull-8', 'md'=>'col-md-3 col-md-pull-9', 'lg'=>'col-lg-3 col-lg-pull-9'),// sidebar1 populated; sidebar2 hidden
    $vars['sidebar2'],
    array(),// sidebar1 hidden; sidebar2 populated
    array('xs'=>'col-xs-6', 'sm'=>'col-sm-6', 'md'=>'col-md-3 col-md-pull-6', 'lg'=>'col-lg-3 col-lg-pull-6'));// sidebar1 populated; sidebar2 populated
    ?>
    <div class="t3-sidebar t3-sidebar-1<?php echo $content_classes ?> <?php $this->_c($vars['sidebar1']) ?>">

    and

    <!-- SIDEBAR 2 -->
    <div class="t3-sidebar t3-sidebar-2 col-xs-6 col-md-3 <?php $this->_c($vars['sidebar2']) ?>">

    becomes

    <!-- SIDEBAR 2 -->
    <?php
    $content_classes = $this->getLayoutClasses(
    $vars['sidebar2'],
    array('xs'=>'col-xs-12', 'sm'=>'col-sm-4', 'md'=>'col-md-3', 'lg'=>'col-lg-3'),// sidebar2 populated; sidebar1 hidden
    $vars['sidebar1'],
    array(),// sidebar2 hidden; sidebar1 populated
    array('xs'=>'col-xs-6', 'sm'=>'col-sm-6', 'md'=>'col-md-3', 'lg'=>'col-lg-3'));// sidebar2 populated; sidebar1 populated
    ?>
    <div class="t3-sidebar t3-sidebar-2<?php echo $content_classes ?> <?php $this->_c($vars['sidebar2']) ?>">

    and now it’s all fully responsive.

    One issue remains which is that the layout display in the template theme editor is left in a bit of a state. To fix that, I added an alternative version of the second of the functions to the T3TemplateLayout class (includes/core/templatelayout.php):


    /**
    * Returns an associative array to indicate whether all elements in a column are hidden for each of the screen
    * widths. This takes into accout whether the position is designated as 'hidden' in the template configuration
    * but assumes that every module position has a published module.
    *
    * Note: The parent function assumes that a position is hidden unless a publishable module can be found. This
    * function assumes, for layout purposes, that a position is not hidden unless specifically designated so.
    *
    * @param string|array/string $name The position name or an array of position names
    *
    * @return array
    */
    protected function getHiddenCols($name)
    {
    // Build an array of position parameter objects
    if (is_array($name)) {
    foreach ($name as $n) {
    $params[] = $this->getLayoutSetting($n, '');
    }
    } else {
    $params[] = $this->getLayoutSetting($name, '');
    }

    // Find out if each screen size has any modules published into positions not designated 'hidden'
    $sizes = array('xs', 'sm', 'md', 'lg');
    if (empty($name)) {
    $hidden = array_fill_keys($sizes, true);
    } else {
    $hidden = array_fill_keys($sizes, false);

    foreach ($params as $param) {
    if (!empty($param)) {
    foreach ($this->maxcol as $device => $span) {
    // Determine whether the screen size is hidden
    if (!empty($param->$device) && strpos(' ' . (string) $param->$device . ' ', ' hidden ') !== false) {
    $hidden[$device] = true;
    }
    }
    }
    }
    }

    return $hidden;
    }

    This alternative version of getHiddenCols() only hides module positions if they have been configured as ‘hidden’ and leaves them visible if they are just empty.

    Regards
    Phil

    Ninja Lead Moderator
    #527826

    Hi Phil,

    Thanks for bringing this case to our attention. I have passed the issue to our development team to check further here. Hope they can figure it out soon.

    Many thanks.

Viewing 2 posts - 1 through 2 (of 2 total)

This topic contains 2 replies, has 2 voices, and was last updated by  Ninja Lead 10 years, 8 months ago.

We moved to new unified forum. Please post all new support queries in our New Forum