Skip to content

Commit

Permalink
feature(js): adds elgg/popup AMD module
Browse files Browse the repository at this point in the history
Converts JS popup to an AMD module subsequently giving more freedom
to plugins to manipulate popups programmatically.
Popup target module can now be defined with data-href attribute in
addition to href attribute (suitable for use with non-anchor elements).
Popup positioning details can be passed with data-position of the trigger
(which is simpler than using the triggered plugin hook).
jQuery "open" and "close" events are now triggered when the popup is shown
and closed, allowing plugins to manipulate popup module and its contents
  • Loading branch information
hypeJunction committed Apr 1, 2016
1 parent ea80be2 commit fd75da6
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 72 deletions.
97 changes: 97 additions & 0 deletions docs/guides/javascript.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -434,6 +434,103 @@ The ``elgg/spinner`` module can be used to create an Ajax loading indicator fixe
.. note:: The ``elgg/Ajax`` module uses the spinner by default. .. note:: The ``elgg/Ajax`` module uses the spinner by default.


Module ``elgg/popup``
-----------------------

The ``elgg/popup`` module can be used to display an overlay positioned relatively to its anchor (trigger).

The ``elgg/popup`` module is loaded by default, and binding a popup module to an anchor is as simple as adding ``rel="popup"``
attribute and defining target module with a ``href`` (or ``data-href``) attribute. Popup module positioning can be defined with
``data-position`` attribute of the trigger element.

.. $.position(): http://api.jqueryui.com/position/
.. code:: php
echo elgg_format_element('div', [
'class' => 'elgg-module-popup hidden',
'id' => 'popup-module',
], 'Popup module content');
// Simple anchor
echo elgg_view('output/url', [
'href' => '#popup-module',
'text' => 'Show popup',
'rel' => 'popup',
]);
// Button with custom positioning of the popup
echo elgg_format_element('button', [
'rel' => 'popup',
'class' => 'elgg-button elgg-button-submit',
'text' => 'Show popup',
'data-href' => '#popup-module',
'data-position' => json_encode([
'my' => 'center bottom',
'at' => 'center top',
]),
]);
The ``elgg/popup`` module allows you to build out more complex UI/UX elements. You can open and close
popup modules programmatically:

.. code:: js
define(function(require) {
var $ = require('jquery');
$(document).on('click', '.elgg-button-popup', function(e) {
e.preventDefault();
var $trigger = $(this);
var $target = $('#my-target');
var $close = $target.find('.close');
require(['elgg/popup'], function(popup) {
popup.open($trigger, $target, {
'collision': 'fit none'
});
$close.on('click', popup.close);
});
});
});
You can use ``getOptions, ui.popup`` plugin hook to manipulate the position of the popup before it has been opened.
You can use jQuery ``open`` and ``close`` events to manipulate popup module after it has been opened or closed.

.. code:: js
define(function(require) {
var elgg = require('elgg');
var $ = require('jquery');
$('#my-target').on('open', function() {
var $module = $(this);
var $trigger = $module.data('trigger');
elgg.ajax('ajax/view/my_module', {
beforeSend: function() {
$trigger.hide();
$module.html('').addClass('elgg-ajax-loader');
},
success: function(output) {
$module.removeClass('elgg-ajax-loader').html(output);
}
});
}).on('close', function() {
var $trigger = $(this).data('trigger');
$trigger.show();
});
});
Open popup modules will always contain the following data that can be accessed via ``$.data()``:

* ``trigger`` - jQuery element used to trigger the popup module to open
* ``position`` - An object defining popup module position that was passed to ``$.position()``

Module ``elgg/widgets`` Module ``elgg/widgets``
----------------------- -----------------------


Expand Down
6 changes: 6 additions & 0 deletions docs/guides/upgrading.rst
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ Deprecated APIs
--------------- ---------------


