Skip to content

Commit

Permalink
feat(components): add inline tabs component with ajax support
Browse files Browse the repository at this point in the history
Inline tabs component can now be rendered with "page/components/tabs" view.
The component allows to switch between pre-populated and ajax-loaded tabs.
  • Loading branch information
hypeJunction committed Sep 28, 2016
1 parent c8fce06 commit 4de1cd2
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 0 deletions.
24 changes: 24 additions & 0 deletions docs/guides/javascript.rst
Expand Up @@ -692,6 +692,30 @@ Note that WYSIWYG will be automatically attached to all instances of ``.elgg-inp
});
Inline tabs component
---------------------

Inline tabs component fires an ``open`` event whenever a tabs is open and, in case of ajax tabs, finished loading:

.. code:: js
// Add custom animation to tab content
require(['jquery', 'elgg/ready'], function($) {
$(document).on('open', '.theme-sandbox-tab-callback', function() {
$(this).find('a').text('Clicked!');
$(this).data('target').hide().show('slide', {
duration: 2000,
direction: 'right',
complete: function() {
alert('Thank you for clicking. We hope you enjoyed the show!');
$(this).css('display', ''); // .show() adds display property
}
});
});
});
Traditional scripts
===================

Expand Down
6 changes: 6 additions & 0 deletions docs/guides/upgrading.rst
Expand Up @@ -106,6 +106,12 @@ Each column is an ``Elgg\Views\TableColumn`` object, usually created via methods
Plugins can provide or alter these factory methods (see ``Elgg\Views\TableColumn\ColumnFactory``).
See the view ``admin/users/newest`` for a usage example.

Inline tabs components
----------------------

Inline tabs component can now be rendered with ``page/components/tabs`` view. The components allows to switch between pre-poluated and ajax-loaded.
See ``page/components/tabs`` in core views and ``theme_sandbox/components/tabs`` in developers plugin for usage instructions and examples.

API to alter registration and login URL
---------------------------------------

Expand Down
1 change: 1 addition & 0 deletions mod/developers/start.php
Expand Up @@ -26,6 +26,7 @@ function developers_init() {
elgg_register_action('developers/ajax_demo', "$action_base/ajax_demo.php", 'admin');

elgg_register_ajax_view('forms/developers/ajax_demo');
elgg_register_ajax_view('theme_sandbox/components/tabs/ajax_demo');
}

