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 @@ 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 });