* ``elgg.ui.river`` JavaScript library: Remove calls to ``elgg_load_js('elgg.ui.river')`` from plugin code. Update ``core/river/filter`` and ``forms/comment/save``, if overwritten, to require component AMD modules * ``elgg.ui.river`` JavaScript library: Remove calls to ``elgg_load_js('elgg.ui.river')`` from plugin code. Update ``core/river/filter`` and ``forms/comment/save``, if overwritten, to require component AMD modules
* ``elgg.ui.popupOpen()`` and ``elgg.ui.popupClose()`` methods in ``elgg.ui`` JS library: Use ``elgg/popup`` module instead.


Deprecated Views Deprecated Views
---------------- ----------------


* ``elgg/ui.river.js`` is deprecated: Do not rely on simplecache URLs to work. * ``elgg/ui.river.js`` is deprecated: Do not rely on simplecache URLs to work.


Added ``elgg/popup`` module
-----------------------------

New :doc:`elgg/popup module <javascript>` can be used to build out more complex trigger-popup interactions, including binding custom anchor types and opening/closing popups programmatically.

From 2.0 to 2.1 From 2.0 to 2.1
=============== ===============


Expand Down
86 changes: 16 additions & 70 deletions js/lib/ui.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ elgg.ui.init = function () {


$(document).on('click', '[rel=toggle]', elgg.ui.toggles); $(document).on('click', '[rel=toggle]', elgg.ui.toggles);


$(document).on('click', '[rel=popup]', elgg.ui.popupOpen); require(['elgg/popup'], function(popup) {
popup.bind($('[rel="popup"]'));
});


$(document).on('click', '.elgg-menu-page .elgg-menu-parent', elgg.ui.toggleMenu); $(document).on('click', '.elgg-menu-page .elgg-menu-parent', elgg.ui.toggleMenu);


Expand Down Expand Up @@ -58,7 +60,7 @@ elgg.ui.toggles = function(event) {
event.preventDefault(); event.preventDefault();
var $this = $(this), var $this = $(this),
selector = $this.data().toggleSelector; selector = $this.data().toggleSelector;

if (!selector) { if (!selector) {
// @todo we can switch to elgg.getSelectorFromUrlFragment() in 1.x if // @todo we can switch to elgg.getSelectorFromUrlFragment() in 1.x if
// we also extend it to support href=".some-class" // we also extend it to support href=".some-class"
Expand Down Expand Up @@ -102,85 +104,29 @@ elgg.ui.toggles = function(event) {
* @return void * @return void
*/ */
elgg.ui.popupOpen = function(event) { elgg.ui.popupOpen = function(event) {
event.preventDefault();
event.stopPropagation();

var target = elgg.getSelectorFromUrlFragment($(this).toggleClass('elgg-state-active').attr('href'));
var $target = $(target);

// emit a hook to allow plugins to position and control popups
var params = {
targetSelector: target,
target: $target,
source: $(this)
};


var options = { elgg.deprecated_notice('elgg.ui.popupOpen() has been deprecated and should not be called directly. Use elgg/popup AMD module instead', '2.2');
my: 'center top',
at: 'center bottom',
of: $(this),
collision: 'fit fit'
};

options = elgg.trigger_hook('getOptions', 'ui.popup', params, options);

// allow plugins to cancel event
if (!options) {
return;
}

// hide if already open
if ($target.is(':visible')) {
$target.fadeOut();
$(document).off('click', elgg.ui.popupClose);
return;
}


$target.appendTo('body') event.preventDefault();
.fadeIn() event.stopPropagation();
.position(options);


$(document) var $elem = $(this);
.off('click', 'body', elgg.ui.popupClose) require(['elgg/popup'], function(popup) {
.on('click', 'body', elgg.ui.popupClose); popup.open($elem);
});
}; };


/** /**
* Catches clicks that aren't in a popup and closes all popups. * Catches clicks that aren't in a popup and closes all popups.
* @deprecated 2.2
*/ */
elgg.ui.popupClose = function(event) { elgg.ui.popupClose = function(event) {
$eventTarget = $(event.target);
var inTarget = false; elgg.deprecated_notice('elgg.ui.popupClose() has been deprecated and should not be called directly. Use elgg/popup AMD module instead', '2.2');
var $popups = $('[rel=popup]');

// if the click event target isn't in a popup target, fade all of them out.
$popups.each(function(i, e) {
var target = elgg.getSelectorFromUrlFragment($(e).attr('href')) + ':visible';
var $target = $(target);

if (!$target.is(':visible')) {
return;
}


// didn't click inside the target require(['elgg/popup'], function(popup) {
if ($eventTarget.closest(target).length > 0) { popup.close();
inTarget = true;
return false;
}
}); });

if (!inTarget) {
$popups.each(function(i, e) {
var $e = $(e);
var $target = $(elgg.getSelectorFromUrlFragment($e.attr('href')) + ':visible');
if ($target.length > 0) {
$target.fadeOut();
$e.removeClass('elgg-state-active');
}
});

$(document).off('click', elgg.ui.popupClose);
}
}; };


/** /**
Expand Down
24 changes: 24 additions & 0 deletions mod/developers/views/default/theme_sandbox/javascript/popup.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,24 @@
define(function (require) {

var elgg = require('elgg');
var $ = require('jquery');

$('#elgg-popup-test2').on('open', function () {
var $module = $(this);
var $trigger = $module.data('trigger');

elgg.ajax('ajax/view/developers/ajax', {
beforeSend: function () {
$trigger.addClass('elgg-state-disabled').prop('disabled', true);
$module.html('').addClass('elgg-ajax-loader');
},
success: function (output) {
$module.removeClass('elgg-ajax-loader').html(output);
}
});
}).on('close', function () {
var $trigger = $(this).data('trigger');
$trigger.removeClass('elgg-state-disabled').prop('disabled', false);
});

});
24 changes: 22 additions & 2 deletions mod/developers/views/default/theme_sandbox/javascript/popup.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,10 +6,30 @@
'text' => 'Popup content', 'text' => 'Popup content',
'href' => "#elgg-popup-test", 'href' => "#elgg-popup-test",
'rel' => 'popup', 'rel' => 'popup',
)); ));


echo $link; echo $link;
echo elgg_view_module('popup', 'Popup Test', $ipsum, array( echo elgg_view_module('popup', 'Popup Test', $ipsum, array(
'id' => 'elgg-popup-test', 'id' => 'elgg-popup-test',
'class' => 'hidden theme-sandbox-content-thin', 'class' => 'hidden theme-sandbox-content-thin',
)); ));


$button = elgg_format_element('button', array(
'class' => 'elgg-button elgg-button-submit mll',
'rel' => 'popup',
'data-href' => "#elgg-popup-test2",
'data-position' => json_encode(array(
'my' => 'left top',
'at' => 'left bottom',
)),
), 'Load content in a popup');

echo $button;

echo elgg_format_element('div', array(
'id' => 'elgg-popup-test2',
'class' => 'hidden theme-sandbox-content-thin elgg-module-popup',
), '');

elgg_require_js('theme_sandbox/javascript/popup');
4 changes: 4 additions & 0 deletions views/default/elgg.js.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
// @todo: remove in 3.x and use async calls // @todo: remove in 3.x and use async calls
echo elgg_view('elgg/widgets.js'); echo elgg_view('elgg/widgets.js');


// We use a named AMD module and inine it here in order to save HTTP requests,
// as this module will be required on each page
echo elgg_view('elgg/popup.js');

$elggDir = \Elgg\Application::elggDir(); $elggDir = \Elgg\Application::elggDir();
$files = array( $files = array(
// these must come first // these must come first
Expand Down
Loading

0 comments on commit fd75da6

Please sign in to comment.