LT-253: Creating select2 component
This commit is contained in:
parent
9d31b49997
commit
8564c52981
@ -100,7 +100,6 @@ promise@0.7.3
|
|||||||
random@1.0.10
|
random@1.0.10
|
||||||
rate-limit@1.0.5
|
rate-limit@1.0.5
|
||||||
reactive-dict@1.1.8
|
reactive-dict@1.1.8
|
||||||
reactive-form-controls@0.0.1
|
|
||||||
reactive-var@1.0.10
|
reactive-var@1.0.10
|
||||||
reload@1.1.10
|
reload@1.1.10
|
||||||
retry@1.0.8
|
retry@1.0.8
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
<template name="radioOption">
|
|
||||||
<label class="radio-option">
|
|
||||||
<input type="radio"
|
|
||||||
name="{{this.name}}"
|
|
||||||
value="{{this.value}}"
|
|
||||||
class="{{this.class}}"
|
|
||||||
checked="{{#if this.checked}}checked{{/if}}"/>
|
|
||||||
<span>{{this.label}}</span>
|
|
||||||
</label>
|
|
||||||
</template>
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
@import "{design}/app"
|
|
||||||
|
|
||||||
label.radio-option
|
|
||||||
cursor: pointer
|
|
||||||
input
|
|
||||||
height: 0
|
|
||||||
width: 0
|
|
||||||
span
|
|
||||||
padding-left: 23px
|
|
||||||
position: relative
|
|
||||||
&:before
|
|
||||||
background: white
|
|
||||||
border-radius: 8px
|
|
||||||
content: ''
|
|
||||||
display: block
|
|
||||||
height: 16px
|
|
||||||
left: 0
|
|
||||||
position: absolute
|
|
||||||
top: 50%
|
|
||||||
transform(translateY(-50%))
|
|
||||||
width: 16px
|
|
||||||
input:focus + span:before
|
|
||||||
// TODO: [design] define a outline for the design
|
|
||||||
outline: none
|
|
||||||
box-shadow: 0 0 2px 2px $textSecondaryColor
|
|
||||||
input:checked + span:after
|
|
||||||
background: $uiBorderColorDark
|
|
||||||
border-radius: 5px
|
|
||||||
content: ''
|
|
||||||
height: 10px
|
|
||||||
left: 3px
|
|
||||||
position: absolute
|
|
||||||
top: 50%
|
|
||||||
transform(translateY(-50%))
|
|
||||||
width: 10px
|
|
||||||
@ -41,10 +41,4 @@ Package.onUse(function(api) {
|
|||||||
'components/roundedButtonGroup/roundedButtonGroup.js'
|
'components/roundedButtonGroup/roundedButtonGroup.js'
|
||||||
], 'client');
|
], 'client');
|
||||||
|
|
||||||
// Radio Option
|
|
||||||
api.addFiles([
|
|
||||||
'components/radioOption/radioOption.html',
|
|
||||||
'components/radioOption/radioOption.styl'
|
|
||||||
], 'client');
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
|
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
|
||||||
|
|
||||||
SimpleSchema.extendOptions({
|
|
||||||
allowedSelect2Values: Match.Optional(Array),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const schema = new SimpleSchema({
|
export const schema = new SimpleSchema({
|
||||||
measurableDisease: {
|
measurableDisease: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -16,7 +12,7 @@ export const schema = new SimpleSchema({
|
|||||||
regionsOfMetastaticDisease: {
|
regionsOfMetastaticDisease: {
|
||||||
type: [String],
|
type: [String],
|
||||||
label: 'Regions of Metastatic Disease',
|
label: 'Regions of Metastatic Disease',
|
||||||
allowedSelect2Values: ['None', 'Lymph Node'], // allowedValues doesn't seem show up in the Schema since this is an Array. There may be a better way to do this
|
allowedValues: ['None', 'Lymph Node'],
|
||||||
defaultValue: ['None'],
|
defaultValue: ['None'],
|
||||||
optional: true
|
optional: true
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,19 +1,23 @@
|
|||||||
<template name="additionalFindings">
|
<template name="additionalFindings">
|
||||||
<div class="additionalFindings">
|
<div class="additionalFindings">
|
||||||
<div class="scrollArea">
|
<div class="scrollArea">
|
||||||
{{#form schema=currentSchema}}
|
{{#form schema=instance.currentSchema}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="header">Additional Findings: Baseline</div>
|
<div class="header">Additional Findings: Baseline</div>
|
||||||
{{>groupRadio labelClass='form-group' key='measurableDisease'}}
|
{{>groupRadio labelClass='form-group' key='measurableDisease'}}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="header">Supplementary Measurements</div>
|
<div class="header">Supplementary Measurements</div>
|
||||||
{{>selectInput (stateDataWithKey "numberOfBoneLesions")}}
|
{{>inputSelect class="bone-lesions" labelClass='form-group' key='numberOfBoneLesions' hideSearch=true}}
|
||||||
{{>select2Input
|
{{>inputSelect
|
||||||
key="regionsOfMetastaticDisease"
|
class="disease-regions"
|
||||||
select2Options=regionsOfMetastaticDiseaseSelect2Options
|
labelClass='form-group'
|
||||||
state=state
|
key='regionsOfMetastaticDisease'
|
||||||
currentSchema=currentSchema
|
multiple=true
|
||||||
|
options=(clone
|
||||||
|
placeholder='Search Regions'
|
||||||
|
allowClear=true
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
{{>groupRadio labelClass='form-group' key='tracerRelatedToMetastaticDisease'}}
|
{{>groupRadio labelClass='form-group' key='tracerRelatedToMetastaticDisease'}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,79 +2,58 @@ import { Template } from 'meteor/templating';
|
|||||||
import { ReactiveDict } from 'meteor/reactive-dict';
|
import { ReactiveDict } from 'meteor/reactive-dict';
|
||||||
import { schema as AdditionalFindingsSchema } from 'meteor/lesiontracker/both/schema/additionalFindings';
|
import { schema as AdditionalFindingsSchema } from 'meteor/lesiontracker/both/schema/additionalFindings';
|
||||||
|
|
||||||
Template.additionalFindings.onCreated(function additionalFindingsOnCreated() {
|
Template.additionalFindings.onCreated(() => {
|
||||||
console.log('additionalFindingsOnCreated');
|
|
||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
|
|
||||||
instance.currentSchema = AdditionalFindingsSchema;
|
instance.currentSchema = AdditionalFindingsSchema;
|
||||||
instance.state = new ReactiveDict();
|
});
|
||||||
|
|
||||||
if (!instance.data.currentTimepointId) {
|
Template.additionalFindings.onRendered(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
// Get the form component
|
||||||
|
const form = instance.$('form:first').data('component');
|
||||||
|
|
||||||
|
const currentTimepointId = instance.data.currentTimepointId;
|
||||||
|
if (!currentTimepointId) {
|
||||||
console.warn('Case has no timepointId');
|
console.warn('Case has no timepointId');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTimepointId = instance.data.currentTimepointId;
|
|
||||||
if (!currentTimepointId) {
|
|
||||||
console.warn('No currentTimepointId');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(AdditionalFindings.find().fetch());
|
|
||||||
const additionalFindings = AdditionalFindings.findOne({
|
const additionalFindings = AdditionalFindings.findOne({
|
||||||
timepointId: currentTimepointId
|
timepointId: currentTimepointId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (additionalFindings) {
|
if (additionalFindings) {
|
||||||
instance.id = additionalFindings._id;
|
instance.id = additionalFindings._id;
|
||||||
|
form.value(additionalFindings);
|
||||||
// Don't store the MongoDB Id in the ReactiveDict
|
|
||||||
delete additionalFindings._id;
|
|
||||||
|
|
||||||
instance.state.set(additionalFindings);
|
|
||||||
} else {
|
} else {
|
||||||
const defaultData = instance.currentSchema.clean({});
|
const defaultData = instance.currentSchema.clean({});
|
||||||
instance.state.set(defaultData);
|
form.value(defaultData);
|
||||||
|
|
||||||
// Include patientId and timepointId
|
// Include patientId and timepointId
|
||||||
defaultData.patientId = Session.get('patientId');
|
defaultData.patientId = Session.get('patientId');
|
||||||
defaultData.timepointId = currentTimepointId;
|
defaultData.timepointId = currentTimepointId;
|
||||||
|
|
||||||
// TODO: Turn this into a Meteor Call to insert it on the server
|
// TODO: [design] Turn this into a Meteor Call to insert it on the server
|
||||||
instance.id = AdditionalFindings.insert(defaultData);
|
instance.id = AdditionalFindings.insert(defaultData);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Template.additionalFindings.onRendered(function additionalFindingsOnRendered() {
|
instance.autorun(computation => {
|
||||||
const instance = Template.instance();
|
// Run this computation everytime the form data is changed
|
||||||
|
form.depend();
|
||||||
|
|
||||||
instance.autorun(() => {
|
// Stop here if it's the computation's first run
|
||||||
console.log('Updating AdditionalFindings Collection');
|
if (computation.firstRun) {
|
||||||
console.log(instance.state.all());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Turn this into a Meteor Call to update it on the server
|
// Get the form data for AdditionalFindings
|
||||||
let update = instance.state.all();
|
let formData = form.value();
|
||||||
|
|
||||||
|
// TODO: [design] Turn this into a Meteor Call to update it on the server
|
||||||
AdditionalFindings.update(instance.id, {
|
AdditionalFindings.update(instance.id, {
|
||||||
$set: update
|
$set: formData
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.additionalFindings.helpers({
|
|
||||||
// We need these helper methods
|
|
||||||
// since we assign them into instance object instead of instance.data
|
|
||||||
currentSchema() {
|
|
||||||
return Template.instance().currentSchema;
|
|
||||||
},
|
|
||||||
|
|
||||||
state() {
|
|
||||||
return Template.instance().state;
|
|
||||||
},
|
|
||||||
|
|
||||||
regionsOfMetastaticDiseaseSelect2Options() {
|
|
||||||
return {
|
|
||||||
placeholder: 'Search Regions',
|
|
||||||
allowClear: true,
|
|
||||||
multiple: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
@ -32,8 +32,9 @@
|
|||||||
background-color: $uiGray
|
background-color: $uiGray
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
padding: 7px 11px
|
|
||||||
color: $textPrimaryColor
|
color: $textPrimaryColor
|
||||||
|
padding: 7px 11px
|
||||||
|
width: 100%
|
||||||
|
|
||||||
&:not(:last-child)
|
&:not(:last-child)
|
||||||
border-bottom: $uiBorderThickness solid $uiBorderColorDark
|
border-bottom: $uiBorderThickness solid $uiBorderColorDark
|
||||||
@ -44,19 +45,13 @@
|
|||||||
line-height: 15px
|
line-height: 15px
|
||||||
margin: 10px 0 8px
|
margin: 10px 0 8px
|
||||||
|
|
||||||
// TODO: remove
|
|
||||||
h5
|
|
||||||
font-weight: bold
|
|
||||||
margin-bottom: 8px
|
|
||||||
|
|
||||||
.control-label
|
.control-label
|
||||||
font-size: 14px
|
font-size: 14px
|
||||||
font-weight: 100
|
font-weight: 100
|
||||||
display: inline-block
|
display: inline-block
|
||||||
padding: 5px
|
padding: 5px
|
||||||
|
|
||||||
// TODO: remove radio-group
|
.group-radio
|
||||||
.radio-group, .group-radio
|
|
||||||
height: 30px
|
height: 30px
|
||||||
|
|
||||||
label
|
label
|
||||||
@ -68,13 +63,11 @@
|
|||||||
line-height: 30px
|
line-height: 30px
|
||||||
width: 50%
|
width: 50%
|
||||||
|
|
||||||
#regionsOfMetastaticDisease
|
.disease-regions + .select2
|
||||||
width: 285px
|
width: 100% !important
|
||||||
height: 30px
|
|
||||||
border-radius: 2px
|
.bone-lesions + .select2
|
||||||
background-color: #b6b6b6
|
width: auto !important
|
||||||
color: #3e3e3e
|
|
||||||
font-size: 14px
|
|
||||||
|
|
||||||
.buttonSection
|
.buttonSection
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
|
|||||||
@ -28,9 +28,9 @@ Package.onUse(function(api) {
|
|||||||
api.use('aldeed:template-extension@4.0.0');
|
api.use('aldeed:template-extension@4.0.0');
|
||||||
|
|
||||||
// Our custom packages
|
// Our custom packages
|
||||||
|
api.use('ohif:core');
|
||||||
api.use('worklist');
|
api.use('worklist');
|
||||||
api.use('cornerstone');
|
api.use('cornerstone');
|
||||||
api.use('reactive-form-controls');
|
|
||||||
|
|
||||||
api.addFiles('log.js', [ 'client', 'server' ]);
|
api.addFiles('log.js', [ 'client', 'server' ]);
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Tracker } from "meteor/tracker";
|
||||||
import { _ } from 'meteor/underscore';
|
import { _ } from 'meteor/underscore';
|
||||||
import { $ } from 'meteor/jquery';
|
import { $ } from 'meteor/jquery';
|
||||||
|
|
||||||
@ -15,6 +16,9 @@ OHIF.mixins.formItem = new OHIF.Mixin({
|
|||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
|
// Create a observer to monitor changed values
|
||||||
|
component.changeObserver = new Tracker.Dependency();
|
||||||
|
|
||||||
// Register the component in the parent component
|
// Register the component in the parent component
|
||||||
component.registerSelf();
|
component.registerSelf();
|
||||||
|
|
||||||
@ -106,6 +110,10 @@ OHIF.mixins.formItem = new OHIF.Mixin({
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
component.depend = () => {
|
||||||
|
return component.changeObserver.depend();
|
||||||
|
};
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onRendered() {
|
onRendered() {
|
||||||
@ -115,9 +123,6 @@ OHIF.mixins.formItem = new OHIF.Mixin({
|
|||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$(':input').first();
|
component.$element = instance.$(':input').first();
|
||||||
|
|
||||||
// Set the element to be styled
|
|
||||||
component.$style = component.$element;
|
|
||||||
|
|
||||||
// Set the most outer wrapper element
|
// Set the most outer wrapper element
|
||||||
component.$wrapper = instance.wrapper.$('*').first();
|
component.$wrapper = instance.wrapper.$('*').first();
|
||||||
},
|
},
|
||||||
@ -133,12 +138,22 @@ OHIF.mixins.formItem = new OHIF.Mixin({
|
|||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
|
// If no style element was defined, set it as the element itself
|
||||||
|
if (!component.$style.length) {
|
||||||
|
component.$style = component.$element;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the component in jQuery data after all mixins are rendered
|
// Set the component in jQuery data after all mixins are rendered
|
||||||
component.$element.data('component', component);
|
component.$element.data('component', component);
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
|
||||||
|
// Enable reactivity by changing a Tracker.Dependency observer
|
||||||
|
change(event, instance) {
|
||||||
|
instance.component.changeObserver.changed();
|
||||||
|
},
|
||||||
|
|
||||||
// TODO: [design] remove log, show error box/hint over the wrapper
|
// TODO: [design] remove log, show error box/hint over the wrapper
|
||||||
errorin(event, instance) {
|
errorin(event, instance) {
|
||||||
console.log('ERROR when validating component', instance.component);
|
console.log('ERROR when validating component', instance.component);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
import { _ } from 'meteor/underscore';
|
import { _ } from 'meteor/underscore';
|
||||||
|
import { $ } from 'meteor/jquery';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* groupRadio: controls all the radio inputs inside the group
|
* groupRadio: controls all the radio inputs inside the group
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { Blaze } from 'meteor/blaze';
|
import { Blaze } from 'meteor/blaze';
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { ReactiveVar } from 'meteor/reactive-var';
|
||||||
import { _ } from 'meteor/underscore';
|
import { _ } from 'meteor/underscore';
|
||||||
|
|
||||||
// Helper function to get the component's current schema
|
// Helper function to get the component's current schema
|
||||||
@ -14,7 +15,12 @@ const getCurrentSchema = (parentComponent, key) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the current schema data using component's key
|
// Get the current schema data using component's key
|
||||||
const currentSchema = schema._schema[key];
|
const currentSchema = _.clone(schema._schema[key]);
|
||||||
|
|
||||||
|
// Merge the sub-schema properties if it's an array
|
||||||
|
if (Array.isArray(currentSchema.type())) {
|
||||||
|
_.extend(currentSchema, schema._schema[key + '.$']);
|
||||||
|
}
|
||||||
|
|
||||||
// Return the component's schema definitions
|
// Return the component's schema definitions
|
||||||
return currentSchema;
|
return currentSchema;
|
||||||
@ -50,7 +56,7 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
|
|||||||
// Fill the items if it's an array schema
|
// Fill the items if it's an array schema
|
||||||
if (!data.items && Array.isArray(currentSchema.allowedValues)) {
|
if (!data.items && Array.isArray(currentSchema.allowedValues)) {
|
||||||
// Initialize the items array
|
// Initialize the items array
|
||||||
data.items = [];
|
const items = [];
|
||||||
|
|
||||||
// Get the values and labels arrays from schema
|
// Get the values and labels arrays from schema
|
||||||
const values = currentSchema.allowedValues;
|
const values = currentSchema.allowedValues;
|
||||||
@ -59,11 +65,14 @@ OHIF.mixins.schemaData = new OHIF.Mixin({
|
|||||||
// Iterate the allowed values array
|
// Iterate the allowed values array
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
// Push the current item to the items array
|
// Push the current item to the items array
|
||||||
data.items.push({
|
items.push({
|
||||||
value: values[i],
|
value: values[i],
|
||||||
label: labels[i] || values[i]
|
label: labels[i] || values[i]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the items to a reactive instance
|
||||||
|
data.items = new ReactiveVar(items);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { Template } from 'meteor/templating';
|
import { Template } from 'meteor/templating';
|
||||||
|
import { $ } from 'meteor/jquery';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* input: controls a select2 component
|
* input: controls a select2 component
|
||||||
@ -11,8 +12,27 @@ OHIF.mixins.select2 = new OHIF.Mixin({
|
|||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Apply the select2 to the component
|
||||||
component.$element = instance.$('select:first');
|
component.$element.select2(instance.data.options);
|
||||||
|
|
||||||
|
// Store the select2 instance to allow its further destruction
|
||||||
|
component.select2Instance = component.$element.data('select2');
|
||||||
|
},
|
||||||
|
|
||||||
|
onDestroyed() {
|
||||||
|
const instance = Template.instance();
|
||||||
|
const component = instance.component;
|
||||||
|
|
||||||
|
// Destroy the select2 instance to remove unwanted DOM elements
|
||||||
|
component.select2Instance.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
events: {
|
||||||
|
'focus .select2-hidden-accessible'(event, instance) {
|
||||||
|
// Redirect the focus to select2 focus control in case of hidden
|
||||||
|
// accessible being focused (e.g. clicking on outer label)
|
||||||
|
$(event.currentTarget).nextAll('.select2:first').find('.select2-selection').focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
id="{{this.id}}"
|
id="{{this.id}}"
|
||||||
class="{{this.class}}"
|
class="{{this.class}}"
|
||||||
name="{{this.name}}"
|
name="{{this.name}}"
|
||||||
|
multiple="{{#if this.multiple}}multiple{{/if}}"
|
||||||
{{this.tagAttributes}}
|
{{this.tagAttributes}}
|
||||||
>
|
>
|
||||||
{{>UI.contentBlock}}
|
{{>UI.contentBlock}}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
type='radio'
|
type='radio'
|
||||||
mixins=(concat 'input ' this.mixins)
|
mixins=(concat 'input ' this.mixins)
|
||||||
labelAfter=(valueIf (isDefined this.labelAfter) this.labelAfter true)
|
labelAfter=(valueIf (isDefined this.labelAfter) this.labelAfter true)
|
||||||
labelClass=(concat 'checkboxLabel' this.labelClass)
|
labelClass=(concat 'checkboxLabel ' this.labelClass)
|
||||||
wrappers=(concat ''
|
wrappers=(concat ''
|
||||||
(valueIf reactive this.label 'wrapperLabel ' '')
|
(valueIf reactive this.label 'wrapperLabel ' '')
|
||||||
this.wrappers
|
this.wrappers
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
<template name="inputSelect">
|
<template name="inputSelect">
|
||||||
{{#baseComponent (extend this
|
{{#baseComponent (extend this
|
||||||
base='baseSelect'
|
base='baseSelect'
|
||||||
class=(concat 'form-control ' this.class)
|
mixins=(concat 'select2 ' this.mixins)
|
||||||
mixins=(concat 'select ' this.mixins)
|
|
||||||
wrappers=(concat ''
|
wrappers=(concat ''
|
||||||
(valueIf reactive this.label 'wrapperLabel ' '')
|
(valueIf reactive this.label 'wrapperLabel ' '')
|
||||||
this.wrappers
|
this.wrappers
|
||||||
)
|
)
|
||||||
|
options=(clone this.options
|
||||||
|
multiple=(valueIf this.multiple true false)
|
||||||
|
minimumResultsForSearch=(valueIf this.hideSearch
|
||||||
|
-1 this.minimumResultsForSearch
|
||||||
|
)
|
||||||
|
)
|
||||||
)}}
|
)}}
|
||||||
{{>UI.contentBlock}}
|
{{>UI.contentBlock}}
|
||||||
{{/baseComponent}}
|
{{/baseComponent}}
|
||||||
|
|||||||
@ -46,14 +46,15 @@ Template.registerHelper('clone', (...argsArray) => {
|
|||||||
return extend(...newArgs);
|
return extend(...newArgs);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the first thrut value in the given arguments
|
// Choose the first truthy value in the given values
|
||||||
Template.registerHelper('choose', (...argsArray) => {
|
Template.registerHelper('choose', (...values) => {
|
||||||
// Iterate over the given objects
|
let result;
|
||||||
for (let i = 0; i < argsArray.length; i++) {
|
_.each(_.initial(values, 1), value => {
|
||||||
// Check if the current value is truth
|
if (result) {
|
||||||
if (!!argsArray[i]) {
|
return;
|
||||||
// Return the current value
|
|
||||||
return argsArray[i];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
result = value;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -59,19 +59,6 @@ Template.registerHelper('or', (...values) => {
|
|||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Choose the first truthy value in the given values
|
|
||||||
Template.registerHelper('choose', (...values) => {
|
|
||||||
let result;
|
|
||||||
_.each(_.initial(values, 1), value => {
|
|
||||||
if (result) {
|
|
||||||
return;
|
|
||||||
} else if (value) {
|
|
||||||
result = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the second parameter if the first is true or the third if it's false
|
// Return the second parameter if the first is true or the third if it's false
|
||||||
Template.registerHelper('valueIf', (condition, valueIfTrue, valueIfFalse) => {
|
Template.registerHelper('valueIf', (condition, valueIfTrue, valueIfFalse) => {
|
||||||
if (condition) {
|
if (condition) {
|
||||||
|
|||||||
@ -10,6 +10,11 @@ Package.onUse(function(api) {
|
|||||||
api.use('ecmascript');
|
api.use('ecmascript');
|
||||||
api.use('standard-app-packages');
|
api.use('standard-app-packages');
|
||||||
api.use('jquery');
|
api.use('jquery');
|
||||||
|
api.use('underscore');
|
||||||
|
api.use('templating');
|
||||||
|
api.use('reactive-var');
|
||||||
|
|
||||||
|
api.use('natestrauser:select2@4.0.1', 'client');
|
||||||
|
|
||||||
api.mainModule('main.js', 'client');
|
api.mainModule('main.js', 'client');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
<template name="helpBlock">
|
|
||||||
{{#if isInvalidKey key}}
|
|
||||||
<span class="help-block m-b-none">
|
|
||||||
{{invalidKeyMessage key}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
import './helpBlock.html';
|
|
||||||
|
|
||||||
Template.helpBlock.onCreated(function helpBlockOnCreated() {
|
|
||||||
var instance = this;
|
|
||||||
|
|
||||||
// Invalid keys comes from Trials schema
|
|
||||||
instance.invalidKeys = this.data.invalidKeys;
|
|
||||||
});
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<template name="radioOptionGroup">
|
|
||||||
<div class="{{class}} form-group">
|
|
||||||
{{#let schema=(getSchema key)}}
|
|
||||||
<h5>{{schema.label}}</h5>
|
|
||||||
<div class="radio-group">
|
|
||||||
{{#each option in schema.allowedValues}}
|
|
||||||
{{>radioOption
|
|
||||||
name=key
|
|
||||||
value=option
|
|
||||||
checked=(eq value option)
|
|
||||||
label=option
|
|
||||||
class="js-form-update"
|
|
||||||
}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/let}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
import './radioOptionGroup.html';
|
|
||||||
|
|
||||||
Template.radioOptionGroup.onCreated(function radioOptionGroupOnCreated() {
|
|
||||||
let instance = this;
|
|
||||||
|
|
||||||
instance.currentSchema = this.data.currentSchema;
|
|
||||||
|
|
||||||
// Here we set this Template instance's state property to equal the input state data
|
|
||||||
// The purpose of this is to make our helpers and events more consistent across templates.
|
|
||||||
instance.state = this.data.state;
|
|
||||||
|
|
||||||
// Invalid keys comes from Trials schema
|
|
||||||
instance.invalidKeys = this.data.invalidKeys;
|
|
||||||
|
|
||||||
// Validation Context of registrationWorkflow
|
|
||||||
instance.validationContext = this.data.validationContext;
|
|
||||||
|
|
||||||
// isModified records whether or not the user has unsaved changes
|
|
||||||
instance.isModified = this.data.isModified;
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.radioOptionGroup.events({
|
|
||||||
'change .js-form-update'(event, instance) {
|
|
||||||
const value = event.currentTarget.value;
|
|
||||||
const key = instance.data.key;
|
|
||||||
if (event.currentTarget.checked) {
|
|
||||||
instance.state.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.radioOptionGroup.helpers({
|
|
||||||
value() {
|
|
||||||
const instance = Template.instance();
|
|
||||||
const key = instance.data.key;
|
|
||||||
return instance.state.get(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
<template name="select2Input">
|
|
||||||
<div class="form-group {{#if isInvalidKey key}}has-error{{/if}}">
|
|
||||||
<label class="control-label">
|
|
||||||
{{#with getSchema key }}
|
|
||||||
{{label}}
|
|
||||||
{{/with }}
|
|
||||||
</label>
|
|
||||||
<div>
|
|
||||||
<select id="{{key}}"
|
|
||||||
name="{{key}}"
|
|
||||||
required="{{isRequired}}"
|
|
||||||
class="form-control js-form-update">
|
|
||||||
{{#each option in options}}
|
|
||||||
<option value="{{option.id}}">
|
|
||||||
{{option.text}}
|
|
||||||
</option>
|
|
||||||
{{/each}}
|
|
||||||
</select>
|
|
||||||
{{> helpBlock}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
import './select2Input.html';
|
|
||||||
|
|
||||||
Template.select2Input.onCreated(function selectInputOnCreated() {
|
|
||||||
let instance = this;
|
|
||||||
|
|
||||||
instance.currentSchema = this.data.currentSchema;
|
|
||||||
|
|
||||||
// Here we set this Template instance's state property to equal the input state data
|
|
||||||
// The purpose of this is to make our helpers and events more consistent across templates.
|
|
||||||
instance.state = this.data.state;
|
|
||||||
|
|
||||||
// Invalid keys comes from Trials schema
|
|
||||||
instance.invalidKeys = this.data.invalidKeys;
|
|
||||||
|
|
||||||
// Validation Context of registrationWorkflow
|
|
||||||
instance.validationContext = this.data.validationContext;
|
|
||||||
|
|
||||||
// isModified records whether or not the user has unsaved changes
|
|
||||||
instance.isModified = this.data.isModified;
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.select2Input.onRendered(function select2InputOnRendered() {
|
|
||||||
const instance = this;
|
|
||||||
|
|
||||||
Meteor.defer(function() {
|
|
||||||
// Initialize select2 box
|
|
||||||
const selectBox = instance.$('select');
|
|
||||||
selectBox.select2(instance.data.select2Options);
|
|
||||||
|
|
||||||
// Set selected value(s) from current state;
|
|
||||||
const key = instance.data.key;
|
|
||||||
const value = instance.state.get(key);
|
|
||||||
if (value) {
|
|
||||||
selectBox.val(value).trigger('change');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.select2Input.helpers({
|
|
||||||
options() {
|
|
||||||
const instance = Template.instance();
|
|
||||||
if (instance.data.options) {
|
|
||||||
return instance.data.options;
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = instance.data.key;
|
|
||||||
const schema = instance.currentSchema.schema(key);
|
|
||||||
const allowedValues = schema.allowedSelect2Values;
|
|
||||||
return allowedValues.map((value) => {
|
|
||||||
return {
|
|
||||||
id: value,
|
|
||||||
text: value
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.select2Input.events({
|
|
||||||
'focus .select2'(event, instance) {
|
|
||||||
var control = event.currentTarget;
|
|
||||||
|
|
||||||
// Find select element that is assigned to select2
|
|
||||||
var hiddenSelect2Box = $(control).prev('select');
|
|
||||||
|
|
||||||
// Prevent auto open for select2 boxes that has multiple attribute
|
|
||||||
if (!hiddenSelect2Box || $(hiddenSelect2Box).prop('multiple')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(hiddenSelect2Box).select2('open');
|
|
||||||
},
|
|
||||||
|
|
||||||
'change .js-form-update'(event, instance) {
|
|
||||||
const value = $(event.currentTarget).val();
|
|
||||||
const key = instance.data.key;
|
|
||||||
instance.state.set(key, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
<template name="selectInput">
|
|
||||||
<div class="form-group {{#if isInvalidKey key}}has-error{{/if}}">
|
|
||||||
{{ #with getSchema key }}
|
|
||||||
<label class="control-label">
|
|
||||||
{{label}}
|
|
||||||
</label>
|
|
||||||
<div>
|
|
||||||
<select id="{{../key}}"
|
|
||||||
name="{{../key}}"
|
|
||||||
required="{{isRequired}}"
|
|
||||||
class="form-control js-form-update">
|
|
||||||
{{#each option in allowedValues}}
|
|
||||||
<option value="{{option}}"
|
|
||||||
selected="{{#if eq selectedOptionValue option}}selected{{/if}}">
|
|
||||||
{{option}}
|
|
||||||
</option>
|
|
||||||
{{/each}}
|
|
||||||
</select>
|
|
||||||
{{> helpBlock }}
|
|
||||||
</div>
|
|
||||||
{{ /with }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
import './selectInput.html';
|
|
||||||
|
|
||||||
Template.selectInput.onCreated(function selectInputOnCreated() {
|
|
||||||
let instance = this;
|
|
||||||
|
|
||||||
instance.currentSchema = this.data.currentSchema;
|
|
||||||
|
|
||||||
// Here we set this Template instance's state property to equal the input state data
|
|
||||||
// The purpose of this is to make our helpers and events more consistent across templates.
|
|
||||||
instance.state = this.data.state;
|
|
||||||
|
|
||||||
// Invalid keys comes from Trials schema
|
|
||||||
instance.invalidKeys = this.data.invalidKeys;
|
|
||||||
|
|
||||||
// Validation Context of registrationWorkflow
|
|
||||||
instance.validationContext = this.data.validationContext;
|
|
||||||
|
|
||||||
// isModified records whether or not the user has unsaved changes
|
|
||||||
instance.isModified = this.data.isModified;
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.selectInput.helpers({
|
|
||||||
selectedOptionValue() {
|
|
||||||
const instance = Template.instance();
|
|
||||||
var key = instance.data.key;
|
|
||||||
return instance.state.get(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.selectInput.events({
|
|
||||||
'change .js-form-update'(event, instance) {
|
|
||||||
const value = $(event.currentTarget).val();
|
|
||||||
const key = instance.data.key;
|
|
||||||
instance.state.set(key, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
Template.registerHelper('getSchema', function(context) {
|
|
||||||
const instance = Template.instance();
|
|
||||||
if (!instance.currentSchema) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance.currentSchema.schema(context);
|
|
||||||
});
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A global Blaze UI helper function to return whether or not a key is part of the invalidKeys dictionary
|
|
||||||
*/
|
|
||||||
Template.registerHelper('isInvalidKey', function(context, format, options) {
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const instance = Template.instance();
|
|
||||||
if (!instance.invalidKeys) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error message if key is at first index
|
|
||||||
var firstInvalidKey = instance.invalidKeys.get(0);
|
|
||||||
return (firstInvalidKey && firstInvalidKey.name === context)
|
|
||||||
});
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { Template } from 'meteor/templating';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A global Blaze UI helper function to return whether or not a
|
|
||||||
* field is required based on the Template's specified currentSchema
|
|
||||||
*/
|
|
||||||
Template.registerHelper('stateDataWithKey', function(context) {
|
|
||||||
const instance = Template.instance();
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
return {
|
|
||||||
state: instance.state,
|
|
||||||
invalidKeys: instance.invalidKeys,
|
|
||||||
validationContext: instance.validationContext,
|
|
||||||
currentSchema: instance.currentSchema,
|
|
||||||
isModified: instance.isModified
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
key: context,
|
|
||||||
state: instance.state,
|
|
||||||
invalidKeys: instance.invalidKeys,
|
|
||||||
validationContext: instance.validationContext,
|
|
||||||
currentSchema: instance.currentSchema,
|
|
||||||
isModified: instance.isModified
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
Package.describe({
|
|
||||||
name: 'reactive-form-controls',
|
|
||||||
summary: 'A set of basic form controls that store their state in a ReactiveDict',
|
|
||||||
version: '0.0.1'
|
|
||||||
});
|
|
||||||
|
|
||||||
Package.onUse(function(api) {
|
|
||||||
api.versionsFrom('1.3.4.1');
|
|
||||||
|
|
||||||
api.use('standard-app-packages');
|
|
||||||
api.use('ecmascript');
|
|
||||||
api.use('jquery');
|
|
||||||
api.use('stylus');
|
|
||||||
api.use('reactive-dict');
|
|
||||||
api.use('templating');
|
|
||||||
api.use('natestrauser:select2@4.0.1', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/helpers/getSchema.js', ['client']);
|
|
||||||
api.addFiles('client/helpers/isInvalidKey.js', ['client']);
|
|
||||||
api.addFiles('client/helpers/stateDataWithKey.js', ['client']);
|
|
||||||
|
|
||||||
api.addFiles('client/components/helpBlock/helpBlock.html', ['client']);
|
|
||||||
api.addFiles('client/components/helpBlock/helpBlock.js', ['client']);
|
|
||||||
|
|
||||||
api.addFiles('client/components/radioOptionGroup/radioOptionGroup.html', ['client']);
|
|
||||||
api.addFiles('client/components/radioOptionGroup/radioOptionGroup.js', ['client']);
|
|
||||||
|
|
||||||
api.addFiles('client/components/selectInput/selectInput.html', ['client']);
|
|
||||||
api.addFiles('client/components/selectInput/selectInput.js', ['client']);
|
|
||||||
|
|
||||||
api.addFiles('client/components/select2Input/select2Input.html', ['client']);
|
|
||||||
api.addFiles('client/components/select2Input/select2Input.js', ['client']);
|
|
||||||
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue
Block a user