Permalink
Browse files

feature(js): adds elgg/popup AMD module

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 Mar 31, 2016
1 parent ea80be2 commit fd75da601ce6aaac4853ba4406f6141e1d17917c
View
@@ -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.
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``
-----------------------
@@ -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.popupOpen()`` and ``elgg.ui.popupClose()`` methods in ``elgg.ui`` JS library: Use ``elgg/popup`` module instead.
Deprecated Views
----------------
* ``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
===============
View
@@ -18,7 +18,9 @@ elgg.ui.init = function () {
$(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);
@@ -58,7 +60,7 @@ elgg.ui.toggles = function(event) {
event.preventDefault();
var $this = $(this),
selector = $this.data().toggleSelector;
if (!selector) {
// @todo we can switch to elgg.getSelectorFromUrlFragment() in 1.x if
// we also extend it to support href=".some-class"
@@ -102,85 +104,29 @@ elgg.ui.toggles = function(event) {
* @return void
*/
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 = {
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;
}
elgg.deprecated_notice('elgg.ui.popupOpen() has been deprecated and should not be called directly. Use elgg/popup AMD module instead', '2.2');
$target.appendTo('body')
.fadeIn()
.position(options);
event.preventDefault();
event.stopPropagation();
$(document)
.off('click', 'body', elgg.ui.popupClose)
.on('click', 'body', elgg.ui.popupClose);
var $elem = $(this);
require(['elgg/popup'], function(popup) {
popup.open($elem);
});
};
/**
* Catches clicks that aren't in a popup and closes all popups.
* @deprecated 2.2
*/
elgg.ui.popupClose = function(event) {
$eventTarget = $(event.target);
var inTarget = false;
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;
}
elgg.deprecated_notice('elgg.ui.popupClose() has been deprecated and should not be called directly. Use elgg/popup AMD module instead', '2.2');
// didn't click inside the target
if ($eventTarget.closest(target).length > 0) {
inTarget = true;
return false;
}
require(['elgg/popup'], function(popup) {
popup.close();
});
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);
}
};
/**
@@ -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);
});
});
@@ -6,10 +6,30 @@
'text' => 'Popup content',
'href' => "#elgg-popup-test",
'rel' => 'popup',
));
));
echo $link;
echo elgg_view_module('popup', 'Popup Test', $ipsum, array(
'id' => 'elgg-popup-test',
'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');
@@ -25,6 +25,10 @@
// @todo: remove in 3.x and use async calls
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();
$files = array(
// these must come first
Oops, something went wrong.

0 comments on commit fd75da6

Please sign in to comment.