Advanced search: WIP POC, UI/UX.

SVN:b1162[5468]
This commit is contained in:
Guillaume Lajarige
2018-03-20 13:44:10 +00:00
parent 2fb0ecc446
commit ca0232ae7b
7 changed files with 300 additions and 21 deletions

View File

@@ -55,8 +55,11 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_raw.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_string.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
$this->add_dict_entries('UI:Combo');
$this->add_ready_script(
<<< EOF

View File

@@ -703,6 +703,9 @@ input.dp-applied {
/* TODO: Remove comment before final commit */
.search_box {
box-sizing: border-box;
position: relative;
z-index: 1100;
/* To be over the table block/unblock UI. Not very sure about this. */
/* Sizing reset */
}
.search_box * {
@@ -724,7 +727,7 @@ input.dp-applied {
color: inherit;
text-decoration: none;
}
.search_form_handler input[type="text"] {
.search_form_handler input[type="text"], .search_form_handler select {
padding: 1px 2px;
}
.search_form_handler.opened .sf_title .sft_toggler {
@@ -776,6 +779,7 @@ input.dp-applied {
}
.search_form_handler .sf_criterion_area .search_form_criteria, .search_form_handler .sf_criterion_area .sf_more_criterion {
margin-bottom: 5px;
vertical-align: top;
}
.search_form_handler .sf_criterion_area .search_form_criteria.opened, .search_form_handler .sf_criterion_area .sf_more_criterion.opened {
margin-bottom: 0px;
@@ -852,12 +856,12 @@ input.dp-applied {
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_title {
padding-right: 35px;
cursor: pointer;
/* Uncomment to try fixed width criteria tabs */
/*max-width: 240px;
padding-right: 25px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;*/
/* TODO: Uncomment to try fixed width criteria tabs */
max-width: 240px;
padding-right: 25px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group {
/* Form group (operators) is displayed only when the criteria is toggled to opened state */
@@ -893,6 +897,7 @@ input.dp-applied {
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_filter {
position: relative;
margin-top: 8px;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_filter input {
width: 100%;
@@ -904,8 +909,25 @@ input.dp-applied {
opacity: 0.6;
user-select: none;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_filter .sfc_opc_mc_reset {
display: none;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_items .sfc_opc_mc_item {
margin-top: 5px;
margin: 0px -5px 0px -5px;
/* -5px to compensate padding and keep aligned to the filter left side */
padding: 5px;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_items .sfc_opc_mc_item:first-of-type {
margin-top: 8px;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_items .sfc_opc_mc_item:hover {
padding: 4px;
/* Normal padding - border size */
background-color: #fff;
border: 1px solid #3f7294;
/* TODO: Try with this */
/*background-color: $box-blue-border-color;
color: $white;*/
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices .sfc_opc_mc_items .sfc_opc_mc_item label {
display: inline-block;
@@ -970,6 +992,15 @@ input.dp-applied {
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_raw .sfc_form_group {
display: none;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_enum .sfc_fg_operator_in > label {
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_enum .sfc_fg_operator_in > label .sfc_op_content {
width: 100%;
}
.search_form_handler .sf_criterion_area .sf_more_criterion.opened {
z-index: 2;
/* To be over criterion */

View File

@@ -784,6 +784,8 @@ input.dp-applied {
/* TODO: Remove comment before final commit */
.search_box{
box-sizing: border-box;
position: relative;
z-index: 1100; /* To be over the table block/unblock UI. Not very sure about this. */
/* Sizing reset */
*{
@@ -806,7 +808,8 @@ input.dp-applied {
text-decoration: none;
}
/* Input reset */
input[type="text"]{
input[type="text"],
select{
padding: 1px 2px;
}
@@ -868,6 +871,7 @@ input.dp-applied {
.search_form_criteria,
.sf_more_criterion {
margin-bottom: 5px;
vertical-align: top;
&.opened{
margin-bottom: 0px; /* To compensate the .sfc/.sfm_header:padding-bottom: 13px */
@@ -958,12 +962,12 @@ input.dp-applied {
.sfc_title{
padding-right: 35px;
cursor: pointer;
/* Uncomment to try fixed width criteria tabs */
/*max-width: 240px;
/* TODO: Uncomment to try fixed width criteria tabs */
max-width: 240px;
padding-right: 25px;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;*/
text-overflow: ellipsis;
}
.sfc_form_group{
/* Form group (operators) is displayed only when the criteria is toggled to opened state */
@@ -1003,6 +1007,7 @@ input.dp-applied {
}
.sfc_opc_mc_filter{
position: relative;
margin-top: 8px;
input{
width: 100%;
@@ -1014,10 +1019,27 @@ input.dp-applied {
opacity: 0.6;
user-select: none;
}
.sfc_opc_mc_reset{
display: none;
}
}
.sfc_opc_mc_items{
.sfc_opc_mc_item{
margin-top: 5px;
margin: 0px -5px 0px -5px; /* -5px to compensate padding and keep aligned to the filter left side */
padding: 5px;
&:first-of-type{
margin-top: 8px;
}
&:hover{
padding: 4px; /* Normal padding - border size */
background-color: $white;
border: 1px solid $box-blue-border-color;
/* TODO: Try with this */
/*background-color: $box-blue-border-color;
color: $white;*/
}
label{
display: inline-block;
@@ -1116,6 +1138,20 @@ input.dp-applied {
display: none;
}
}
&.search_form_criteria_enum{
.sfc_fg_operator_in{
> label{
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
.sfc_op_content{
width: 100%;
}
}
}
}
}
/* More criterion */

View File

@@ -1386,7 +1386,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
// Search form
'UI:Search:Toggle' => 'Minimize / Expand',
'UI:Search:Criterion:MoreMenu:AddCriteria' => 'Add new criteria',
// - Operators
// - Criteria operators
'UI:Search:Criteria:Operator:Default:Empty' => 'Is empty',
'UI:Search:Criteria:Operator:Default:NotEmpty' => 'Is not empty',
'UI:Search:Criteria:Operator:Default:Equals' => 'Equals:',
@@ -1402,11 +1402,22 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Search:Criteria:Operator:Numeric:Between' => 'Between:',
// - Criteria titles
// - Default widget
'UI:Search:Criteria:Title:Default:Empty' => '%1$s is empty',
'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s is not empty',
'UI:Search:Criteria:Title:Default:Equals' => '%1$s equals %2$s',
// - String widget
'UI:Search:Criteria:Title:String:Contains' => '%1$s contains %2$s',
'UI:Search:Criteria:Title:String:StartsWith' => '%1$s starts with %2$s',
'UI:Search:Criteria:Title:String:EndsWith' => '%1$s ends with %2$s',
'UI:Search:Criteria:Title:Enum:In' => '%1$s in %2$s',
// - External key widget
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s is defined',
'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s is not defined',
'UI:Search:Criteria:Title:ExternalKey:Equals' => '%1$s %2$s',
// - Enum widget
'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s',
'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s: %2$s and %3$s others',
// -
'UI:Search:Value:Filter:Placeholder' => 'Filter...',
'UI:Search:Value:Toggler:CheckAllNone' => 'Check all / none',
));

View File

@@ -1212,13 +1212,24 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:Search:Criteria:Operator:String:StartsWith' => 'Commence par :',
'UI:Search:Criteria:Operator:String:EndsWith' => 'Fini par :',
// - Criteria titles
// - Default widget
'UI:Search:Criteria:Title:Default:Empty' => '%1$s vide',
'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s non vide',
'UI:Search:Criteria:Title:Default:Equals' => '%1$s égal %2$s',
// - String widget
'UI:Search:Criteria:Title:String:Contains' => '%1$s contient %2$s',
'UI:Search:Criteria:Title:String:StartsWith' => '%1$s commence par %2$s',
'UI:Search:Criteria:Title:String:EndsWith' => '%1$s fini par %2$s',
'UI:Search:Criteria:Title:Enum:In' => '%1$s parmi %2$s',
// - External key widget
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s est rempli',
'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s n\'est pas rempli',
'UI:Search:Criteria:Title:ExternalKey:Equals' => '%1$s %2$s',
// - Enum widget
'UI:Search:Criteria:Title:Enum:In' => '%1$s : %2$s',
'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s : %2$s et %3$s autres',
// -
'UI:Search:Value:Filter:Placeholder' => 'Filter...',
'UI:Search:Value:Toggler:CheckAllNone' => 'Check all / none',
));

View File

@@ -75,20 +75,20 @@ $(function()
// Hide radio & name for now, until there is more than one operator
oOpElem.find('.sfc_op_radio, .sfc_op_name').hide();
// DOM element
// DOM elements
var sOpId = oOpElem.attr('id');
var oOpContentElem = $('<div class="sfc_opc_multichoices"></div>');
// - Check / Uncheck all togglers
var sTogglerId = 'toggle_' + sOpId;
var oTogglersElem = $('<div class="sfc_opc_mc_toggler"></div>')
.append('<label for="' + sTogglerId + '"><input type="checkbox" id="' + sTogglerId + '" />' + Dict.S('TOTR: CHECK / UNCHECK ALL') + '</label>')
.append('<label for="' + sTogglerId + '"><input type="checkbox" id="' + sTogglerId + '" />' + Dict.S('UI:Search:Value:Toggler:CheckAllNone') + '</label>')
.appendTo(oOpContentElem);
// - Filter
var sFilterId = 'filter_' + sOpId;
var oFilterElem = $('<div class="sfc_opc_mc_filter"></div>')
.append('<input type="text" id="' + sFilterId + '" placeholder="TOTR: FILTER..." /><span class="sfc_opc_mcf_picto fa fa-filter"></span>')
.append('<input type="text" id="' + sFilterId + '" placeholder="' + Dict.S('UI:Search:Value:Filter:Placeholder') + '" /><span class="sfc_opc_mcf_picto sfc_opc_mcf_filter fa fa-filter"></span><span class="sfc_opc_mcf_picto sfc_opc_mcf_reset fa fa-times"></span>')
.appendTo(oOpContentElem);
// - Allowed values
@@ -124,13 +124,18 @@ $(function()
// Apply criteria
me._apply();
});
// - Filter
oFilterElem.find('input').on('keyup', function(){
oFilterElem.find('input').on('keyup focus', function(oEvent){
// TODO: Move on values with up and down arrow keys; select with space or enter.
var sFilter = $(this).val();
if(sFilter === '')
{
oOpContentElem.find('.sfc_opc_mc_item').show();
oFilterElem.find('.sfc_opc_mcf_filter').show();
oFilterElem.find('.sfc_opc_mcf_reset').hide();
}
else
{
@@ -148,8 +153,19 @@ $(function()
$(this).hide();
}
});
oFilterElem.find('.sfc_opc_mcf_filter').hide();
oFilterElem.find('.sfc_opc_mcf_reset').show();
}
});
oFilterElem.find('.sfc_opc_mcf_filter').on('click', function(){
oFilterElem.find('input').trigger('focus');
});
oFilterElem.find('.sfc_opc_mcf_reset').on('click', function(){
oFilterElem.find('input')
.val('')
.trigger('focus');
});
// - Apply on check
oAllowedValuesElem.find('.sfc_opc_mc_item').on('click', function(){
// Uncheck toggler
@@ -161,6 +177,25 @@ $(function()
oOpElem.find('.sfc_op_content').append(oOpContentElem);
},
_setTitle: function(sTitle)
{
var iValLimit = 3;
var iValCount = Object.keys(this.options.values).length;
if(iValCount > iValLimit)
{
var aFirstValues = [];
for(var i=0; i<iValLimit-1; i++)
{
aFirstValues.push(this.options.values[i].label);
}
sTitle = Dict.Format('UI:Search:Criteria:Title:Enum:In:Many', this.options.field.label, aFirstValues.join(', '), (iValCount - iValLimit+1));
}
this._super(sTitle);
},
// Operators helpers
// Reset operator's state
_resetInOperator: function(oOpElem)
{

View File

@@ -0,0 +1,152 @@
//iTop Search form criteria external key
;
$(function()
{
// the widget definition, where 'itop' is the namespace,
// 'search_form_criteria_external_key' the widget name
$.widget( 'itop.search_form_criteria_external_key', $.itop.search_form_criteria,
{
// default options
options:
{
// Overload default operator
'operator': '=',
// Available operators
'available_operators': {
},
},
// the constructor
_create: function()
{
var me = this;
this._super();
this.element.addClass('search_form_criteria_external_key');
},
// called when created, and later when changing options
_refresh: function()
{
},
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function()
{
this.element.removeClass('search_form_criteria_external_key');
this._super();
},
// _setOptions is called with a hash of all options that are changing
// always refresh when changing options
_setOptions: function()
{
this._superApply(arguments);
},
// _setOption is called for each individual option that is changing
_setOption: function( key, value )
{
this._super( key, value );
},
//------------------
// Inherited methods
//------------------
// Protected methods
// - Cinematic
// - Open / close criteria
_open: function()
{
this._super();
// Focus on right input
var oOpElemToFocus;
if(this.element.find('.sfc_form_group').hasClass('advanced'))
{
oOpElemToFocus = this.element.find('.sfc_fg_operator .sfc_op_radio:checked').closest('.sfc_fg_operator');
}
else
{
oOpElemToFocus = this.element.find('.sfc_fg_operator:first');
}
oOpElemToFocus.find('.sfc_op_content :input:first').trigger('click').trigger('focus');
},
// DOM element helpers
// TODO: Remove this function after UX tests
_prepareOperators: function()
{
var me = this;
this._super();
if(this.options.field.allowed_values.values !== undefined)
{
var sSelect = '<select>';
sSelect += '<option value="">' + Dict.S('UI:Combo:SelectValue') + '</option>';
for(var sValCode in this.options.field.allowed_values.values)
{
var sValLabel = this.options.field.allowed_values.values[sValCode];
sSelect += '<option value="' + sValCode + '">' + sValLabel + '</option>';
}
sSelect += '</select>';
this.element.find('.sfc_fg_operator[data-operator-code="equals"] .sfc_op_content').html(sSelect);
}
else
{
this.element.find('.sfc_fg_operator[data-operator-code="equals"] .sfc_op_content').html('Not implemented yet.');
}
this.element.find('.sfc_fg_operator[data-operator-code="equals"] .sfc_op_content select').on('change', function(){
me._apply();
});
},
// Operators helpers
// Reset operator's state
// TODO: Reset operator's state
// _resetEqualsOperator: function(oOpElem)
// {
// // Uncheck toggler
// oOpElem.find('sfc_opc_mc_toggler input').prop('checked', false);
//
// // Clear filter
// oOpElem.find('sfc_opc_mc_filter input').val('');
// },
// Get operator's values
_getEqualsOperatorValues: function(oOpElem)
{
var aValues = [];
var sValue = oOpElem.find('.sfc_op_content select > option:selected').val();
var sLabel = oOpElem.find('.sfc_op_content select > option:selected').text();
if(sValue !== "")
{
aValues.push({value: sValue, label: sLabel});
}
return aValues;
},
// Set operator's values
// TODO: Set operator's values
// _setInOperatorValues: function(oOpElem, aValues)
// {
// if(aValues.length === 0)
// {
// return false;
// }
//
// // Uncheck all allowed values
// oOpElem.find('.sfc_opc_mc_item input').prop('checked', false);
//
// // Re-check allowed values from param
// for(var iIdx in aValues)
// {
// oOpElem.find('.sfc_opc_mc_item[data-value-code="' + aValues[iIdx].value + '"] input').prop('checked', true);
// }
//
// return true;
// },
});
});