mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* EUIficate ranges control * Remove unused translations * Fix screenreader issue * Add an initial case for safety * Remove labels, add icon between ranges * Add title for delete btn # Conflicts: # x-pack/plugins/translations/translations/zh-CN.json
This commit is contained in:
parent
000da86fc6
commit
704d9b75cb
6 changed files with 172 additions and 86 deletions
|
@ -21,7 +21,7 @@ import { BucketAggType } from './_bucket_agg_type';
|
|||
import { createFilterRange } from './create_filter/range';
|
||||
import { FieldFormat } from '../../../field_formats/field_format';
|
||||
import { RangeKey } from './range_key';
|
||||
import rangesTemplate from '../controls/ranges.html';
|
||||
import { RangesParamEditor } from '../controls/ranges';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const keyCaches = new WeakMap();
|
||||
|
@ -91,7 +91,7 @@ export const rangeBucketAgg = new BucketAggType({
|
|||
{ from: 0, to: 1000 },
|
||||
{ from: 1000, to: 2000 }
|
||||
],
|
||||
editor: rangesTemplate,
|
||||
editorComponent: RangesParamEditor,
|
||||
write: function (aggConfig, output) {
|
||||
output.params.ranges = aggConfig.params.ranges;
|
||||
output.params.keyed = true;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFieldText, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiFieldText, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import Ipv4Address from '../../../utils/ipv4_address';
|
||||
import { InputList, InputListConfig, InputModel, InputObject, InputItem } from './input_list';
|
||||
|
@ -95,6 +95,9 @@ function FromToList({ showValidation, onBlur, ...rest }: FromToListProps) {
|
|||
onBlur={onBlur}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="sortRight" color="subdued" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFieldText
|
||||
aria-label={i18n.translate('common.ui.aggTypes.ipRanges.ipRangeToAriaLabel', {
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
<table class="visEditorAgg__rangesTable form-group" ng-show="agg.params.ranges.length">
|
||||
<tr>
|
||||
<th scope="col">
|
||||
<label
|
||||
id="visEditorRangeFrom{{agg.id}}"
|
||||
i18n-id="common.ui.aggTypes.ranges.fromColumnLabel"
|
||||
i18n-default-message="From"
|
||||
></label>
|
||||
</th>
|
||||
<th scope="col" colspan="2">
|
||||
<label
|
||||
id="visEditorRangeTo{{agg.id}}"
|
||||
i18n-id="common.ui.aggTypes.ranges.toColumnLabel"
|
||||
i18n-default-message="To"
|
||||
></label>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr
|
||||
ng-repeat="range in agg.params.ranges track by $index">
|
||||
<td>
|
||||
<input
|
||||
aria-labelledby="visEditorRangeFrom{{agg.id}}"
|
||||
ng-model="range.from"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="range.from"
|
||||
step="any" />
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
aria-labelledby="visEditorRangeTo{{agg.id}}"
|
||||
ng-model="range.to"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="range.to"
|
||||
step="any" />
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="{{ ::'common.ui.aggTypes.ranges.removeRangeButtonAriaLabel' | i18n: { defaultMessage: 'Remove this range' } }}"
|
||||
ng-click="agg.params.ranges.splice($index, 1)"
|
||||
class="kuiButton kuiButton--danger kuiButton--small">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input ng-model="agg.params.ranges.length" name="rangeLength" required min="1" type="number" class="ng-hide" />
|
||||
<div class="hintbox" ng-show="aggForm.rangeLength.$invalid">
|
||||
<p>
|
||||
<i class="fa fa-danger text-danger"></i>
|
||||
<strong
|
||||
i18n-id="common.ui.aggTypes.ranges.requiredRangeTitle"
|
||||
i18n-default-message="Required:"
|
||||
></strong>
|
||||
<span
|
||||
i18n-id="common.ui.aggTypes.ranges.requiredRangeDescription"
|
||||
i18n-default-message="You must specify at least one range."
|
||||
></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
ng-click="agg.params.ranges.push({})"
|
||||
class="kuiButton kuiButton--primary kuiButton--fullWidth"
|
||||
i18n-id="common.ui.aggTypes.ranges.addRangeButtonLabel"
|
||||
i18n-default-message="Add Range"
|
||||
></button>
|
166
src/legacy/ui/public/agg_types/controls/ranges.tsx
Normal file
166
src/legacy/ui/public/agg_types/controls/ranges.tsx
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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, { Fragment, useState, useEffect } from 'react';
|
||||
import {
|
||||
htmlIdGenerator,
|
||||
EuiButtonIcon,
|
||||
EuiFieldNumber,
|
||||
EuiFlexItem,
|
||||
EuiFlexGroup,
|
||||
EuiIcon,
|
||||
EuiSpacer,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEqual, omit } from 'lodash';
|
||||
import { AggParamEditorProps } from 'ui/vis/editors/default';
|
||||
|
||||
const FROM_PLACEHOLDER = '\u2212\u221E';
|
||||
const TO_PLACEHOLDER = '+\u221E';
|
||||
|
||||
const generateId = htmlIdGenerator();
|
||||
const isEmpty = (value: any) => value === undefined || value === null;
|
||||
|
||||
interface RangeValues {
|
||||
from?: number;
|
||||
to?: number;
|
||||
}
|
||||
|
||||
interface RangeValuesModel extends RangeValues {
|
||||
id: string;
|
||||
}
|
||||
|
||||
function RangesParamEditor({ agg, value = [], setValue }: AggParamEditorProps<RangeValues[]>) {
|
||||
const [ranges, setRanges] = useState(() => value.map(range => ({ ...range, id: generateId() })));
|
||||
|
||||
// set up an initial range when there is no default range
|
||||
useEffect(() => {
|
||||
if (!value.length) {
|
||||
onAddRange();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
// responsible for discarding changes
|
||||
if (
|
||||
value.length !== ranges.length ||
|
||||
value.some((range, index) => !isEqual(range, omit(ranges[index], 'id')))
|
||||
) {
|
||||
setRanges(value.map(range => ({ ...range, id: generateId() })));
|
||||
}
|
||||
},
|
||||
[value]
|
||||
);
|
||||
|
||||
const updateRanges = (rangeValues: RangeValuesModel[]) => {
|
||||
// do not set internal id parameter into saved object
|
||||
setValue(rangeValues.map(range => omit(range, 'id')));
|
||||
setRanges(rangeValues);
|
||||
};
|
||||
const onAddRange = () => updateRanges([...ranges, { id: generateId() }]);
|
||||
const onRemoveRange = (id: string) => updateRanges(ranges.filter(range => range.id !== id));
|
||||
const onChangeRange = (id: string, key: string, newValue: string) =>
|
||||
updateRanges(
|
||||
ranges.map(range =>
|
||||
range.id === id
|
||||
? {
|
||||
...range,
|
||||
[key]: newValue === '' ? undefined : parseFloat(newValue),
|
||||
}
|
||||
: range
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ranges.map(({ from, to, id }) => {
|
||||
const deleteBtnTitle = i18n.translate(
|
||||
'common.ui.aggTypes.ranges.removeRangeButtonAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Remove the range of {from} to {to}',
|
||||
values: {
|
||||
from: isEmpty(from) ? FROM_PLACEHOLDER : from,
|
||||
to: isEmpty(to) ? TO_PLACEHOLDER : to,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment key={id}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiFieldNumber
|
||||
aria-label={i18n.translate('common.ui.aggTypes.ranges.fromLabel', {
|
||||
defaultMessage: 'From',
|
||||
})}
|
||||
value={isEmpty(from) ? '' : from}
|
||||
placeholder={FROM_PLACEHOLDER}
|
||||
onChange={ev => onChangeRange(id, 'from', ev.target.value)}
|
||||
fullWidth={true}
|
||||
compressed={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="sortRight" color="subdued" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFieldNumber
|
||||
aria-label={i18n.translate('common.ui.aggTypes.ranges.toLabel', {
|
||||
defaultMessage: 'To',
|
||||
})}
|
||||
value={isEmpty(to) ? '' : to}
|
||||
placeholder={TO_PLACEHOLDER}
|
||||
onChange={ev => onChangeRange(id, 'to', ev.target.value)}
|
||||
fullWidth={true}
|
||||
compressed={true}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
title={deleteBtnTitle}
|
||||
aria-label={deleteBtnTitle}
|
||||
disabled={value.length === 1}
|
||||
color="danger"
|
||||
iconType="trash"
|
||||
onClick={() => onRemoveRange(id)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="xs" />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexItem>
|
||||
<EuiButtonEmpty iconType="plusInCircleFilled" onClick={onAddRange} size="xs">
|
||||
<FormattedMessage
|
||||
id="common.ui.aggTypes.ranges.addRangeButtonLabel"
|
||||
defaultMessage="Add range"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { RangesParamEditor };
|
|
@ -229,12 +229,6 @@
|
|||
"common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} は必須パラメーターです",
|
||||
"common.ui.aggTypes.placeMarkersOffGridLabel": "グリッド外にマーカーを配置 (ジオセントロイドを使用)",
|
||||
"common.ui.aggTypes.precisionLabel": "精度",
|
||||
"common.ui.aggTypes.ranges.addRangeButtonLabel": "範囲を追加",
|
||||
"common.ui.aggTypes.ranges.fromColumnLabel": "From",
|
||||
"common.ui.aggTypes.ranges.removeRangeButtonAriaLabel": "この範囲を削除",
|
||||
"common.ui.aggTypes.ranges.requiredRangeDescription": "範囲を最低 1 つ指定する必要があります。",
|
||||
"common.ui.aggTypes.ranges.requiredRangeTitle": "必須:",
|
||||
"common.ui.aggTypes.ranges.toColumnLabel": "To",
|
||||
"common.ui.aggTypes.showEmptyBucketsLabel": "空のバケットを表示",
|
||||
"common.ui.aggTypes.showEmptyBucketsTooltip": "結果のあるバケットだけでなくすべてのバケットを表示します",
|
||||
"common.ui.aggTypes.sizeLabel": "サイズ",
|
||||
|
|
|
@ -228,12 +228,6 @@
|
|||
"common.ui.aggTypes.paramTypes.field.requiredFieldParameterErrorMessage": "{fieldParameter} 是必需字段",
|
||||
"common.ui.aggTypes.placeMarkersOffGridLabel": "将标记置于网格外(使用 geocentroid)",
|
||||
"common.ui.aggTypes.precisionLabel": "精确度",
|
||||
"common.ui.aggTypes.ranges.addRangeButtonLabel": "添加范围",
|
||||
"common.ui.aggTypes.ranges.fromColumnLabel": "从",
|
||||
"common.ui.aggTypes.ranges.removeRangeButtonAriaLabel": "移除此范围",
|
||||
"common.ui.aggTypes.ranges.requiredRangeDescription": "必须指定至少一个范围。",
|
||||
"common.ui.aggTypes.ranges.requiredRangeTitle": "必需:",
|
||||
"common.ui.aggTypes.ranges.toColumnLabel": "到",
|
||||
"common.ui.aggTypes.showEmptyBucketsLabel": "显示空存储桶",
|
||||
"common.ui.aggTypes.showEmptyBucketsTooltip": "显示所有存储桶,不仅仅有结果的存储桶",
|
||||
"common.ui.aggTypes.sizeLabel": "大小",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue