@atlassian/aui
Version:
Atlassian User Interface Framework
394 lines (323 loc) • 13.2 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['./jquery', './internal/log', './internal/amdify', './key-code', './internal/skate', '../../js-vendor/jquery/jquery.tipsy'], factory);
} else if (typeof exports !== "undefined") {
factory(require('./jquery'), require('./internal/log'), require('./internal/amdify'), require('./key-code'), require('./internal/skate'), require('../../js-vendor/jquery/jquery.tipsy'));
} else {
var mod = {
exports: {}
};
factory(global.jquery, global.log, global.amdify, global.keyCode, global.skate, global.jquery);
global.formNotification = mod.exports;
}
})(this, function (_jquery, _log, _amdify, _keyCode, _skate) {
'use strict';
var _jquery2 = _interopRequireDefault(_jquery);
var logger = _interopRequireWildcard(_log);
var _amdify2 = _interopRequireDefault(_amdify);
var _keyCode2 = _interopRequireDefault(_keyCode);
var _skate2 = _interopRequireDefault(_skate);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
var NOTIFICATION_NAMESPACE = 'aui-form-notification';
var CLASS_NOTIFICATION_INITIALISED = '_aui-form-notification-initialised';
var CLASS_NOTIFICATION_ICON = 'aui-icon-notification';
var CLASS_TOOLTIP = NOTIFICATION_NAMESPACE + '-tooltip';
var CLASS_TOOLTIP_ERROR = CLASS_TOOLTIP + '-error';
var CLASS_TOOLTIP_INFO = CLASS_TOOLTIP + '-info';
var ATTRIBUTE_NOTIFICATION_PREFIX = 'data-aui-notification-';
var ATTRIBUTE_NOTIFICATION_WAIT = ATTRIBUTE_NOTIFICATION_PREFIX + 'wait';
var ATTRIBUTE_NOTIFICATION_INFO = ATTRIBUTE_NOTIFICATION_PREFIX + 'info';
var ATTRIBUTE_NOTIFICATION_ERROR = ATTRIBUTE_NOTIFICATION_PREFIX + 'error';
var ATTRIBUTE_NOTIFICATION_SUCCESS = ATTRIBUTE_NOTIFICATION_PREFIX + 'success';
var ATTRIBUTE_TOOLTIP_POSITION = NOTIFICATION_NAMESPACE + '-position';
var NOTIFICATION_PRIORITY = [ATTRIBUTE_NOTIFICATION_ERROR, ATTRIBUTE_NOTIFICATION_SUCCESS, ATTRIBUTE_NOTIFICATION_WAIT, ATTRIBUTE_NOTIFICATION_INFO];
var notificationFields = [];
/* --- Tipsy configuration --- */
var TIPSY_OPACITY = 1;
var TIPSY_OFFSET_INSIDE_FIELD = 9; //offset in px from the icon to the start of the tipsy
var TIPSY_OFFSET_OUTSIDE_FIELD = 3;
function initialiseNotification($field) {
if (!isFieldInitialised($field)) {
prepareFieldMarkup($field);
initialiseTooltip($field);
bindFieldEvents($field);
synchroniseNotificationDisplay($field);
}
notificationFields.push($field);
}
function isFieldInitialised($field) {
return $field.hasClass(CLASS_NOTIFICATION_INITIALISED);
}
function constructFieldIcon() {
return (0, _jquery2.default)('<span class="aui-icon aui-icon-small ' + CLASS_NOTIFICATION_ICON + '"/>');
}
function prepareFieldMarkup($field) {
$field.addClass(CLASS_NOTIFICATION_INITIALISED);
appendIconToField($field);
}
function appendIconToField($field) {
var $icon = constructFieldIcon();
$field.after($icon);
}
function initialiseTooltip($field) {
getTooltipAnchor($field).tipsy({
gravity: getTipsyGravity($field),
title: function title() {
return getNotificationMessage($field);
},
trigger: 'manual',
offset: canContainIcon($field) ? TIPSY_OFFSET_INSIDE_FIELD : TIPSY_OFFSET_OUTSIDE_FIELD,
opacity: TIPSY_OPACITY,
className: function className() {
return 'aui-form-notification-tooltip ' + getNotificationClass($field);
},
html: true
});
}
// A list of HTML5 input types that don't typically get augmented by the browser, so are safe to put icons inside of.
var unadornedInputFields = ['text', 'url', 'email', 'tel', 'password'];
function canContainIcon($field) {
return unadornedInputFields.indexOf($field.attr('type')) !== -1;
}
function getNotificationMessage($field) {
var notificationType = getFieldNotificationType($field);
var message = notificationType ? $field.attr(notificationType) : '';
return formatMessage(message);
}
function formatMessage(message) {
if (message === '') {
return message;
}
var messageArray = jsonToArray(message);
if (messageArray.length === 1) {
return messageArray[0];
} else {
return '<ul><li>' + messageArray.join('</li><li>') + '</li></ul>';
}
}
function jsonToArray(jsonOrString) {
var jsonArray;
try {
jsonArray = JSON.parse(jsonOrString);
} catch (exception) {
jsonArray = [jsonOrString];
}
return jsonArray;
}
function getNotificationClass($field) {
var notificationType = getFieldNotificationType($field);
if (notificationType === ATTRIBUTE_NOTIFICATION_ERROR) {
return CLASS_TOOLTIP_ERROR;
} else if (notificationType === ATTRIBUTE_NOTIFICATION_INFO) {
return CLASS_TOOLTIP_INFO;
}
}
function getFieldNotificationType($field) {
var fieldNotificationType;
NOTIFICATION_PRIORITY.some(function (prioritisedNotification) {
if ($field.is('[' + prioritisedNotification + ']')) {
fieldNotificationType = prioritisedNotification;
return true;
}
});
return fieldNotificationType;
}
function bindFieldEvents($field) {
if (focusTogglesTooltip($field)) {
bindFieldTabEvents($field);
}
}
function focusTogglesTooltip($field) {
return $field.is(':aui-focusable');
}
function fieldHasTooltip($field) {
return getNotificationMessage($field) !== '';
}
function showTooltip($field) {
getTooltipAnchor($field).tipsy('show');
if (focusTogglesTooltip($field)) {
bindTooltipTabEvents($field);
}
}
function hideTooltip($field) {
getTooltipAnchor($field).tipsy('hide');
}
function bindFocusTooltipInteractions() {
document.addEventListener('focus', function (e) {
notificationFields.forEach(function (field) {
var $field = (0, _jquery2.default)(field);
var $tooltip = getTooltip($field);
if (!focusTogglesTooltip($field)) {
return;
}
var isFocusInTooltip = $tooltip && _jquery2.default.contains($tooltip[0], e.target);
var isFocusTargetField = $field.is(e.target);
var isFocusTargetChildOfField = isFocusEventTargetInElement(e, $field);
if (isFocusTargetField || isFocusTargetChildOfField) {
showTooltip($field);
} else if ($tooltip && !isFocusInTooltip) {
hideTooltip($field);
}
});
}, true);
}
bindFocusTooltipInteractions();
function isFocusEventTargetInElement(event, $element) {
return (0, _jquery2.default)(event.target).closest($element).length > 0;
}
function bindFieldTabEvents($field) {
$field.on('keydown', function (e) {
if (isNormalTab(e) && fieldHasTooltip($field)) {
var $firstTooltipLink = getFirstTooltipLink($field);
if ($firstTooltipLink.length) {
$firstTooltipLink.focus();
e.preventDefault();
}
}
});
}
function isNormalTab(e) {
return e.keyCode === _keyCode2.default.TAB && !e.shiftKey && !e.altKey;
}
function isShiftTab(e) {
return e.keyCode === _keyCode2.default.TAB && e.shiftKey;
}
function getFirstTooltipLink($field) {
return getTooltip($field).find(':aui-tabbable').first();
}
function getLastTooltipLink($field) {
return getTooltip($field).find(':aui-tabbable').last();
}
function getTooltip($field) {
var $anchor = getTooltipAnchor($field);
if ($anchor.data('tipsy')) {
return $anchor.data('tipsy').$tip;
}
}
function bindTooltipTabEvents($field) {
var $tooltip = getTooltip($field);
$tooltip.on('keydown', function (e) {
var leavingTooltipForwards = elementIsActive(getLastTooltipLink($field));
var leavingTooltipBackwards = elementIsActive(getFirstTooltipLink($field));
if (isNormalTab(e) && leavingTooltipForwards) {
if (leavingTooltipForwards) {
$field.focus();
}
}
if (isShiftTab(e) && leavingTooltipBackwards) {
if (leavingTooltipBackwards) {
$field.focus();
e.preventDefault();
}
}
});
}
function getTipsyGravity($field) {
var position = $field.data(ATTRIBUTE_TOOLTIP_POSITION) || 'side';
var gravityMap = {
side: 'w',
top: 'se',
bottom: 'ne'
};
var gravity = gravityMap[position];
if (!gravity) {
gravity = 'w';
logger.warn('Invalid notification position: "' + position + '". Valid options are "side", "bottom, "top"');
}
return gravity;
}
function getTooltipAnchor($field) {
return getFieldIcon($field);
}
function getFieldIcon($field) {
return $field.next('.' + CLASS_NOTIFICATION_ICON);
}
function elementIsActive($el) {
var el = $el instanceof _jquery2.default ? $el[0] : $el;
return el && el === document.activeElement;
}
function synchroniseNotificationDisplay(field) {
var $field = (0, _jquery2.default)(field);
if (!isFieldInitialised($field)) {
return;
}
var notificationType = getFieldNotificationType($field);
var showSpinner = notificationType === ATTRIBUTE_NOTIFICATION_WAIT;
setFieldSpinner($field, showSpinner);
var noNotificationOnField = !notificationType;
if (noNotificationOnField) {
hideTooltip($field);
return;
}
var message = getNotificationMessage($field);
var fieldContainsActiveElement = _jquery2.default.contains($field[0], document.activeElement);
var tooltipShouldBeVisible = fieldContainsActiveElement || elementIsActive($field) || !focusTogglesTooltip($field);
if (tooltipShouldBeVisible && message) {
showTooltip($field);
} else {
hideTooltip($field);
}
}
function setFieldSpinner($field, isSpinnerVisible) {
if (isSpinnerVisible) {
getFieldIcon($field).addClass('aui-icon-wait');
} else {
getFieldIcon($field).removeClass('aui-icon-wait');
}
}
document.addEventListener('mousedown', function (e) {
var isTargetLink = (0, _jquery2.default)(e.target).is('a');
if (isTargetLink) {
return;
}
var isTargetTooltip = (0, _jquery2.default)(e.target).closest('.aui-form-notification-tooltip').length > 0;
if (isTargetTooltip) {
return;
}
var $allNotificationFields = (0, _jquery2.default)('[data-aui-notification-field]');
$allNotificationFields.each(function () {
var $notificationField = (0, _jquery2.default)(this);
var targetIsThisField = $notificationField.is(e.target);
var isFocusTargetChildOfField = isFocusEventTargetInElement(e, $notificationField);
if (!targetIsThisField && !isFocusTargetChildOfField) {
hideTooltip($notificationField);
}
if (focusTogglesTooltip($notificationField)) {
hideTooltip($notificationField);
}
});
});
(0, _skate2.default)('data-aui-notification-field', {
attached: function attached(element) {
initialiseNotification((0, _jquery2.default)(element));
},
attributes: function () {
var attrs = {};
NOTIFICATION_PRIORITY.forEach(function (type) {
attrs[type] = synchroniseNotificationDisplay;
});
return attrs;
}(),
type: _skate2.default.type.ATTRIBUTE
});
(0, _amdify2.default)('aui/form-notification');
});
//# sourceMappingURL=form-notification.js.map