LT-67: Styling select2 error state and adding its error tooltip

This commit is contained in:
Bruno Alves de Faria 2016-09-01 13:33:17 -03:00
parent 620f5fd4ff
commit c05e36eca8
9 changed files with 106 additions and 36 deletions

View File

@ -22,3 +22,8 @@
theme('background-color', '$uiStateError')
theme('border-color', '$uiStateErrorBorder')
theme('color', '$uiStateErrorText')
.select2-selection
theme('background-color', '$uiStateError')
theme('border-color', '$uiStateErrorBorder')
theme('color', '$uiStateErrorText')

View File

@ -15,7 +15,7 @@
key='regionsOfMetastaticDisease'
multiple=true
options=(clone
placeholder='Choose a region...'
placeholder='Choose a region'
allowClear=true
)
}}

View File

@ -11,7 +11,7 @@
</div>
<div class="col-lg-6">
{{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
options=(clone placeholder='Select an option...')}}
options=(clone placeholder='Select an option')}}
</div>
<div class="col-lg-6">
{{>inputText labelClass='form-group' key='qidoRoot'}}

View File

@ -8,7 +8,7 @@
<div class="col-lg-6">
{{>inputSelect labelClass='form-group' key='type'
hideSearch=true disabled=(eq this.mode.get 'edit')
options=(clone placeholder='Choose a server type...')}}
options=(clone placeholder='Choose a server type')}}
</div>
</div>
<hr>

View File

@ -27,6 +27,18 @@ OHIF.mixins.form = new OHIF.Mixin({
// Enable reactivity by changing a Tracker.Dependency observer
component.validationObserver.changed();
}, 200);
// Change the validation function to focus the fields with error
const validateSelf = component.validate;
component.validate = () => {
// Call the original validation function
validateSelf();
// Focus the first error field if some validation failed
if (component.schema && component.schema._invalidKeys.length) {
instance.$('.state-error :input:first').focus();
}
};
},
onRendered() {
@ -35,16 +47,6 @@ OHIF.mixins.form = new OHIF.Mixin({
// Set the component main and style elements
component.$style = component.$element = instance.$('form:first');
instance.autorun(() => {
// Run this computation everytime the validation is triggered
component.validationObserver.depend();
// Focus the first error field if some validation failed
if (component.schema && component.schema._invalidKeys.length) {
instance.$('.state-error :input:first').focus();
}
});
},
events: {

View File

@ -1,6 +1,7 @@
import { OHIF } from 'meteor/ohif:core';
import { Template } from 'meteor/templating';
import { Tracker } from 'meteor/tracker';
import { Meteor } from 'meteor/meteor';
import { _ } from 'meteor/underscore';
import { $ } from 'meteor/jquery';
@ -53,6 +54,19 @@ OHIF.mixins.formItem = new OHIF.Mixin({
component.$wrapper[method]();
};
// Check if the focus is inside this element
component.hasFocus = () => {
// Get the focused element
const focused = $(':focus')[0];
// Check if the focused element is inside the component
const contains = $.contains(component.$wrapper[0], focused);
const isEqual = component.$wrapper[0] === focused;
// Return true if he component has the focus
return contains || isEqual;
};
// Add or remove a state from the component
component.state = (state, flag) => {
component.$wrapper.toggleClass(`state-${state}`, !!flag);
@ -71,19 +85,50 @@ OHIF.mixins.formItem = new OHIF.Mixin({
}
};
// Toggle the state over the component
// Toggle the tooltip over the component
component.toggleTooltip = (isShow, message) => {
if (isShow) {
console.warn('>>>>message', message);
if (isShow && message) {
// Stop here if the tooltip is already created
if (component.$wrapper.next('.tooltip').length) {
return;
}
// Create the tooltip
component.$wrapper.tooltip({
trigger: 'manual',
title: message
}).tooltip('show');
} else {
// Destroy the tooltip
component.$wrapper.tooltip('destroy');
}
};
// Toggle a state message as a tooltip over the component
component.toggleMessage = isShow => {
// Check if the action is to hide
if (!isShow) {
Meteor.setTimeout(() => {
// Check if the component has the focus
if (component.hasFocus()) {
// Prevent the tooltip from being hidden
return;
}
// Hide the tooltip
component.toggleTooltip(false);
}, 100);
return;
}
// Check for error state and message
const errorMessage = component.$wrapper.attr('data-error');
if (errorMessage) {
// Show the tooltip with the error message
component.toggleTooltip(true, errorMessage);
}
},
// Search for the parent form component
component.getForm = () => {
let currentComponent = component;
@ -198,25 +243,31 @@ OHIF.mixins.formItem = new OHIF.Mixin({
focus(event, instance) {
const component = instance.component;
// Stop here if it is an group
if (component.isGroup || component.isCustomFocus) {
return;
}
// Prevent event bubbling
event.stopPropagation();
// Check for error state and message
const errorMessage = component.$wrapper.attr('data-error');
if (errorMessage) {
// Show the tooltip with the error message
component.toggleTooltip(true, errorMessage);
}
// Check for state messages and show it
component.toggleMessage(true);
},
blur(event, instance) {
const component = instance.component;
// Stop here if it is an group
if (component.isGroup || component.isCustomFocus) {
return;
}
// Prevent event bubbling
event.stopPropagation();
// Hide any tooltips
component.toggleTooltip(false);
// Hide state messages
component.toggleMessage(false);
}
}

View File

@ -12,6 +12,9 @@ OHIF.mixins.group = new OHIF.Mixin({
const instance = Template.instance();
const component = instance.component;
// Set the group identifier flag
component.isGroup = true;
// Run this computation every time the schema property is changed
instance.autorun(() => {
let schema = instance.data.schema;

View File

@ -11,6 +11,9 @@ OHIF.mixins.select2 = new OHIF.Mixin({
onCreated() {
const instance = Template.instance();
// Set the custom focus flag
instance.component.isCustomFocus = true;
// Check if this select will include a placeholder
const placeholder = instance.data.options && instance.data.options.placeholder;
if (placeholder) {
@ -54,6 +57,22 @@ OHIF.mixins.select2 = new OHIF.Mixin({
// Store the select2 instance to allow its further destruction
component.select2Instance = component.$element.data('select2');
// Get the focusable elements
const elements = [];
elements.push(component.$element[0]);
elements.push(component.$element.nextAll('.select2:first').find('.select2-selection')[0]);
// Attach focus and blur handlers to focusable elements
$(elements).on('focus', event => {
event.stopPropagation();
// Show the state message on elements focus
component.toggleMessage(true);
}).on('blur', event => {
event.stopPropagation();
// Hide the state message on elements blur
component.toggleMessage(false);
});
},
onDestroyed() {
@ -62,16 +81,6 @@ OHIF.mixins.select2 = new OHIF.Mixin({
// Destroy the select2 instance to remove unwanted DOM elements
component.select2Instance.destroy();
},
events: {
'focusin .select2-hidden-accessible'(event, instance) {
event.preventDefault();
// 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();
}
}
}
});

View File

@ -59,8 +59,8 @@ export const DICOMWebServer = new SimpleSchema({
imageRendering: {
type: String,
label: 'Image rendering',
allowedValues: ['wadouri', 'orthanc'],
valuesLabels: ['WADO URI', 'ORTHANC']
allowedValues: ['', 'wadouri', 'orthanc'],
valuesLabels: ['', 'WADO URI', 'ORTHANC']
},
qidoRoot: {
type: String,