LT-275: Creating measure flow component layout and motion

This commit is contained in:
Bruno Alves de Faria 2016-07-25 17:19:47 -03:00
parent 82e374b1e3
commit 7fdfbf1c1a
26 changed files with 415 additions and 26 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

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

View File

@ -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';

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

View File

@ -5,6 +5,7 @@
class="{{this.class}}"
name="{{this.name}}"
value="{{reactive this.value}}"
placeholder="{{this.placeholder}}"
{{this.tagAttributes}}
>
{{>UI.contentBlock}}

View File

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

View File

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

View File

@ -5,3 +5,4 @@ import './input/groupRadio.html';
import './input/radio.html';
import './input/select.html';
import './input/text.html';
import './input/selectTree.html';

View File

@ -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

View File

@ -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 ' '')

View File

@ -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>

View File

@ -1,2 +1,4 @@
import './base';
import './bootstrap';
import './playground/playground.html';
import './playground/playground.js';

View 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>

View 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'
}];
});

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

View File

@ -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';

View File

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

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

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

View File

@ -1 +1,2 @@
import './blaze.js';
import './string.js';

View 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;
};

View File

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

View File

@ -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