function developers_process_settings() {
Expand Down
3 changes: 3 additions & 0 deletions mod/developers/views/default/theme_sandbox/components.php
Expand Up @@ -29,3 +29,6 @@

$body = elgg_view('theme_sandbox/components/messages');
echo elgg_view_module('theme-sandbox-demo', 'Messages (.elgg-message)', $body);

$body = elgg_view('theme_sandbox/components/tabs');
echo elgg_view_module('theme-sandbox-demo', 'Inline/Ajax Tabs (.elgg-tabs-component)', $body);
71 changes: 71 additions & 0 deletions mod/developers/views/default/theme_sandbox/components/tabs.php
@@ -0,0 +1,71 @@
<?php

echo elgg_view('page/components/tabs', [
'tabs' => [
'inline' => [
'text' => 'Inline Content',
'content' => elgg_view('developers/ipsum'),
],
'ajax' => [
'text' => 'Ajax [selected]',
'href' => 'ajax/view/theme_sandbox/components/tabs/ajax',
'selected' => true,
'data-ajax-reload' => false,
'data-ajax-query' => json_encode([
'content' => 'This tab was preselected and loaded via ajax',
]),
],
'inline2' => [
'text' => 'Inline List',
'content' => elgg_list_entities([
'types' => 'user',
'list_type' => 'gallery',
'gallery_class' => 'elgg-gallery-users',
'pagination' => false,
]),
],
'ajax2' => [
'text' => 'Ajax [data-ajax-reload]',
'href' => 'ajax/view/theme_sandbox/components/tabs/ajax',
'data-ajax-reload' => true,
'data-ajax-query' => json_encode([
'content' => 'This tab reloads every time you click on it',
]),
],
'ajax3' => [
'text' => 'Ajax [data-ajax-href]',
'href' => 'activity',
'data-ajax-href' => 'ajax/view/theme_sandbox/components/tabs/ajax',
'data-ajax-reload' => false,
'data-ajax-query' => json_encode([
'content' => 'If you right click on the tab and open it in a new tab, you will end up on the activity page',
]),
],
'callback' => [
'text' => 'Click Me',
'href' => 'ajax/view/theme_sandbox/components/tabs/ajax',
'data-ajax-query' => json_encode([
'content' => 'This tab has events attached to it.',
]),
'data-ajax-reload' => false,
'class' => 'theme-sandbox-tab-callback',
]
],
]);

?>
<script>
require(['jquery', 'elgg/ready'], function($) {
$(document).on('open', '.theme-sandbox-tab-callback', function() {
$(this).find('a').text('Clicked!');
$(this).data('target').hide().show('slide', {
duration: 2000,
direction: 'right',
complete: function() {
alert('Thank you for clicking. We hope you enjoyed the show!');
$(this).css('display', ''); // .show() adds display property
}
});
});
});
</script>
@@ -0,0 +1,4 @@
<?php

$content = get_input('content', 'No data was sent by the tab with the request');
echo elgg_view_module('featured', 'Ajax Content', $content);
94 changes: 94 additions & 0 deletions views/default/page/components/tabs.js
@@ -0,0 +1,94 @@
/**
* Tabbed module
*
* @module page/components/tabs
*/
define(function (require) {

var elgg = require('elgg');
require('elgg/ready');
var $ = require('jquery');
var Ajax = require('elgg/Ajax');
var ajax = new Ajax(false);

function changeTab($tab) {

$tab.siblings().andSelf().removeClass('elgg-state-selected');
$tab.addClass('elgg-state-selected');

var $target = $tab.data('target');
if (!$target || !$target.length) {
return false;
}

$target.siblings().addClass('hidden').removeClass('elgg-state-active');
$target.removeClass('hidden').addClass('elgg-state-active');

return true;
}

$(document).on('click', '.elgg-tabs-component .elgg-tabs > li > a', function (e) {
e.preventDefault();

var $link = $(this);
var $tab = $(this).parent();
var $component = $(this).closest('.elgg-tabs-component');
var $content = $component.find('.elgg-tabs-content');

var href = $link.data('ajaxHref') || $link.attr('href');
var $target = $tab.data('target');

if (href.indexOf('#') === 0) {
// Open inline tab
if (!$target || !$target.length) {
var $target = $content.find(href);
$tab.data('target', $target);
}

if (changeTab($tab)) {
$tab.trigger('open');
return;
}
} else {
// Load an ajax tab
if (!$target || !$target.length) {
var $target = $('<div>').addClass('elgg-content hidden');
$content.append($target);
$tab.data('target', $target);
}

if ($tab.data('loaded') && !$link.data('ajaxReload')) {
if (changeTab($tab)) {
$tab.trigger('open');
return;
}
}

ajax.path(href, {
data: $link.data('ajaxQuery') || {},
beforeSend: function () {
changeTab($tab);
$target.addClass('elgg-ajax-loader');
}
}).done(function (output, statusText, jqXHR) {
$tab.data('loaded', true);
$target.removeClass('elgg-ajax-loader');
if (jqXHR.AjaxData.status === -1) {
$target.html(elgg.echo('ajax:error'));
return;
} else {
$target.html(output);
}

if (changeTab($tab)) {
$tab.trigger('open');
}
});
}
});

// Open selected tabs
// This will load any selected tabs that link to ajax views
$('.elgg-tabs-component .elgg-tabs > li.elgg-state-selected > a').trigger('click');
});

93 changes: 93 additions & 0 deletions views/default/page/components/tabs.php
@@ -0,0 +1,93 @@
<?php
/**
* Tabbed module component
* Provides support for inline and ajax tabbing
*
* @uses $vars['id'] Optional ID of the module
* @uses $vars['class'] Additional classes
* @uses $vars['module'] Module name
* Defaults to 'tabs' (.elgg-module-tabs)
* @uses $vars['tabs'] An array of tabs suitable for 'navigation/tabs' view
* Each tab should specify either:
* - a 'href' parameter to load content via AJAX, or
* - a 'content' parameter to display content inline
* If the tab uses a 'href' attribute, it should specify
* whether the contents should be reloaded on a subsequent
* click via 'data-ajax-reload' parameter; by default,
* all tabs will be reloading on subsequent clicks.
* You can pass additional data to the ajax view via
* data-ajax-query attribute (json encoded string).
* You can also set the data-ajax-href parameter of the tab,
* which will override the href parameter, in case you want
* to ensure the tab is clickable even before the JS is bootstrapped.
* <code>
* [
* ['text' => 'Tab 1', 'href' => '/dynamic', 'data-ajax-reload' => true],
* ['text' => 'Tab 2', 'href' => '/static', 'data-ajax-reload' => false],
* ['text' => 'Tab 3', 'href' => '/static', 'data-ajax-reload' => false, 'data-ajax-query' => json_encode($data)],
* ['text' => 'Tab 3', 'href' => '/page', 'data-ajax-href' => '/ajax/page'],
* ['text' => 'Tab 4', 'href' => 'Tab content'],
* ]
* </code>
*/
$id = elgg_extract('id', $vars);
if (!isset($vars['id'])) {
$id = "elgg-tabs-" . base_convert(mt_rand(), 10, 36);
}
$vars['id'] = $id;

$vars['class'] = elgg_extract_class($vars, 'elgg-tabs-component');

$tabs = (array) elgg_extract('tabs', $vars, []);
unset($vars['tabs']);
if (empty($tabs)) {
return;
}

$content = '';
foreach ($tabs as $index => $tab) {
if (!isset($tab['href']) && !isset($tab['content'])) {
elgg_log('Tab configuration in "page/components/tabs"
requires either a "href" or "content" parameter', 'ERROR');
continue;
}

$selected = elgg_extract('selected', $tab);

if (isset($tab['content'])) {
$class = ['elgg-content'];
$class[] = $selected ? 'elgg-state-active' : 'hidden';

$content .= elgg_format_element('div', [
'class' => $class,
'id' => "{$id}-{$index}",
], $tab['content']);
unset($tab['content']);

$tab['href'] = "#{$id}-{$index}";
} else {
if (!isset($tab['data-ajax-reload'])) {
$tab['data-ajax-reload'] = true;
}
}

$tabs[$index] = $tab;
}

$tabs = elgg_view('navigation/tabs', [
'tabs' => $tabs,
]);

$content = elgg_format_element('div', [
'class' => 'elgg-tabs-content',
], $content);

$module = elgg_extract('module', $vars, 'tabs');
unset($vars['module']);
echo elgg_view_module($module, $tabs, $content, $vars);

?>
<script>
require(['page/components/tabs']);
</script>

0 comments on commit 4de1cd2

Please sign in to comment.