diff --git a/Packages/design/styles/components/selectTree.styl b/Packages/design/styles/components/selectTree.styl index 057529da4..b365790d4 100644 --- a/Packages/design/styles/components/selectTree.styl +++ b/Packages/design/styles/components/selectTree.styl @@ -161,9 +161,9 @@ $gray6 = #303030 height: 100% padding-left: 6px position: absolute - right: -100% + right: 0 top: 0 - width: 100% + width: 50% .content background-color: $gray6 diff --git a/Packages/ohif-core/client/components/bootstrap/input/selectTree.js b/Packages/ohif-core/client/components/bootstrap/input/selectTree.js index 5e065df73..e2bf54120 100644 --- a/Packages/ohif-core/client/components/bootstrap/input/selectTree.js +++ b/Packages/ohif-core/client/components/bootstrap/input/selectTree.js @@ -24,6 +24,9 @@ Template.selectTree.onRendered(() => { instance.component = component; + // Set the margin to display the common section + $treeRoot.children('.tree-content').css('margin-right', $treeRoot.width()); + // Start the component transitions $treeRoot.addClass('started'); @@ -96,8 +99,14 @@ Template.selectTree.onRendered(() => { Template.selectTree.events({ 'click .select-tree-root>.tree-content'(event, instance) { + // Get the tree root + const $treeRoot = $(event.currentTarget).closest('.select-tree-root'); + // Detect the first interaction with the component and do the animation - $(event.currentTarget).parent().addClass('interacted'); + $treeRoot.addClass('interacted'); + + // Remove the margin after the common section is closed + $treeRoot.children('.tree-content').css('margin-right', ''); }, 'input .tree-search input'(event, instance) { diff --git a/Packages/ohif-core/client/ui/bounded/bounded.js b/Packages/ohif-core/client/ui/bounded/bounded.js new file mode 100644 index 000000000..96205424c --- /dev/null +++ b/Packages/ohif-core/client/ui/bounded/bounded.js @@ -0,0 +1,145 @@ +// Allow attaching to jQuery selectors +$.fn.bounded = function(options) { + _.each(this, element => { + const boundedInstance = $(element).data('boundedInstance'); + if (options === 'destroy' && boundedInstance) { + $(element).removeData('boundedInstance'); + boundedInstance.destroy(); + } else { + if (boundedInstance) { + boundedInstance.options(options); + } else { + $(element).data('boundedInstance', new Bounded(element, options)); + } + } + }); + + return this; +}; + +/** + * This class makes an element bounded to other element's borders. + */ +class Bounded { + + // Initialize the instance with the given element and options + constructor(element, options={}) { + this.element = element; + this.$element = $(element); + this.options(options); + } + + // Set or change the instance options + options(options={}) { + // Process the given options and store it in the instance + const { boundingElement, allowResizing } = options; + this.boundingElement = boundingElement || window; + this.$boundingElement = $(this.boundingElement); + this.allowResizing = allowResizing; + + // Destroy and initialize again the instance + this.destroy(); + this.init(); + } + + // Initialize the bounding behaviour + init() { + // Create the event handlers + this.defineEventHandlers(); + + // Attach the created event handlers to the component + this.attachEventHandlers(); + + // Add the bounded class to the element + this.$element.addClass('bounded'); + } + + // Destroy this instance, returning the element to its previous state + destroy() { + // Detach the event handlers + this.detachEventHandlers(); + + // Remove the bounded class from the element + this.$element.removeClass('bounded'); + } + + spatialInfo(element) { + // Create the result object + const result = {}; + + // Get the jQuery object for the element + const el = element === window ? window.document.body : element; + const $element = $(el); + + // Get the element style + const style = el.style; + + // Get the integer numbers for element's width + if (style.width && style.width.indexOf('px') > -1) { + result.width = parseInt(style.width); + } else { + result.width = $element.outerWidth(); + } + + // Get the integer numbers for element's height + if (style.height && style.height.indexOf('px') > -1) { + result.height = parseInt(style.height); + } else { + result.height = $element.outerHeight(); + } + + // Get the element's start position + const offset = $element.offset(); + result.x0 = offset.left; + result.y0 = offset.top; + + // Get the element's end position + result.x1 = result.x0 + result.width; + result.y1 = result.y0 + result.height; + + // Return the result object + return result; + } + + // Define the event handlers for this class + defineEventHandlers() { + this.spatialChangedHandler = event => { + // Get the spatial information for element and its bounding element + const elementInfo = this.spatialInfo(this.element); + const boundingInfo = this.spatialInfo(this.boundingElement); + + // Fix element's x positioning and width + if (this.allowResizing && elementInfo.width > boundingInfo.width) { + this.$element.width(boundingInfo.width); + this.$element.css('left', boundingInfo.x0); + } else if (elementInfo.x0 < boundingInfo.x0) { + this.$element.css('left', boundingInfo.x0); + } else if (elementInfo.x1 > boundingInfo.x1) { + this.$element.css('left', boundingInfo.x1 - elementInfo.width); + } + + // Fix element's y positioning and height + if (this.allowResizing && elementInfo.height > boundingInfo.height) { + this.$element.height(boundingInfo.height); + this.$element.css('top', boundingInfo.y0); + } else if (elementInfo.y0 < boundingInfo.y0) { + this.$element.css('top', boundingInfo.y0); + } else if (elementInfo.y1 > boundingInfo.y1) { + this.$element.css('top', boundingInfo.y1 - elementInfo.height); + } + }; + } + + // Attach the event handlers to the element in order to bound it + attachEventHandlers() { + this.$element.on('spatialChanged', this.spatialChangedHandler); + this.$boundingElement.on('resize', this.spatialChangedHandler); + } + + // Detach the event handlers from the element + detachEventHandlers() { + this.$element.off('spatialChanged', this.spatialChangedHandler); + this.$boundingElement.off('resize', this.spatialChangedHandler); + } + +} diff --git a/Packages/ohif-core/client/ui/index.js b/Packages/ohif-core/client/ui/index.js index ad5af1950..2b2c872b5 100644 --- a/Packages/ohif-core/client/ui/index.js +++ b/Packages/ohif-core/client/ui/index.js @@ -1,2 +1,3 @@ +import './bounded/bounded.js'; import './draggable/draggable.js'; import './resizable/resizable.js'; diff --git a/Packages/ohif-core/client/ui/resizable/resizable.js b/Packages/ohif-core/client/ui/resizable/resizable.js index 906a69af9..edde14ab0 100644 --- a/Packages/ohif-core/client/ui/resizable/resizable.js +++ b/Packages/ohif-core/client/ui/resizable/resizable.js @@ -39,6 +39,8 @@ class Resizable { } init() { + this.defineEventHandlers(); + for (var x = -1; x <= 1; x++) { for (var y = -1; y <= 1; y++) { this.createBound(x, y); @@ -49,13 +51,23 @@ class Resizable { } destroy() { - this.$element.removeClass('resizable').find('resize-bound').remove(); + // Remove the instance added classes + this.$element.removeClass('resizable resizing'); + + // Get the bound borders + const $bound = this.$element.find('resize-bound'); + + // Remove the bound borders + $bound.remove(); + + // Detach the event handlers + this.detachEventHandlers($bound); } - attachEventHandlers($bound, xDirection, yDirection) { - const $window = $(window); + defineEventHandlers() { + this.initResizeHandler = event => { + const $window = $(window); - const initResizeHandler = event => { event.stopPropagation(); this.width = this.initialWidth = this.$element.width(); @@ -73,11 +85,12 @@ class Resizable { this.$element.addClass('resizing'); - $window.on('mousemove', resizeHandler); - $window.on('mouseup', endResizeHandler); + $window.on('mousemove', event.data, this.resizeHandler); + $window.on('mouseup', event.data, this.endResizeHandler); }; - const resizeHandler = event => { + this.resizeHandler = event => { + const { xDirection, yDirection } = event.data; let x, y; x = event.clientX < 0 ? 0 : event.clientX; x = x > window.innerWidth ? window.innerWidth : x; @@ -113,14 +126,30 @@ class Resizable { } }; - const endResizeHandler = event => { - $window.off('mousemove', resizeHandler); - $window.off('mouseup', endResizeHandler); + this.endResizeHandler = event => { + const $window = $(window); + + $window.off('mousemove', this.resizeHandler); + $window.off('mouseup', this.endResizeHandler); this.$element.removeClass('resizing'); }; + } - $bound.on('mousedown', initResizeHandler); + attachEventHandlers($bound, xDirection, yDirection) { + const eventData = { + xDirection, + yDirection + }; + $bound.on('mousedown', eventData, this.initResizeHandler); + } + + detachEventHandlers($bound) { + const $window = $(window); + + $bound.off('mousedown', this.initResizeHandler); + $window.off('mousemove', this.resizeHandler); + $window.off('mouseup', this.endResizeHandler); } createBound(xDirection, yDirection) {