diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions
index 7f68afcf0..1bc3ff4fd 100644
--- a/LesionTracker/.meteor/versions
+++ b/LesionTracker/.meteor/versions
@@ -97,6 +97,8 @@ ohif:log@0.0.1
ohif:measurements@0.0.1
ohif:metadata@0.0.1
ohif:study-list@0.0.1
+ohif:themes@0.0.1
+ohif:themes-common@0.0.1
ohif:user-management@0.0.1
ohif:viewerbase@0.0.1
ohif:wadoproxy@0.0.1
diff --git a/Packages/ohif-core/client/components/base/index.js b/Packages/ohif-core/client/components/base/index.js
index f961c003e..521942557 100644
--- a/Packages/ohif-core/client/components/base/index.js
+++ b/Packages/ohif-core/client/components/base/index.js
@@ -12,6 +12,7 @@ import './mixins/action.js';
import './mixins/button.js';
import './mixins/checkbox.js';
import './mixins/component.js';
+import './mixins/dropdown.js';
import './mixins/form.js';
import './mixins/formItem.js';
import './mixins/group.js';
diff --git a/Packages/ohif-core/client/components/base/mixin.js b/Packages/ohif-core/client/components/base/mixin.js
index 355ec1b6b..fa4979404 100644
--- a/Packages/ohif-core/client/components/base/mixin.js
+++ b/Packages/ohif-core/client/components/base/mixin.js
@@ -76,6 +76,9 @@ class Mixin {
static initData(data) {
// Split the mixins by space
const mixinsArray = data.mixins.split(' ');
+
+ // Control and ignore the mixins that have already been applied
+ const appliedOnData = [];
_.each(mixinsArray, mixinName => {
// Ignore blank strings
if (!mixinName) {
@@ -86,7 +89,7 @@ class Mixin {
const mixin = Mixin.getMixin(mixinName);
// Initialize the data manipulation composition
- mixin.init(null, data, [], ['onData']);
+ mixin.init(null, data, appliedOnData, ['onData']);
});
}
@@ -94,6 +97,10 @@ class Mixin {
static initAll(template, data) {
// Split the mixins by space
const mixinsArray = data.mixins.split(' ');
+
+ // Control and ignore the mixins that have already been applied
+ const appliedCommon = [];
+ const appliedOnMixins = [];
_.each(mixinsArray, mixinName => {
// Ignore blank strings
if (!mixinName) {
@@ -104,10 +111,10 @@ class Mixin {
const mixin = Mixin.getMixin(mixinName);
// Initialize blaze default compositions
- mixin.init(template, data, [], ['onCreated', 'onRendered', 'onDestroyed', 'events', 'helpers']);
+ mixin.init(template, data, appliedCommon, ['onCreated', 'onRendered', 'onDestroyed', 'events', 'helpers']);
// Execute some behaviors after all mixins are applied
- mixin.init(template, data, [], ['onMixins']);
+ mixin.init(template, data, appliedOnMixins, ['onMixins']);
});
}
diff --git a/Packages/ohif-core/client/components/base/mixins/action.js b/Packages/ohif-core/client/components/base/mixins/action.js
index d645b93c4..799cf7620 100644
--- a/Packages/ohif-core/client/components/base/mixins/action.js
+++ b/Packages/ohif-core/client/components/base/mixins/action.js
@@ -1,5 +1,5 @@
-import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating';
+import { OHIF } from 'meteor/ohif:core';
/*
* action: controls an element that will trigger some form API's method
@@ -11,24 +11,32 @@ OHIF.mixins.action = new OHIF.Mixin({
const instance = Template.instance();
const component = instance.component;
+ // Add the form-action identification class
component.$element.addClass('form-action');
},
events: {
'click .form-action'(event, instance) {
+ event.preventDefault();
const component = instance.component;
- // Extract the action and the params
- const { action, params } = instance.data;
+ // Extract action, params and disabled state
+ const { action, params, disabled } = instance.data;
+
+ // Stop here if the component is disabled
+ if (disabled) return;
// Get the current component's API
const api = component.getApi();
- // Stop here if no API or action was defined
- if (!api || !action || typeof api[action] !== 'function') {
- return;
+ // Stop here calling the action if it's a function
+ if (typeof action === 'function') {
+ return action.call(this, params);
}
+ // Stop here if no API or action was defined
+ if (!api || !action || typeof api[action] !== 'function') return;
+
// Call the defined action function
api[action].call(this, params);
}
diff --git a/Packages/ohif-core/client/components/base/mixins/dropdown.js b/Packages/ohif-core/client/components/base/mixins/dropdown.js
new file mode 100644
index 000000000..7956860ac
--- /dev/null
+++ b/Packages/ohif-core/client/components/base/mixins/dropdown.js
@@ -0,0 +1,81 @@
+import { Meteor } from 'meteor/meteor';
+import { Template } from 'meteor/templating';
+import { Blaze } from 'meteor/blaze';
+import { OHIF } from 'meteor/ohif:core';
+
+/*
+ * dropdown: controls a dropdown
+ */
+OHIF.mixins.dropdown = new OHIF.Mixin({
+ dependencies: 'form',
+ composition: {
+ onRendered() {
+ const instance = Template.instance();
+ const { event, centered } = instance.data.options;
+
+ // Destroy the Blaze created view (either created with template calls or with renderWithData)
+ instance.destroyView = () => {
+ if (typeof instance.data.destroyView === 'function') {
+ instance.data.destroyView();
+ } else {
+ Blaze.remove(instance.view);
+ }
+ };
+
+ // Get the dropdown element to enable position manipulation
+ const $dropdown = instance.$('.dropdown');
+ const dropdown = $dropdown[0];
+ const $dropdownMenu = $dropdown.children('.dropdown-menu');
+
+ dropdown.oncontextmenu = () => false;
+
+ const cssBefore = {
+ 'z-index': 10000
+ };
+ if (event) {
+ cssBefore.position = 'fixed';
+ $dropdownMenu.bounded();
+ }
+
+ $dropdownMenu.css(cssBefore).focus();
+
+ // Postpone visibility change to allow CSS transitions
+ Meteor.defer(() => {
+ // Show the dropdown
+ $dropdown.addClass('open');
+
+ // Change the dropdown position if mouse event was given
+ if (event) {
+ const position = {
+ left: event.clientX,
+ top: event.clientY
+ };
+
+ if (centered) {
+ position.left -= $dropdownMenu.outerWidth() / 2;
+ position.top -= $dropdownMenu.outerHeight() / 2;
+ }
+
+ $dropdownMenu.css(position).trigger('spatialChanged');
+ }
+ });
+ },
+
+ events: {
+ 'click .form-action.disabled'(event, instance) {
+ event.preventDefault();
+ event.stopPropagation();
+ },
+
+ 'click .dropdown'(event, instance) {
+ const $target = $(event.target);
+ if ($target.hasClass('disabled')) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ instance.destroyView();
+ }
+ }
+ }
+ }
+});
diff --git a/Packages/ohif-core/client/components/base/mixins/link.js b/Packages/ohif-core/client/components/base/mixins/link.js
index 717a99d1b..f9e34c69e 100644
--- a/Packages/ohif-core/client/components/base/mixins/link.js
+++ b/Packages/ohif-core/client/components/base/mixins/link.js
@@ -1,5 +1,5 @@
-import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating';
+import { OHIF } from 'meteor/ohif:core';
/*
* link: controls a link
diff --git a/Packages/ohif-core/client/components/bootstrap/dropdown/form.html b/Packages/ohif-core/client/components/bootstrap/dropdown/form.html
index f79a2ae99..d5dd7d4d9 100644
--- a/Packages/ohif-core/client/components/bootstrap/dropdown/form.html
+++ b/Packages/ohif-core/client/components/bootstrap/dropdown/form.html
@@ -1,8 +1,29 @@
- {{#form class='dropdown' api=this.api hideValidationBox=true}}
+ {{#form (extend this mixins='dropdown' class='dropdown' hideValidationBox=true)}}
{{>dropdownBackdrop}}
-
diff --git a/Packages/ohif-core/client/components/bootstrap/dropdown/form.js b/Packages/ohif-core/client/components/bootstrap/dropdown/form.js
deleted file mode 100644
index 407c081a3..000000000
--- a/Packages/ohif-core/client/components/bootstrap/dropdown/form.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Template } from 'meteor/templating';
-import { Blaze } from 'meteor/blaze';
-import { $ } from 'meteor/jquery';
-import { _ } from 'meteor/underscore';
-
-Template.dropdownForm.onRendered(() => {
- const instance = Template.instance();
- const dropdown = instance.$('.dropdown');
-
- // Show the dropdown
- dropdown.addClass('open');
- dropdown.find('ul.dropdown-menu li a').addClass('noselect');
-
- // Destroy the Blaze created view (either created with template calls or with renderWithData)
- instance.destroyView = () => {
- if (_.isFunction(instance.data.destroyView)) {
- instance.data.destroyView();
- } else {
- Blaze.remove(instance.view);
- }
- };
-});
-
-Template.dropdownForm.events({
- 'click .dropdown'(event, instance) {
- let target = $(event.target);
- if (target.hasClass('disabled')) {
- event.preventDefault();
- event.stopPropagation();
- } else {
- instance.destroyView();
- }
- }
-});
diff --git a/Packages/ohif-core/client/components/bootstrap/index.js b/Packages/ohif-core/client/components/bootstrap/index.js
index ef8169b87..3fef3385d 100644
--- a/Packages/ohif-core/client/components/bootstrap/index.js
+++ b/Packages/ohif-core/client/components/bootstrap/index.js
@@ -17,7 +17,6 @@ import './dialog/unsavedChangesDialog.js';
import './dropdown/backdrop.html';
import './dropdown/form.html';
-import './dropdown/form.js';
import './form/button.html';
import './form/form.html';
diff --git a/Packages/ohif-core/client/ui/bounded/bounded.js b/Packages/ohif-core/client/ui/bounded/bounded.js
index d76ca4af7..2a328c345 100644
--- a/Packages/ohif-core/client/ui/bounded/bounded.js
+++ b/Packages/ohif-core/client/ui/bounded/bounded.js
@@ -1,3 +1,5 @@
+import { $ } from 'meteor/jquery';
+import { _ } from 'meteor/underscore';
import { OHIF } from 'meteor/ohif:core';
// Allow attaching to jQuery selectors
@@ -61,6 +63,19 @@ class Bounded {
// Add the bounded class to the element
this.$element.addClass('bounded');
+ // Handle the positioning on window resize
+ const $window = $(window);
+ const windowResizeHandler = () => {
+ // Check if the element is still in DOM and remove the handler if it is not
+ if (!this.$element.closest(document.documentElement).length) {
+ $window.off('resize', windowResizeHandler);
+ }
+
+ this.$element.trigger('spatialChanged');
+ };
+
+ $window.on('resize', windowResizeHandler);
+
// Trigger the bounding check for the first timepoint
setTimeout(() => this.$element.trigger('spatialChanged'));
}
diff --git a/Packages/ohif-core/client/ui/dropdown/dropdown.js b/Packages/ohif-core/client/ui/dropdown/dropdown.js
new file mode 100644
index 000000000..423feb766
--- /dev/null
+++ b/Packages/ohif-core/client/ui/dropdown/dropdown.js
@@ -0,0 +1,29 @@
+import { Template } from 'meteor/templating';
+import { Blaze } from 'meteor/blaze';
+import { OHIF } from 'meteor/ohif:core';
+
+OHIF.ui.showDropdown = (items=[], options={}) => {
+ // Prepare the method to destroy the view
+ let view;
+ const destroyView = () => Blaze.remove(view);
+ const templateData = {
+ items,
+ options,
+ destroyView
+ };
+
+ // Render the dialog with the given template and data
+ const parentElement = options.parentElement || document.body;
+ view = Blaze.renderWithData(Template.dropdownForm, templateData, parentElement);
+};
+
+Meteor.startup(() => {
+ OHIF.ui.showDropdown([{
+ text: 'test 1',
+ action: () => console.warn('test1Clicked'),
+ separatorAfter: true
+ }, {
+ text: 'test 2',
+ action: () => console.warn('test2Clicked')
+ }]);
+});
diff --git a/Packages/ohif-core/client/ui/index.js b/Packages/ohif-core/client/ui/index.js
index 48cc0e7f1..917a09dba 100644
--- a/Packages/ohif-core/client/ui/index.js
+++ b/Packages/ohif-core/client/ui/index.js
@@ -4,6 +4,7 @@ import './dialog/display.js';
import './dialog/spatial.js';
import './dialog/unsavedChangesDialog.js';
import './draggable/draggable.js';
+import './dropdown/dropdown.js';
import './dropdown/form.js';
import './resizable/resizable.js';
import './unsavedChanges/unsavedChanges.js';
diff --git a/Packages/ohif-header/components/header.html b/Packages/ohif-header/components/header.html
new file mode 100644
index 000000000..5c2ba39d0
--- /dev/null
+++ b/Packages/ohif-header/components/header.html
@@ -0,0 +1,12 @@
+
+
+
diff --git a/Packages/ohif-header/components/header.styl b/Packages/ohif-header/components/header.styl
new file mode 100644
index 000000000..7aef70132
--- /dev/null
+++ b/Packages/ohif-header/components/header.styl
@@ -0,0 +1,31 @@
+$expandedHeight = 160px
+
+.header
+ height: $topBarHeight
+ padding: 10px 10px 0
+ theme('color', '$textPrimaryColor')
+ theme('background-color', '$primaryBackgroundColor')
+ transition(all 0.5s ease)
+
+ &>.clearfix
+ position: relative
+
+ .brand
+ background-image: url(/packages/pim_webviewer/assets/logo.svg)
+ background-repeat: no-repeat
+ background-size: 100%
+ display: block
+ font-size: 9px
+ height: 3.5em
+ text-decoration: none
+ width: 34.6em
+
+ &.studyList
+ background-color: rgba(21, 25, 30, 0.7)
+ height: $expandedHeight
+ padding-left: $studyListPadding
+ padding-right: $studyListPadding
+
+ .brand
+ font-size: 16px
+ margin-top: 44px
diff --git a/Packages/ohif-header/index.js b/Packages/ohif-header/index.js
new file mode 100644
index 000000000..fd2f325d4
--- /dev/null
+++ b/Packages/ohif-header/index.js
@@ -0,0 +1,2 @@
+import './components/header.html';
+import './components/header.styl';
diff --git a/Packages/ohif-header/package.js b/Packages/ohif-header/package.js
new file mode 100644
index 000000000..860b55274
--- /dev/null
+++ b/Packages/ohif-header/package.js
@@ -0,0 +1,19 @@
+Package.describe({
+ name: 'ohif:header',
+ summary: 'OHIF Header Templates',
+ version: '0.0.1'
+});
+
+Package.onUse(function(api) {
+ api.versionsFrom('1.4.2.3');
+
+ // Meteor packages
+ api.use('ecmascript');
+ api.use('stylus');
+
+ // OHIF dependencies
+ api.use('ohif:core', 'client');
+
+ // Client imports
+ api.addFiles('index.js', 'client');
+});
diff --git a/Packages/ohif-themes/package.js b/Packages/ohif-themes/package.js
index 4422c85b5..53b9805cc 100644
--- a/Packages/ohif-themes/package.js
+++ b/Packages/ohif-themes/package.js
@@ -9,7 +9,7 @@ Package.onUse(function(api) {
api.use('stylus');
- api.use('ohif:themes-common');
+ api.use('ohif:themes-common', 'client');
// Importable themes related variables
api.addFiles('themes.styl', 'client', { isImport: true });