diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index f3a0c56218..350005420a 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -1332,6 +1332,18 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/core/behavior-keyboard-shortcuts.js', ), + 'javelin-behavior-phabricator-notification-example' => + array( + 'uri' => '/res/0b8fadf5/rsrc/js/application/uiexample/notification-example.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'phabricator-notification', + 1 => 'javelin-stratcom', + 2 => 'javelin-behavior', + ), + 'disk' => '/rsrc/js/application/uiexample/notification-example.js', + ), 'javelin-behavior-phabricator-object-selector' => array( 'uri' => '/res/0c4b0d82/rsrc/js/application/core/behavior-object-selector.js', @@ -2146,6 +2158,27 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/core/DropdownMenuItem.js', ), + 'phabricator-notification' => + array( + 'uri' => '/res/8497d4b2/rsrc/js/application/core/Notification.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-install', + 1 => 'javelin-dom', + 2 => 'javelin-stratcom', + ), + 'disk' => '/rsrc/js/application/core/Notification.js', + ), + 'phabricator-notification-css' => + array( + 'uri' => '/res/423a14d1/rsrc/css/aphront/notification.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/notification.css', + ), 'phabricator-object-selector-css' => array( 'uri' => '/res/7eb4c705/rsrc/css/application/objectselector/object-selector.css', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 1ef665b6eb..04db725959 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -966,6 +966,7 @@ phutil_register_library_map(array( 'PhabricatorUIExampleController' => 'applications/uiexample/controller/PhabricatorUIExampleController.php', 'PhabricatorUIExampleRenderController' => 'applications/uiexample/controller/PhabricatorUIExampleRenderController.php', 'PhabricatorUIListFilterExample' => 'applications/uiexample/examples/PhabricatorUIListFilterExample.php', + 'PhabricatorUINotificationExample' => 'applications/uiexample/examples/PhabricatorUINotificationExample.php', 'PhabricatorUIPagerExample' => 'applications/uiexample/examples/PhabricatorUIPagerExample.php', 'PhabricatorUITooltipExample' => 'applications/uiexample/examples/PhabricatorUITooltipExample.php', 'PhabricatorUnitsTestCase' => 'view/__tests__/PhabricatorUnitsTestCase.php', @@ -1900,6 +1901,7 @@ phutil_register_library_map(array( 'PhabricatorUIExampleController' => 'PhabricatorController', 'PhabricatorUIExampleRenderController' => 'PhabricatorUIExampleController', 'PhabricatorUIListFilterExample' => 'PhabricatorUIExample', + 'PhabricatorUINotificationExample' => 'PhabricatorUIExample', 'PhabricatorUIPagerExample' => 'PhabricatorUIExample', 'PhabricatorUITooltipExample' => 'PhabricatorUIExample', 'PhabricatorUnitsTestCase' => 'PhabricatorTestCase', diff --git a/src/applications/uiexample/examples/PhabricatorUINotificationExample.php b/src/applications/uiexample/examples/PhabricatorUINotificationExample.php new file mode 100644 index 0000000000..fd5082b62e --- /dev/null +++ b/src/applications/uiexample/examples/PhabricatorUINotificationExample.php @@ -0,0 +1,46 @@ +JX.Notification to create notifications.'; + } + + public function renderExample() { + + require_celerity_resource('phabricator-notification-css'); + Javelin::initBehavior('phabricator-notification-example'); + + $content = javelin_render_tag( + 'a', + array( + 'sigil' => 'notification-example', + 'class' => 'button green', + ), + 'Show Notification'); + + $content = '
'.$content.''; + + return $content; + } +} diff --git a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php index 75a0588d26..1a3e536875 100644 --- a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php +++ b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php @@ -31,6 +31,9 @@ final class PhabricatorJavelinLinter extends ArcanistLinter { public function willLintPaths(array $paths) { + $root = dirname(phutil_get_library_root('phabricator')); + require_once $root.'/scripts/__init_script__.php'; + if ($this->haveSymbolsBinary === null) { $binary = $this->getSymbolsBinaryPath(); $this->haveSymbolsBinary = Filesystem::pathExists($binary); diff --git a/webroot/rsrc/css/aphront/notification.css b/webroot/rsrc/css/aphront/notification.css new file mode 100644 index 0000000000..0ae4826a18 --- /dev/null +++ b/webroot/rsrc/css/aphront/notification.css @@ -0,0 +1,24 @@ +/** + * @provides phabricator-notification-css + */ + +.jx-notification-container { + position: fixed; + + bottom: 24px; + left: 24px; + width: 240px; + padding: 8px 16px; + + font-size: 11px; + overflow: hidden; + + background: #f3f6ff; + color: #444444; + border: 1px solid #afbfcf; + + cursor: pointer; + + border-radius: 3px; + box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.25); +} diff --git a/webroot/rsrc/js/application/core/Notification.js b/webroot/rsrc/js/application/core/Notification.js new file mode 100644 index 0000000000..421df0520a --- /dev/null +++ b/webroot/rsrc/js/application/core/Notification.js @@ -0,0 +1,106 @@ +/** + * @requires javelin-install + * javelin-dom + * javelin-stratcom + * @provides phabricator-notification + * @javelin + */ + +/** + * Show a notification. Usage: + * + * var n = new JX.Notification() + * .setContent('click me!'); + * n.listen('activate', function(e) { alert('you clicked!'); }); + * n.show(); + * + */ +JX.install('Notification', { + + events : ['activate', 'close'], + + members : { + show : function() { + var self = JX.Notification; + + self.close(); + self._installListener(); + self._active = this; + + var container = JX.$N( + 'div', + { + className: 'jx-notification-container', + sigil: 'jx-notification' + }, + this.getContent()); + document.body.appendChild(container); + + self._container = container; + + if (this.getDuration()) { + self._timeout = setTimeout(self.close, this.getDuration()); + } + } + }, + + properties : { + content : null, + + /** + * Duration before the notification fades away, in milliseconds. If set to + * 0, the notification persists until dismissed. + * + * @param int Notification duration, in milliseconds. + */ + duration : 12000 + }, + + statics : { + _container : null, + _listening : false, + _active : null, + _installListener : function() { + var self = JX.Notification; + + if (self._listening) { + return; + } else { + self._listening = true; + } + + JX.Stratcom.listen( + 'click', + 'jx-notification', + function(e) { + // NOTE: Don't kill the event since the user might have clicked a + // link, and we want to follow the link if they did. Istead, invoke + // the activate event for the active notification and dismiss it if it + // isn't handled. + + var activation = self._active.invoke('activate'); + if (activation.getPrevented()) { + return; + } + + self.close(); + }); + }, + close : function() { + var self = JX.Notification; + + if (self._container) { + JX.DOM.remove(self._container); + self._container = null; + + self._active.invoke('close'); + self._active = null; + } + + self._timeout && clearTimeout(self._timeout); + self._timeout = null; + } + } + +}); + diff --git a/webroot/rsrc/js/application/uiexample/notification-example.js b/webroot/rsrc/js/application/uiexample/notification-example.js new file mode 100644 index 0000000000..1563fa30ed --- /dev/null +++ b/webroot/rsrc/js/application/uiexample/notification-example.js @@ -0,0 +1,29 @@ +/** + * @requires phabricator-notification + * javelin-stratcom + * javelin-behavior + * @provides javelin-behavior-phabricator-notification-example + */ + +JX.behavior('phabricator-notification-example', function(config) { + JX.Stratcom.listen( + 'click', + 'notification-example', + function(e) { + e.kill(); + + var notification = new JX.Notification() + .setContent('It is ' + new Date().toString()); + + notification.listen( + 'activate', + function(e) { + if (!confirm("Close notification?")) { + e.kill(); + } + }); + + notification.show() + }); + +});