diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index 718880b6bf..a97d97b00b 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -55,9 +55,7 @@ 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_dict_entries('UI:Search:'); -// $this->add_dict_entries('UI:Button:'); + $this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js'); $this->add_ready_script( <<< EOF diff --git a/css/css-variables.scss b/css/css-variables.scss index f8973d7832..ba5dfddd84 100644 --- a/css/css-variables.scss +++ b/css/css-variables.scss @@ -34,4 +34,4 @@ $box-blue-border: 1px solid $box-blue-border-color; $box-blue-radius: 1px; // Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0 -$version: "v2.4.0"; \ No newline at end of file +$version: "v2.4.0"; diff --git a/css/light-grey.css b/css/light-grey.css index 1c38b07222..5bc713c019 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -675,35 +675,6 @@ input.dp-applied { float: left; } /* For search forms */ -/* TODO: Remove when cleaning up old search */ -/*.SearchDrawer { - //background: $complement-color url(../images/search-top-left-corner.png?v=#{$version}) top left no-repeat; - border-top: 5px solid $complement-color; - border-left: 5px solid $complement-color; - border-right: 5px solid $complement-color; - border-bottom: 0; - background: $complement-light; - color: #000; - padding: 10px; - margin: 0; - font-size: 12px; - } - .SearchDrawer label { - background: $complement-light; - color: #000; - text-align: right; - } - .SearchDrawer h1 { - color: #000; - } - .SearchDrawer .SearchAttribute{ - > .field_input_zone{ - display: inline-block; - > .field_select_wrapper{ - display: inline-block; - } - } - }*/ .mini_tabs a { text-decoration: none; font-weight: bold; @@ -744,6 +715,7 @@ input.dp-applied { border: 1px solid #3f7294; /* Sizing reset */ /* Hyperlink reset */ + /* Input reset */ } .search_form_handler * { box-sizing: border-box; @@ -752,6 +724,9 @@ input.dp-applied { color: inherit; text-decoration: none; } +.search_form_handler input[type="text"] { + padding: 1px 2px; +} .search_form_handler.opened .sf_title .sft_toggler { transform: rotateX(180deg); } @@ -781,6 +756,11 @@ input.dp-applied { .search_form_handler .sf_title .sft_toggler { margin-left: 0.7em; } +.search_form_handler .sf_message { + display: none; + margin: 8px 8px 0px 8px; + border-radius: 0px; +} .search_form_handler .sf_criterion_area { /*display: none;*/ padding: 8px 8px 3px 8px; @@ -872,6 +852,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;*/ } .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 */ @@ -898,6 +884,30 @@ input.dp-applied { margin: 0px; margin-right: 7px; } +.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator .sfc_opc_multichoices label > input { + vertical-align: middle; + margin-right: 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 { + position: relative; +} +.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%; +} +.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_mcf_picto { + position: absolute; + right: 5px; + top: 5px; + 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_items .sfc_opc_mc_item { + margin-top: 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 label { + display: inline-block; + width: 100%; +} .search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_equals .sfc_op_name, .search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_contains .sfc_op_name, .search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_starts_with .sfc_op_name, .search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_ends_with .sfc_op_name { width: 90px; } diff --git a/css/light-grey.scss b/css/light-grey.scss index c44e020f13..be0388a484 100644 --- a/css/light-grey.scss +++ b/css/light-grey.scss @@ -756,36 +756,6 @@ input.dp-applied { } /* For search forms */ -/* TODO: Remove when cleaning up old search */ -/*.SearchDrawer { - //background: $complement-color url(../images/search-top-left-corner.png?v=#{$version}) top left no-repeat; - border-top: 5px solid $complement-color; - border-left: 5px solid $complement-color; - border-right: 5px solid $complement-color; - border-bottom: 0; - background: $complement-light; - color: #000; - padding: 10px; - margin: 0; - font-size: 12px; -} -.SearchDrawer label { - background: $complement-light; - color: #000; - text-align: right; -} -.SearchDrawer h1 { - color: #000; -} -.SearchDrawer .SearchAttribute{ - > .field_input_zone{ - display: inline-block; - - > .field_select_wrapper{ - display: inline-block; - } - } -}*/ .mini_tabs a { text-decoration: none; font-weight:bold; @@ -835,6 +805,10 @@ input.dp-applied { color: inherit; text-decoration: none; } + /* Input reset */ + input[type="text"]{ + padding: 1px 2px; + } &.opened{ .sf_title{ @@ -874,6 +848,11 @@ input.dp-applied { margin-left: 0.7em; } } + .sf_message{ + display: none; + margin: 8px 8px 0px 8px; + border-radius: 0px; + } .sf_criterion_area{ /*display: none;*/ padding: 8px 8px 3px 8px; /* padding-bottom must equals to padding-top - .search_form_criteria:margin-bottom */ @@ -926,7 +905,8 @@ input.dp-applied { display: inline-block; margin-right: 5px; - /* Non editable criteria */ + + /* Non editable criteria */ &.locked{ background-color: $gray-extra-light; } @@ -978,6 +958,12 @@ input.dp-applied { .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;*/ } .sfc_form_group{ /* Form group (operators) is displayed only when the criteria is toggled to opened state */ @@ -1003,6 +989,40 @@ input.dp-applied { margin-right: 7px; } + .sfc_opc_multichoices{ + label > input{ + vertical-align: middle; + margin-right: 8px; + } + .sfc_opc_mc_toggler{ + + } + .sfc_opc_mc_filter{ + position: relative; + + input{ + width: 100%; + } + .sfc_opc_mcf_picto{ + position: absolute; + right: 5px; + top: 5px; + opacity: 0.6; + user-select: none; + } + } + .sfc_opc_mc_items{ + .sfc_opc_mc_item{ + margin-top: 5px; + + label{ + display: inline-block; + width: 100%; + } + } + } + } + /* Common operators for most criteria types processing*/ &.sfc_fg_operator_equals, &.sfc_fg_operator_contains, diff --git a/js/search/search_form_criteria.js b/js/search/search_form_criteria.js index 209b07fed1..67ef1c1f1d 100644 --- a/js/search/search_form_criteria.js +++ b/js/search/search_form_criteria.js @@ -14,12 +14,13 @@ $(function() 'operator': '=', 'values': [], 'oql': '', - 'is_removable': true, + 'is_removable': true, // Not used for now. If we come to show locked criterion they will need to have this flag set to false. + 'is_null_allowed': false, 'field': { 'label': '', + 'allowed_values': null, }, - // Available operators. They can be extended or restricted by derivated widgets (see this._initOperators() for more informations) 'available_operators': { '=': { @@ -191,7 +192,7 @@ $(function() { oOpElemToFocus = this.element.find('.sfc_fg_operator:first'); } - oOpElemToFocus.find('.sfc_op_content input:first').trigger('click').trigger('focus'); + oOpElemToFocus.find('.sfc_op_content input[type="text"]:first').trigger('click').trigger('focus'); }, _close: function() { @@ -380,6 +381,8 @@ $(function() // Append to form group oOpElem.appendTo(this.element.find('.sfc_fg_operators')); } + + // TODO: We could hide the radio button if there is only one operator }, // - Prepare the buttons (DOM and events) for a criteria _prepareButtons: function() @@ -440,7 +443,9 @@ $(function() // TODO: Make nice label sTitle = this.options.field.label + ' ' + this.operators[this.options.operator].label + ' ' + this._getValuesAsText(); } - this.element.find('.sfc_title').text(sTitle); + this.element.find('.sfc_title') + .text(sTitle) + .attr('title', sTitle); }, // Operators helpers diff --git a/js/search/search_form_criteria_enum.js b/js/search/search_form_criteria_enum.js new file mode 100644 index 0000000000..a96bbaa20c --- /dev/null +++ b/js/search/search_form_criteria_enum.js @@ -0,0 +1,241 @@ +//iTop Search form criteria enum +; +$(function() +{ + // the widget definition, where 'itop' is the namespace, + // 'search_form_criteria_enum' the widget name + $.widget( 'itop.search_form_criteria_enum', $.itop.search_form_criteria, + { + // default options + options: + { + // Overload default operator + 'operator': 'IN', + // Available operators + 'available_operators': { + 'IN': { + 'label': Dict.S('UI:Search:Criteria:Operator:Enum:In'), + 'code': 'in', + 'rank': 10, + }, + '=': null, // Remove this one from enum widget. + }, + }, + + + // the constructor + _create: function() + { + var me = this; + + this._super(); + this.element.addClass('search_form_criteria_enum'); + }, + // 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_enum'); + 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 + //------------------ + + // DOM element helpers + // - Prepare element DOM structure + _prepareElement: function() + { + this._super(); + + // Remove buttons + this.element.find('.sfc_fg_buttons').remove(); + }, + _prepareInOperator: function(oOpElem, sOpIdx, oOp) + { + var me = this; + + // Hide radio & name for now, until there is more than one operator + oOpElem.find('.sfc_op_radio, .sfc_op_name').hide(); + + // DOM element + var sOpId = oOpElem.attr('id'); + var oOpContentElem = $('
'); + + // - Check / Uncheck all togglers + var sTogglerId = 'toggle_' + sOpId; + var oTogglersElem = $('') + .append('') + .appendTo(oOpContentElem); + + // - Filter + var sFilterId = 'filter_' + sOpId; + var oFilterElem = $('') + .append('') + .appendTo(oOpContentElem); + + // - Allowed values + var oAllowedValuesElem = $(''); + if(this.options.field.allowed_values.values !== undefined) + { + var iValCounter = 0; + for(var sValCode in this.options.field.allowed_values.values) + { + var sItemId = 'value_' + sOpId + '_' + iValCounter; + var sValLabel = this.options.field.allowed_values.values[sValCode]; + var oValueElem = $('') + .append('') + .appendTo(oAllowedValuesElem); + + if(this._isSelectedValues(sValCode)) + { + oValueElem.find(':checkbox').prop('checked', true); + } + + iValCounter++; + } + } + oAllowedValuesElem.appendTo(oOpContentElem); + + // Events + // - Check / Uncheck all toggler + oTogglersElem.on('click', function(oEvent){ + // Check / uncheck all allowed values + var bChecked = $(this).closest('.sfc_opc_mc_toggler').find('input:checkbox').prop('checked'); + oOpContentElem.find('.sfc_opc_mc_item input:checkbox').prop('checked', bChecked); + + // Apply criteria + me._apply(); + }); + // - Filter + oFilterElem.find('input').on('keyup', function(){ + var sFilter = $(this).val(); + + if(sFilter === '') + { + oOpContentElem.find('.sfc_opc_mc_item').show(); + } + else + { + oOpContentElem.find('.sfc_opc_mc_item').each(function(){ + var oRegExp = new RegExp(sFilter, 'ig'); + var sValue = $(this).find('input').val(); + var sLabel = $(this).text(); + + if( (sValue.match(oRegExp) !== null) || (sLabel.match(oRegExp) !== null) ) + { + $(this).show(); + } + else + { + $(this).hide(); + } + }); + } + }); + // - Apply on check + oAllowedValuesElem.find('.sfc_opc_mc_item').on('click', function(){ + // Uncheck toggler + oTogglersElem.find('input:checkbox').prop('checked', false); + + // Apply criteria + me._apply(); + }); + + oOpElem.find('.sfc_op_content').append(oOpContentElem); + }, + // Reset operator's state + _resetInOperator: 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 + _getInOperatorValues: function(oOpElem) + { + var aValues = []; + + oOpElem.find('.sfc_opc_mc_item input:checked').each(function(iIdx, oElem){ + var sValue = $(oElem).val(); + var sLabel = $(oElem).parent().text(); + aValues.push({value: sValue, label: sLabel}); + }); + + return aValues; + }, + // 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; + }, + + + // Value helpers + // - Return true if sValue is among the selected values "codes" + _isSelectedValues: function(sValue) + { + var bFound = false; + + for(var iValIdx in this.options.values) + { + if(this.options.values[iValIdx].value === sValue) + { + bFound = true; + break; + } + } + + return bFound; + }, + // - Return true if sLabel is among the selected values "labels" + _isSelectedLabels: function(sLabel) + { + var bFound = false; + + for(var iValIdx in this.options.values) + { + if(this.options.values[iValIdx].label === sLabel) + { + bFound = true; + break; + } + } + + return bFound; + }, + }); +}); diff --git a/js/search/search_form_handler.js b/js/search/search_form_handler.js index cf62d9e954..005fe35d12 100644 --- a/js/search/search_form_handler.js +++ b/js/search/search_form_handler.js @@ -64,13 +64,13 @@ $(function() // }, ], }, - 'supported_criterion_types': ['raw', 'string'], 'default_criteria_type': 'raw', }, // jQuery elements elements: { + message_area: null, active_criterion: null, more_criterion: null, results_area: null, @@ -216,6 +216,11 @@ $(function() { var me = this; + // Build DOM elements + // - Message area + this.elements.message_area = this.element.find('.sf_message'); + this._cleanMessageArea(); + // Events // - Refresh icon this.element.find('.sft_refresh').on('click', function(oEvent){ @@ -426,6 +431,7 @@ $(function() class_alias: oFieldDef.class_alias, code: oFieldDef.code, widget: oFieldDef.widget, + allowed_values: oFieldDef.allowed_values, }; } @@ -445,30 +451,18 @@ $(function() // - Find a criteria's type from a field's ref (usually