LT-275: Creating measure flow component layout and motion
This commit is contained in:
parent
82e374b1e3
commit
7fdfbf1c1a
@ -15,6 +15,15 @@ tracker # Meteor's client-side reactive programming library
|
||||
es5-shim # ECMAScript 5 compatibility for older browsers.
|
||||
ecmascript # Enable ECMAScript2015+ syntax in app code
|
||||
|
||||
clinical:router
|
||||
clinical:extended-api
|
||||
clinical:active-entry
|
||||
clinical:error-pages
|
||||
clinical:theming
|
||||
clinical:fonts
|
||||
clinical:hipaa-audit-log
|
||||
clinical:hipaa-logger
|
||||
|
||||
design
|
||||
ohif:core
|
||||
|
||||
@ -40,14 +49,6 @@ lesiontracker
|
||||
practicalmeteor:loglevel
|
||||
momentjs:moment
|
||||
|
||||
clinical:router
|
||||
clinical:extended-api
|
||||
clinical:active-entry
|
||||
clinical:error-pages
|
||||
clinical:theming
|
||||
clinical:fonts
|
||||
clinical:hipaa-audit-log
|
||||
clinical:hipaa-logger
|
||||
anti:gagarin@=0.4.11
|
||||
check
|
||||
aldeed:template-extension@4.0.0
|
||||
|
||||
@ -32,7 +32,8 @@ Package.onUse(function(api) {
|
||||
// Component styles
|
||||
api.addFiles([
|
||||
'styles/components/radio.styl',
|
||||
'styles/components/select2.styl'
|
||||
'styles/components/select2.styl',
|
||||
'styles/components/selectTree.styl'
|
||||
], 'client');
|
||||
|
||||
// Rounded Button Group
|
||||
|
||||
@ -46,6 +46,7 @@ span.select2.select2-container
|
||||
border-color: $uiGray
|
||||
border-radius: $borderRadius
|
||||
color: $gray4
|
||||
font-size: 12px
|
||||
line-height: 22px
|
||||
margin-top: 3px
|
||||
|
||||
|
||||
72
Packages/design/styles/components/selectTree.styl
Normal file
72
Packages/design/styles/components/selectTree.styl
Normal file
@ -0,0 +1,72 @@
|
||||
@import "{design}/app"
|
||||
|
||||
// TODO: [design] can't we use colors that are already in common pallete?
|
||||
$gray1 = #A3A3A3
|
||||
$gray2 = #A0A0A0
|
||||
$gray3 = #C4C4C4
|
||||
$gray4 = #C0C0C0
|
||||
$gray5 = #9B9B9B
|
||||
$gray6 = #303030
|
||||
|
||||
.select-tree-root
|
||||
background-color: white
|
||||
border: 1px solid $gray3
|
||||
border-radius: 5px
|
||||
display: table
|
||||
overflow: hidden
|
||||
|
||||
.tree-search
|
||||
background-color: $gray4
|
||||
border-bottom: 1px solid $gray3
|
||||
color: $gray6
|
||||
display: block
|
||||
margin: 0
|
||||
padding: 5px
|
||||
text-align: center
|
||||
|
||||
span
|
||||
font-weight: bold
|
||||
line-height: 24px
|
||||
|
||||
input
|
||||
border-color: $gray5
|
||||
font-weight: normal
|
||||
|
||||
.tree-breadcrumb
|
||||
line-height: 32px
|
||||
padding: 5px 12px
|
||||
|
||||
.tree-options
|
||||
overflow: hidden
|
||||
|
||||
.tree-node, .tree-leaf
|
||||
cursor: pointer
|
||||
display: block
|
||||
margin-bottom: 0
|
||||
padding: 0 12px
|
||||
position: relative
|
||||
z-index: 1
|
||||
|
||||
&>input
|
||||
display: none
|
||||
|
||||
.tree-node
|
||||
height: 41px
|
||||
border-bottom: 1px solid $gray3
|
||||
border-top: 1px solid $gray3
|
||||
line-height: 41px
|
||||
margin-top: -1px
|
||||
|
||||
&:last-child
|
||||
border-bottom: 0
|
||||
|
||||
.tree-leaf
|
||||
line-height: 26px
|
||||
|
||||
label
|
||||
transition(all 0.3s ease)
|
||||
|
||||
&.collapsed
|
||||
|
||||
&>.tree-breadcrumb, &>label:not(.active)
|
||||
display: none
|
||||
@ -1,5 +1,5 @@
|
||||
<template name="studyTimepointStudy">
|
||||
{{#let isSidebar=(not (isDefined viewportIndex))}}
|
||||
{{#let isSidebar=(isUndefined viewportIndex)}}
|
||||
<div class="studyTimepointStudy {{#if isSidebar}}studySidebarTimepoint{{else}}studyQuickSwitchTimepoint{{/if}} {{#if this.active}}active{{/if}}">
|
||||
<div class="studyItem">
|
||||
{{>loadingText}}
|
||||
|
||||
@ -13,6 +13,7 @@ import './mixins/input.js';
|
||||
import './mixins/schemaData.js';
|
||||
import './mixins/select.js';
|
||||
import './mixins/select2.js';
|
||||
import './mixins/selectTree.js';
|
||||
|
||||
// Templates
|
||||
import './templates/custom.html';
|
||||
|
||||
89
Packages/ohif-core/components/base/mixins/selectTree.js
Normal file
89
Packages/ohif-core/components/base/mixins/selectTree.js
Normal file
@ -0,0 +1,89 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
/*
|
||||
* input: controls a tree selection component
|
||||
*/
|
||||
OHIF.mixins.selectTree = new OHIF.Mixin({
|
||||
dependencies: 'group',
|
||||
composition: {
|
||||
onCreated() {
|
||||
const instance = Template.instance();
|
||||
const component = instance.component;
|
||||
|
||||
// Create the component's value storage property
|
||||
instance.data.currentNode = new ReactiveVar(null);
|
||||
|
||||
// Share the component in data property
|
||||
instance.data.component = component;
|
||||
|
||||
// Override the default value method
|
||||
component.value = value => {
|
||||
const isGet = _.isUndefined(value);
|
||||
|
||||
// Return the current node value
|
||||
if (isGet) {
|
||||
const currentNode = instance.data.currentNode.get();
|
||||
return currentNode ? currentNode.value : null;
|
||||
}
|
||||
|
||||
// get the items array
|
||||
const items = component.templateInstance.data.items;
|
||||
|
||||
// Get the node for the selected value
|
||||
const node = _.findWhere(items, {
|
||||
value: value
|
||||
});
|
||||
|
||||
// Change the current node
|
||||
instance.data.currentNode.set(node);
|
||||
};
|
||||
},
|
||||
|
||||
events: {
|
||||
'change .select-tree:first>.tree-options>label>input'(event, instance) {
|
||||
const component = instance.component;
|
||||
const $target = $(event.target);
|
||||
const eventComponent = $target.data('component');
|
||||
|
||||
// Change the active item
|
||||
const rootComponent = instance.data.root || component;
|
||||
rootComponent.templateInstance.$('label').removeClass('active');
|
||||
$target.closest('label.tree-leaf').addClass('active');
|
||||
|
||||
// Change the component's value
|
||||
component.value(eventComponent.value());
|
||||
},
|
||||
|
||||
'click .select-tree:first>.tree-options>.tree-breadcrumb>.tree-back'(event, instance) {
|
||||
// Prevent the default element action
|
||||
event.preventDefault();
|
||||
|
||||
// Reset the checked inputs to unchecked state
|
||||
const rootComponent = instance.data.root || instance.component;
|
||||
rootComponent.templateInstance.$('input').removeAttr('checked');
|
||||
|
||||
// Get the index of the breadcrumb's clicked option
|
||||
const index = $(event.currentTarget).attr('data-index') | 0;
|
||||
|
||||
// Set the current instance
|
||||
let currentInstance = instance.component.parent.templateInstance;
|
||||
|
||||
// Iterate over all parents until gets into the clicked node
|
||||
for (let i = 1; i <= index; i++) {
|
||||
// Unset the selected node
|
||||
currentInstance.data.currentNode.set(null);
|
||||
|
||||
// Check if it's not the root node
|
||||
if (i < index) {
|
||||
// Change the current instance
|
||||
currentInstance = currentInstance.component.parent.templateInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -5,6 +5,7 @@
|
||||
class="{{this.class}}"
|
||||
name="{{this.name}}"
|
||||
value="{{reactive this.value}}"
|
||||
placeholder="{{this.placeholder}}"
|
||||
{{this.tagAttributes}}
|
||||
>
|
||||
{{>UI.contentBlock}}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template name="form">
|
||||
{{#baseComponent (extend this
|
||||
base="baseForm"
|
||||
mixins=(concat "form " this.mixins)
|
||||
base='baseForm'
|
||||
mixins=(concat 'form ' this.mixins)
|
||||
)}}
|
||||
{{>UI.contentBlock}}
|
||||
{{/baseComponent}}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<template name="group">
|
||||
{{#baseComponent (extend this
|
||||
class=(concat 'component-group ' this.class)
|
||||
base="baseDiv"
|
||||
mixins=(concat "group " this.mixins)
|
||||
base='baseDiv'
|
||||
mixins=(concat 'group ' this.mixins)
|
||||
)}}
|
||||
{{>UI.contentBlock}}
|
||||
{{/baseComponent}}
|
||||
|
||||
@ -5,3 +5,4 @@ import './input/groupRadio.html';
|
||||
import './input/radio.html';
|
||||
import './input/select.html';
|
||||
import './input/text.html';
|
||||
import './input/selectTree.html';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template name="groupRadio">
|
||||
{{#group (extend this
|
||||
class=(concat 'group-radio ' this.class)
|
||||
mixins=(concat "groupRadio " this.mixins)
|
||||
mixins=(concat 'groupRadio ' this.mixins)
|
||||
wrappers=(concat ''
|
||||
(valueIf reactive this.label 'wrapperTitle ' '')
|
||||
this.wrappers
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
base='baseInput'
|
||||
type='radio'
|
||||
mixins=(concat 'input ' this.mixins)
|
||||
labelAfter=(valueIf (isDefined this.labelAfter) this.labelAfter true)
|
||||
labelAfter=(valueIf (isUndefined this.labelAfter) true this.labelAfter)
|
||||
labelClass=(concat 'checkboxLabel ' this.labelClass)
|
||||
wrappers=(concat ''
|
||||
(valueIf reactive this.label 'wrapperLabel ' '')
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
<template name="selectTree">
|
||||
{{#group (extend this
|
||||
class=(concat
|
||||
'select-tree '
|
||||
(valueIf this.root '' 'select-tree-root ')
|
||||
this.class
|
||||
)
|
||||
mixins=(concat 'selectTree ' this.mixins)
|
||||
)}}
|
||||
{{>UI.contentBlock}}
|
||||
{{#let currentNode=this.currentNode.get}}
|
||||
{{#if (and (not this.root) (ne this.search false))}}
|
||||
{{>inputText
|
||||
label=this.label
|
||||
labelClass='tree-search'
|
||||
placeholder=(choose this.searchPlaceholder 'Search')
|
||||
}}
|
||||
{{/if}}
|
||||
<div class="tree-options {{#if currentNode}}collapsed{{/if}}">
|
||||
{{#if this.root}}
|
||||
{{>selectTreeBreadcrumb component=this.component}}
|
||||
{{/if}}
|
||||
{{#each item in (reactive this.items)}}
|
||||
{{>inputRadio
|
||||
class=this.radioClass
|
||||
name=this.key
|
||||
value=item.value
|
||||
label=item.label
|
||||
labelClass=(valueIf (isArray item.items) 'tree-node' 'tree-leaf')
|
||||
checked=(eq item.value (reactive this.value))
|
||||
}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if (and currentNode (isArray currentNode.items))}}
|
||||
{{>selectTree (clone this
|
||||
items=currentNode.items
|
||||
root=(choose this.root this.component)
|
||||
class='select-tree-node'
|
||||
)}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/group}}
|
||||
</template>
|
||||
|
||||
<template name="selectTreeBreadcrumb">
|
||||
<div class="tree-breadcrumb">
|
||||
{{>selectTreeBreadcrumbParent
|
||||
component=this.component.parent
|
||||
index=1
|
||||
}}
|
||||
{{#let currentNode=this.component.parent.templateInstance.data.currentNode.get}}
|
||||
<span class="tree-current-node">{{currentNode.label}}</span>
|
||||
{{/let}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="selectTreeBreadcrumbParent">
|
||||
{{#let parent=this.component.parent}}
|
||||
{{#let parentNode=(reactive parent.templateInstance.data.currentNode)}}
|
||||
{{#if parentNode}}
|
||||
{{>selectTreeBreadcrumbParent
|
||||
component=parent
|
||||
index=(sum this.index 1)
|
||||
}}
|
||||
<a href="#" class="tree-back" data-index="{{this.index}}">{{parentNode.label}}</a>
|
||||
<span>/</span>
|
||||
{{else}}
|
||||
<a href="#" class="tree-back" data-index="{{this.index}}"><i class="fa fa-fast-backward"></i></a>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</template>
|
||||
@ -1,2 +1,4 @@
|
||||
import './base';
|
||||
import './bootstrap';
|
||||
import './playground/playground.html';
|
||||
import './playground/playground.js';
|
||||
|
||||
10
Packages/ohif-core/components/playground/playground.html
Normal file
10
Packages/ohif-core/components/playground/playground.html
Normal file
@ -0,0 +1,10 @@
|
||||
<template name="componentPlayground">
|
||||
<div class="p-a-3">
|
||||
{{>selectTree
|
||||
key='label'
|
||||
items=instance.items
|
||||
label='Assign label'
|
||||
searchPlaceholder='Search labels'
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
44
Packages/ohif-core/components/playground/playground.js
Normal file
44
Packages/ohif-core/components/playground/playground.js
Normal file
@ -0,0 +1,44 @@
|
||||
Template.componentPlayground.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
instance.items = [{
|
||||
label: 'Category 1',
|
||||
value: 'Category 1',
|
||||
items: [{
|
||||
label: 'Subcategory 1.1',
|
||||
value: 'Subcategory 1.1'
|
||||
}, {
|
||||
label: 'Subcategory 1.2',
|
||||
value: 'Subcategory 1.2',
|
||||
items: [{
|
||||
label: 'Subcategory 1.2.1',
|
||||
value: 'Subcategory 1.2.1'
|
||||
}, {
|
||||
label: 'Subcategory 1.2.2',
|
||||
value: 'Subcategory 1.2.2',
|
||||
items: [{
|
||||
label: 'Subcategory 1.2.2.1',
|
||||
value: 'Subcategory 1.2.2.1'
|
||||
}, {
|
||||
label: 'Subcategory 1.2.2.2',
|
||||
value: 'Subcategory 1.2.2.2'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: 'Category 2',
|
||||
value: 'Category 2',
|
||||
items: [{
|
||||
label: 'Subcategory 2.1',
|
||||
value: 'Subcategory 2.1'
|
||||
}, {
|
||||
label: 'Subcategory 2.2',
|
||||
value: 'Subcategory 2.2'
|
||||
}, {
|
||||
label: 'Subcategory 2.3',
|
||||
value: 'Subcategory 2.3'
|
||||
}]
|
||||
}, {
|
||||
label: 'Category 3',
|
||||
value: 'Category 3'
|
||||
}];
|
||||
});
|
||||
17
Packages/ohif-core/helpers/debug.js
Normal file
17
Packages/ohif-core/helpers/debug.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
/**
|
||||
* Global Blaze UI helpers to development debugging
|
||||
*/
|
||||
|
||||
// Stop here if it's not development environment
|
||||
if (!Meteor.isDevelopment) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Debug some value on console
|
||||
Template.registerHelper('debug', (...values) => {
|
||||
console.debug(...values);
|
||||
});
|
||||
@ -1,4 +1,7 @@
|
||||
import './blaze';
|
||||
import './data';
|
||||
import './logical';
|
||||
import './string';
|
||||
import './blaze.js';
|
||||
import './data.js';
|
||||
import './debug.js';
|
||||
import './logical.js';
|
||||
import './number.js';
|
||||
import './string.js';
|
||||
import './typing.js';
|
||||
|
||||
@ -17,7 +17,7 @@ Template.registerHelper('eq', (a, b) => {
|
||||
|
||||
// Check if two values are different
|
||||
Template.registerHelper('ne', (a, b) => {
|
||||
return a === b;
|
||||
return a !== b;
|
||||
});
|
||||
|
||||
// Check if the first value is greater than the second one
|
||||
@ -67,8 +67,3 @@ Template.registerHelper('valueIf', (condition, valueIfTrue, valueIfFalse) => {
|
||||
|
||||
return valueIfFalse;
|
||||
});
|
||||
|
||||
// Check if the value is different from undefined
|
||||
Template.registerHelper('isDefined', value => {
|
||||
return typeof value !== 'undefined';
|
||||
});
|
||||
|
||||
13
Packages/ohif-core/helpers/number.js
Normal file
13
Packages/ohif-core/helpers/number.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
/**
|
||||
* Global Blaze UI helpers to work with numeric operations
|
||||
*/
|
||||
|
||||
// Sum all the given numbers
|
||||
Template.registerHelper('sum', (...values) => {
|
||||
let result = 0;
|
||||
_.each(_.initial(values, 1), value => (result += (value | 0)));
|
||||
return result;
|
||||
});
|
||||
21
Packages/ohif-core/helpers/typing.js
Normal file
21
Packages/ohif-core/helpers/typing.js
Normal file
@ -0,0 +1,21 @@
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { Template } from 'meteor/templating';
|
||||
|
||||
/**
|
||||
* Global Blaze UI helpers to work with Strings
|
||||
*/
|
||||
|
||||
// Check if the value's type is undefined
|
||||
Template.registerHelper('isUndefined', value => {
|
||||
return _.isUndefined(value);
|
||||
});
|
||||
|
||||
// Check if the value's type is object
|
||||
Template.registerHelper('isObject', value => {
|
||||
return _.isObject(value);
|
||||
});
|
||||
|
||||
// Check if the value is an array instance
|
||||
Template.registerHelper('isArray', value => {
|
||||
return _.isArray(value);
|
||||
});
|
||||
@ -1 +1,2 @@
|
||||
import './blaze.js';
|
||||
import './string.js';
|
||||
|
||||
33
Packages/ohif-core/lib/string.js
Normal file
33
Packages/ohif-core/lib/string.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
OHIF.string = {};
|
||||
|
||||
// Search for some string inside any object or array
|
||||
OHIF.string.search = (object, query, property=null, result=[]) => {
|
||||
// Create the search pattern
|
||||
const pattern = new RegExp($.trim(query).replace(/\s/gi, '|'), 'i');
|
||||
|
||||
_.each(object, item => {
|
||||
// Stop here if item is empty
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the value to be compared
|
||||
const value = _.isString(property) ? item[property] : item;
|
||||
|
||||
// Check if the value match the pattern
|
||||
if (_.isString(value) && pattern.test(value)) {
|
||||
// Add the current item to the result
|
||||
result.push(item);
|
||||
} else if (_.isObject(value)) {
|
||||
// Search recursively the item if the current item is an object
|
||||
OHIF.string.search(value, query, property, result);
|
||||
}
|
||||
});
|
||||
|
||||
// Return the found items
|
||||
return result;
|
||||
};
|
||||
@ -1,6 +1,13 @@
|
||||
export { OHIF } from './ohif.js';
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Router } from 'meteor/clinical:router';
|
||||
|
||||
import './lib';
|
||||
import './components';
|
||||
import './helpers';
|
||||
import './ui';
|
||||
|
||||
Router.route('/playground', function() {
|
||||
this.render('componentPlayground');
|
||||
});
|
||||
|
||||
@ -15,6 +15,10 @@ Package.onUse(function(api) {
|
||||
api.use('templating');
|
||||
api.use('reactive-var');
|
||||
|
||||
// Router dependencies
|
||||
api.use('clinical:router@2.0.18', 'client');
|
||||
|
||||
// Component's library dependencies
|
||||
api.use('natestrauser:select2@4.0.1', 'client');
|
||||
|
||||
// UI Styles
|
||||
|
||||
Loading…
Reference in New Issue
Block a user