diff --git a/src/legacy/ui/public/agg_types/buckets/filters.js b/src/legacy/ui/public/agg_types/buckets/filters.js index 17166d2d74ab8..b052b3444e8f0 100644 --- a/src/legacy/ui/public/agg_types/buckets/filters.js +++ b/src/legacy/ui/public/agg_types/buckets/filters.js @@ -23,9 +23,7 @@ import angular from 'angular'; import { BucketAggType } from './_bucket_agg_type'; import { createFilterFilters } from './create_filter/filters'; import { decorateQuery, luceneStringToDsl } from '@kbn/es-query'; -import '../directives/click_focus'; -import '../directives/parse_query'; -import filtersTemplate from '../controls/filters.html'; +import { FiltersParamEditor } from '../controls/filters'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; @@ -41,7 +39,7 @@ export const filtersBucketAgg = new BucketAggType({ params: [ { name: 'filters', - editor: filtersTemplate, + editorComponent: FiltersParamEditor, default: [ { input: {}, label: '' } ], write: function (aggConfig, output) { const inFilters = aggConfig.params.filters; diff --git a/src/legacy/ui/public/agg_types/controls/filter.tsx b/src/legacy/ui/public/agg_types/controls/filter.tsx new file mode 100644 index 0000000000000..963d941d3432b --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/filter.tsx @@ -0,0 +1,136 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState } from 'react'; +import { + EuiForm, + EuiFlexGroup, + EuiFlexItem, + EuiButtonIcon, + EuiFieldText, + EuiFormRow, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface FilterRowProps { + id: string; + arrayIndex: number; + customLabel: string; + value: string; + autoFocus: boolean; + disableRemove: boolean; + dataTestSubj: string; + onChangeValue(id: string, query: string, label: string): void; + onRemoveFilter(id: string): void; +} + +function FilterRow({ + id, + arrayIndex, + customLabel, + value, + autoFocus, + disableRemove, + dataTestSubj, + onChangeValue, + onRemoveFilter, +}: FilterRowProps) { + const [showCustomLabel, setShowCustomLabel] = useState(false); + const filterLabel = i18n.translate('common.ui.aggTypes.filters.filterLabel', { + defaultMessage: 'Filter {index}', + values: { + index: arrayIndex + 1, + }, + }); + + const FilterControl = ( + + + setShowCustomLabel(!showCustomLabel)} + /> + + + onRemoveFilter(id)} + /> + + + ); + + return ( + + + onChangeValue(id, ev.target.value, customLabel)} + fullWidth={true} + autoFocus={autoFocus} + /> + + {showCustomLabel ? ( + + onChangeValue(id, value, ev.target.value)} + fullWidth={true} + /> + + ) : null} + + ); +} + +export { FilterRow }; diff --git a/src/legacy/ui/public/agg_types/controls/filters.html b/src/legacy/ui/public/agg_types/controls/filters.html deleted file mode 100644 index 13d32c716ecca..0000000000000 --- a/src/legacy/ui/public/agg_types/controls/filters.html +++ /dev/null @@ -1,90 +0,0 @@ -
-
-
- - -
- - -
-
- -
- -
- -
- - -
-
-
- - -
-

- - - -

-
- - - -
diff --git a/src/legacy/ui/public/agg_types/controls/filters.tsx b/src/legacy/ui/public/agg_types/controls/filters.tsx new file mode 100644 index 0000000000000..1d393b996b4ea --- /dev/null +++ b/src/legacy/ui/public/agg_types/controls/filters.tsx @@ -0,0 +1,120 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState, useEffect } from 'react'; +import { omit, isEqual } from 'lodash'; +import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui'; +import { AggParamEditorProps } from 'ui/vis/editors/default'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { data } from 'plugins/data'; +import { FilterRow } from './filter'; + +const { toUser, fromUser } = data.query.helpers; +const generateId = htmlIdGenerator(); + +interface FilterValue { + input: any; + label: string; + id: string; +} + +function FiltersParamEditor({ agg, value, setValue }: AggParamEditorProps) { + const [filters, setFilters] = useState(() => + value.map(filter => ({ ...filter, id: generateId() })) + ); + + useEffect(() => { + // set parsed values into model after initialization + setValue( + filters.map(filter => + omit({ ...filter, input: { query: fromUser(filter.input.query) } }, 'id') + ) + ); + }, []); + + useEffect( + () => { + // responsible for discarding changes + if ( + value.length !== filters.length || + value.some((filter, index) => !isEqual(filter, omit(filters[index], 'id'))) + ) { + setFilters(value.map(filter => ({ ...filter, id: generateId() }))); + } + }, + [value] + ); + + const updateFilters = (updatedFilters: FilterValue[]) => { + // do not set internal id parameter into saved object + setValue(updatedFilters.map(filter => omit(filter, 'id'))); + setFilters(updatedFilters); + }; + + const onAddFilter = () => + updateFilters([...filters, { input: { query: '' }, label: '', id: generateId() }]); + const onRemoveFilter = (id: string) => updateFilters(filters.filter(filter => filter.id !== id)); + const onChangeValue = (id: string, query: string, label: string) => + updateFilters( + filters.map(filter => + filter.id === id + ? { + ...filter, + input: { query: fromUser(query) }, + label, + } + : filter + ) + ); + + return ( + <> + {filters.map(({ input, label, id }, arrayIndex) => ( + + ))} + + + + + + ); +} + +export { FiltersParamEditor }; diff --git a/src/legacy/ui/public/agg_types/directives/click_focus.js b/src/legacy/ui/public/agg_types/directives/click_focus.js deleted file mode 100644 index d7cd813469921..0000000000000 --- a/src/legacy/ui/public/agg_types/directives/click_focus.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import { uiModules } from '../../modules'; -const module = uiModules.get('kibana'); - -module.directive('clickFocus', function () { - return { - scope: { - clickFocus: '=' - }, - restrict: 'A', - link: function ($scope, $elem) { - function handler() { - const focusElem = $.find('input[name=' + $scope.clickFocus + ']'); - if (focusElem[0]) focusElem[0].focus(); - } - - $elem.bind('click', handler); - $scope.$on('$destroy', _.bindKey($elem, 'unbind', 'click', handler)); - } - }; -}); diff --git a/src/legacy/ui/public/agg_types/directives/parse_query.js b/src/legacy/ui/public/agg_types/directives/parse_query.js deleted file mode 100644 index 768e0257aa56a..0000000000000 --- a/src/legacy/ui/public/agg_types/directives/parse_query.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { data } from 'plugins/data'; -const { toUser, fromUser } = data.query.helpers; -import { uiModules } from '../../modules'; - -uiModules - .get('kibana') - .directive('parseQuery', function () { - - return { - restrict: 'A', - require: 'ngModel', - scope: { - 'ngModel': '=' - }, - link: function ($scope, elem, attr, ngModel) { - const init = function () { - $scope.ngModel = fromUser($scope.ngModel); - }; - - ngModel.$parsers.push(fromUser); - ngModel.$formatters.push(toUser); - - init(); - } - }; - }); diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1c3e69c41ba3b..bd7e86c25526d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -124,13 +124,10 @@ "common.ui.aggTypes.extendedBounds.maxLabel": "最大值", "common.ui.aggTypes.extendedBounds.minLabel": "最小值", "common.ui.aggTypes.field.fieldLabel": "字段", - "common.ui.aggTypes.filters.addFilterButtonLabel": "添加筛选", "common.ui.aggTypes.filters.definiteFilterLabel": "筛选 {index} 标签", "common.ui.aggTypes.filters.filterLabel": "筛选 {index}", "common.ui.aggTypes.filters.labelPlaceholder": "标签", "common.ui.aggTypes.filters.removeFilterButtonAriaLabel": "移除此筛选", - "common.ui.aggTypes.filters.requiredFilterDescription": "必须指定至少一个筛选。", - "common.ui.aggTypes.filters.requiredFilterLabel": "必需:", "common.ui.aggTypes.filters.toggleFilterButtonAriaLabel": "切换筛选标签", "common.ui.aggTypes.histogram.missingMaxMinValuesWarning": "无法检索最大值和最小值以自动缩放直方图存储桶。这可能会导致可视化性能低下。", "common.ui.aggTypes.ipRanges.cidrMask.addRangeButtonLabel": "添加范围",