From 820b2efd885ddb724c98d129b73f190546998ddf Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Fri, 29 Sep 2017 12:51:39 -0300 Subject: [PATCH] Creating a notification mechanism --- .../client/components/bootstrap/index.js | 2 + .../bootstrap/notification/index.js | 4 + .../notification/notificationArea.html | 3 + .../notification/notificationArea.styl | 62 +++++++++++ .../notification/notificationNote.html | 12 ++ .../notification/notificationNote.js | 21 ++++ Packages/ohif-core/client/ui/index.js | 1 + .../client/ui/notifications/notifications.js | 105 ++++++++++++++++++ .../ohif-hotkeys/client/components/form.html | 2 +- .../ohif-hotkeys/client/components/form.js | 7 +- 10 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 Packages/ohif-core/client/components/bootstrap/notification/index.js create mode 100644 Packages/ohif-core/client/components/bootstrap/notification/notificationArea.html create mode 100644 Packages/ohif-core/client/components/bootstrap/notification/notificationArea.styl create mode 100644 Packages/ohif-core/client/components/bootstrap/notification/notificationNote.html create mode 100644 Packages/ohif-core/client/components/bootstrap/notification/notificationNote.js create mode 100644 Packages/ohif-core/client/ui/notifications/notifications.js diff --git a/Packages/ohif-core/client/components/bootstrap/index.js b/Packages/ohif-core/client/components/bootstrap/index.js index 1e4fc3db5..cf5e5ed2f 100644 --- a/Packages/ohif-core/client/components/bootstrap/index.js +++ b/Packages/ohif-core/client/components/bootstrap/index.js @@ -32,4 +32,6 @@ import './input/range.html'; import './input/select.html'; import './input/text.html'; +import './notification'; + import './popover/form.html'; diff --git a/Packages/ohif-core/client/components/bootstrap/notification/index.js b/Packages/ohif-core/client/components/bootstrap/notification/index.js new file mode 100644 index 000000000..0ae286d8e --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/notification/index.js @@ -0,0 +1,4 @@ +import './notificationArea.html'; +import './notificationArea.styl'; +import './notificationNote.html'; +import './notificationNote.js'; diff --git a/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.html b/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.html new file mode 100644 index 000000000..4648c287d --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.html @@ -0,0 +1,3 @@ + diff --git a/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.styl b/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.styl new file mode 100644 index 000000000..c7b3b8790 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/notification/notificationArea.styl @@ -0,0 +1,62 @@ +@require '{ohif:design}/app' + +.notification-area + position: fixed + right: 0 + top: 50px + max-width: 320px + z-index: 10000 + +.notification-note + opacity: 0 + position: relative + transition(opacity 0.5s linear\, max-height 0.5s linear) + width: 300px + z-index: 2 + + .note-container + padding: 10px + transform(translateY(0)) + transition(transform 0.5s linear) + + .note-body + margin: 0 + padding: 10px + position: relative + + &:not(.hide-dismiss) + padding-right: 40px + + .note-dismiss + display: block + + .note-dismiss + bottom: 0 + cursor: pointer + display: none + position: absolute + right: 0 + top: 0 + width: 40px + + i + display: block + font-size: 20px + left: 0 + position: absolute + text-align: center + top: 50% + transform(translateY(-50%)) + width: 100% + + &.in + max-height: auto + opacity: 1 + + &.out + max-height: 0 !important + opacity: 0 + z-index: 1 + + .note-container + transform(translateY(-100%)) diff --git a/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.html b/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.html new file mode 100644 index 000000000..72c244bf8 --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.html @@ -0,0 +1,12 @@ + diff --git a/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.js b/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.js new file mode 100644 index 000000000..c0581c38b --- /dev/null +++ b/Packages/ohif-core/client/components/bootstrap/notification/notificationNote.js @@ -0,0 +1,21 @@ +import { Meteor } from 'meteor/meteor'; +import { Template } from 'meteor/templating'; +import { OHIF } from 'meteor/ohif:core'; + +Template.notificationNote.onRendered(() => { + const instance = Template.instance(); + Meteor.setTimeout(() => { + const $note = instance.$('.notification-note'); + $note.css('max-height', $note.outerHeight()).addClass('in'); + }, 100); +}); + +Template.notificationNote.events({ + 'click .note-dismiss'(event, instance) { + if (instance.data.promiseResolve) { + instance.data.promiseResolve(); + } else { + OHIF.ui.notifications.dismiss(instance.data.id); + } + } +}); diff --git a/Packages/ohif-core/client/ui/index.js b/Packages/ohif-core/client/ui/index.js index 30d66293d..76df02b1c 100644 --- a/Packages/ohif-core/client/ui/index.js +++ b/Packages/ohif-core/client/ui/index.js @@ -6,6 +6,7 @@ import './dialog/unsavedChangesDialog.js'; import './draggable/draggable.js'; import './dropdown/class.js'; import './dropdown/display.js'; +import './notifications/notifications.js'; import './popover/display.js'; import './resizable/resizable.js'; import './unsavedChanges/unsavedChanges.js'; diff --git a/Packages/ohif-core/client/ui/notifications/notifications.js b/Packages/ohif-core/client/ui/notifications/notifications.js new file mode 100644 index 000000000..57fb9e205 --- /dev/null +++ b/Packages/ohif-core/client/ui/notifications/notifications.js @@ -0,0 +1,105 @@ +import { Meteor } from 'meteor/meteor'; +import { Template } from 'meteor/templating'; +import { ReactiveDict } from 'meteor/reactive-dict'; +import { Tracker } from 'meteor/tracker'; +import { $ } from 'meteor/jquery'; +import { OHIF } from 'meteor/ohif:core'; + +const Notifications = { + currentId: 0, + views: new Map() +}; + +// Remove the view object from DOM and from views Map +const removeView = (id, view) => { + Notifications.views.delete(id); + Blaze.remove(view); +}; + +// Dismiss a single notification note by its id +Notifications.dismiss = id => { + const view = Notifications.views.get(id); + if (!view || view.isDestroyed) { + return Notifications.views.delete(id); + } + + const node = view.firstNode(); + const $note = node && $(node); + + if ($note.length) { + $note.addClass('out').one('transitionend', () => removeView(id, view)); + } else { + removeView(id, view) + } +}; + +// Dismiss all notification notes +Notifications.clear = () => Array.from(Notifications.views.keys()).forEach(Notifications.dismiss); + +// Display a notification note +Notifications.show = ({ template, promise, text, style, timeout=5000 }) => { + // Check if the given template exists + const templateObject = Template[template]; + if (template && !templateObject) { + throw new Meteor.Error('TEMPLATE_NOT_FOUND', `Template ${template} not found.`); + } + + // Check if there is a notification area container + const $area = $('#notificationArea'); + if (!$area.length) { + throw new Meteor.Error('NOTIFICATION_AREA_NOT_FOUND', `Notification area not found.`); + } + + let notificationPromise; + let templateData = { + text, + style, + timeout, + id: Notifications.currentId++ + }; + + if (promise instanceof Promise) { + // Use the given promise to control the notification + notificationPromise = templateData.promise = promise; + } else { + // Create a new promise to control the modal and store its resolve and reject callbacks + let promiseResolve; + let promiseReject; + notificationPromise = new Promise((resolve, reject) => { + promiseResolve = resolve; + promiseReject = reject; + }); + + // Render the notification passing the promise object and callbacks + _.extend({}, templateData, { + promise: notificationPromise, + promiseResolve, + promiseReject + }); + } + + const view = Blaze.renderWithData(Template.notificationNote, templateData, $area[0]); + const dismissNotification = () => Notifications.dismiss(templateData.id); + + // Add the current view to the list of views to allow clearing all notifications + Notifications.views.set(templateData.id, view); + + // Destroy the created notification view when the promise is either resolved or rejected + notificationPromise.then(dismissNotification).catch(dismissNotification); + + // Destroy the created notification view if the given timeout time has passed + if (timeout > 0) { + Meteor.setTimeout(() => { + if (templateData.promiseResolve) { + templateData.promiseResolve(); + } else { + dismissNotification(); + } + }, timeout); + } + + // Return the promise to allow callbacks stacking from outside + return notificationPromise; +}; + +OHIF.ui.notifications = Notifications; diff --git a/Packages/ohif-hotkeys/client/components/form.html b/Packages/ohif-hotkeys/client/components/form.html index 185b8b1f1..6fd147fd5 100644 --- a/Packages/ohif-hotkeys/client/components/form.html +++ b/Packages/ohif-hotkeys/client/components/form.html @@ -15,7 +15,7 @@
{{#button class='btn btn-danger pull-left' action='resetDefaults'}}Reset to Defaults{{/button}} {{#button class='btn btn-primary pull-right' action='save'}}Save{{/button}} - {{#button class='btn btn-default pull-right m-r-1' tagAttributes=(extend '' data-dismiss='modal')}}Cancel{{/button}} + {{#button class='btn btn-default pull-right m-r-1' tagAttributes=(extend '' data-dismiss='modal')}}Close{{/button}}
{{/form}} diff --git a/Packages/ohif-hotkeys/client/components/form.js b/Packages/ohif-hotkeys/client/components/form.js index 1bae95c4a..47a9ef790 100644 --- a/Packages/ohif-hotkeys/client/components/form.js +++ b/Packages/ohif-hotkeys/client/components/form.js @@ -11,7 +11,12 @@ Template.hotkeysForm.onCreated(() => { const { contextName } = instance.data; const form = instance.$('form').first().data('component'); const definitions = form.value(); - return OHIF.hotkeys.store(contextName, definitions); + const promise = OHIF.hotkeys.store(contextName, definitions); + promise.then(() => OHIF.ui.notifications.show({ + text: 'The keyboard shortcut preferences were successfully saved.', + style: 'success' + })); + return promise; }, resetDefaults() {