123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const React = require("devtools/client/shared/vendor/react");
- const Immutable = require("devtools/client/shared/vendor/immutable");
- const { LocalizationHelper } = require("devtools/shared/l10n");
- const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
- // Shortcuts
- const { PropTypes, createClass, DOM } = React;
- const { div, span, button } = DOM;
- // Priority Levels
- const PriorityLevels = {
- PRIORITY_INFO_LOW: 1,
- PRIORITY_INFO_MEDIUM: 2,
- PRIORITY_INFO_HIGH: 3,
- PRIORITY_WARNING_LOW: 4,
- PRIORITY_WARNING_MEDIUM: 5,
- PRIORITY_WARNING_HIGH: 6,
- PRIORITY_CRITICAL_LOW: 7,
- PRIORITY_CRITICAL_MEDIUM: 8,
- PRIORITY_CRITICAL_HIGH: 9,
- PRIORITY_CRITICAL_BLOCK: 10,
- };
- /**
- * This component represents Notification Box - HTML alternative for
- * <xul:notifictionbox> binding.
- *
- * See also MDN for more info about <xul:notificationbox>:
- * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/notificationbox
- */
- var NotificationBox = createClass({
- displayName: "NotificationBox",
- propTypes: {
- // List of notifications appended into the box.
- notifications: PropTypes.arrayOf(PropTypes.shape({
- // label to appear on the notification.
- label: PropTypes.string.isRequired,
- // Value used to identify the notification
- value: PropTypes.string.isRequired,
- // URL of image to appear on the notification. If "" then an icon
- // appropriate for the priority level is used.
- image: PropTypes.string.isRequired,
- // Notification priority; see Priority Levels.
- priority: PropTypes.number.isRequired,
- // Array of button descriptions to appear on the notification.
- buttons: PropTypes.arrayOf(PropTypes.shape({
- // Function to be called when the button is activated.
- // This function is passed three arguments:
- // 1) the NotificationBox component the button is associated with
- // 2) the button description as passed to appendNotification.
- // 3) the element which was the target of the button press event.
- // If the return value from this function is not True, then the
- // notification is closed. The notification is also not closed
- // if an error is thrown.
- callback: PropTypes.func.isRequired,
- // The label to appear on the button.
- label: PropTypes.string.isRequired,
- // The accesskey attribute set on the <button> element.
- accesskey: PropTypes.string,
- })),
- // A function to call to notify you of interesting things that happen
- // with the notification box.
- eventCallback: PropTypes.func,
- })),
- // Message that should be shown when hovering over the close button
- closeButtonTooltip: PropTypes.string
- },
- getDefaultProps() {
- return {
- closeButtonTooltip: l10n.getStr("notificationBox.closeTooltip")
- };
- },
- getInitialState() {
- return {
- notifications: new Immutable.OrderedMap()
- };
- },
- /**
- * Create a new notification and display it. If another notification is
- * already present with a higher priority, the new notification will be
- * added behind it. See `propTypes` for arguments description.
- */
- appendNotification(label, value, image, priority, buttons = [],
- eventCallback) {
- // Priority level must be within expected interval
- // (see priority levels at the top of this file).
- if (priority < PriorityLevels.PRIORITY_INFO_LOW ||
- priority > PriorityLevels.PRIORITY_CRITICAL_BLOCK) {
- throw new Error("Invalid notification priority " + priority);
- }
- // Custom image URL is not supported yet.
- if (image) {
- throw new Error("Custom image URL is not supported yet");
- }
- let type = "warning";
- if (priority >= PriorityLevels.PRIORITY_CRITICAL_LOW) {
- type = "critical";
- } else if (priority <= PriorityLevels.PRIORITY_INFO_HIGH) {
- type = "info";
- }
- let notifications = this.state.notifications.set(value, {
- label: label,
- value: value,
- image: image,
- priority: priority,
- type: type,
- buttons: buttons,
- eventCallback: eventCallback,
- });
- // High priorities must be on top.
- notifications = notifications.sortBy((val, key) => {
- return -val.priority;
- });
- this.setState({
- notifications: notifications
- });
- },
- /**
- * Remove specific notification from the list.
- */
- removeNotification(notification) {
- this.close(this.state.notifications.get(notification.value));
- },
- /**
- * Returns an object that represents a notification. It can be
- * used to close it.
- */
- getNotificationWithValue(value) {
- let notification = this.state.notifications.get(value);
- if (!notification) {
- return null;
- }
- // Return an object that can be used to remove the notification
- // later (using `removeNotification` method) or directly close it.
- return Object.assign({}, notification, {
- close: () => {
- this.close(notification);
- }
- });
- },
- getCurrentNotification() {
- return this.state.notifications.first();
- },
- /**
- * Close specified notification.
- */
- close(notification) {
- if (!notification) {
- return;
- }
- if (notification.eventCallback) {
- notification.eventCallback("removed");
- }
- this.setState({
- notifications: this.state.notifications.remove(notification.value)
- });
- },
- /**
- * Render a button. A notification can have a set of custom buttons.
- * These are used to execute custom callback.
- */
- renderButton(props, notification) {
- let onClick = event => {
- if (props.callback) {
- let result = props.callback(this, props, event.target);
- if (!result) {
- this.close(notification);
- }
- event.stopPropagation();
- }
- };
- return (
- button({
- key: props.label,
- className: "notification-button",
- accesskey: props.accesskey,
- onClick: onClick},
- props.label
- )
- );
- },
- /**
- * Render a notification.
- */
- renderNotification(notification) {
- return (
- div({
- key: notification.value,
- className: "notification",
- "data-type": notification.type},
- div({className: "notificationInner"},
- div({className: "details"},
- div({
- className: "messageImage",
- "data-type": notification.type}),
- span({className: "messageText"},
- notification.label
- ),
- notification.buttons.map(props =>
- this.renderButton(props, notification)
- )
- ),
- div({
- className: "messageCloseButton",
- title: this.props.closeButtonTooltip,
- onClick: this.close.bind(this, notification)}
- )
- )
- )
- );
- },
- /**
- * Render the top (highest priority) notification. Only one
- * notification is rendered at a time.
- */
- render() {
- let notification = this.state.notifications.first();
- let content = notification ?
- this.renderNotification(notification) :
- null;
- return div({className: "notificationbox"},
- content
- );
- },
- });
- module.exports.NotificationBox = NotificationBox;
- module.exports.PriorityLevels = PriorityLevels;
|