[Maps] add text halo color and width style properties (#53827)

* [Maps] add text halo color and width style properties

* fix jest test

* update for new editor UI

* add removed styling

* get halo size from label size

* fix label border size with dynamic label size

* clean up

* fix jest test

* fix jest test

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2020-01-13 16:34:56 -05:00 committed by GitHub
parent b65710d33d
commit e9e44ec851
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 239 additions and 16 deletions

View file

@ -42,6 +42,14 @@ export function getVectorStyleLabel(styleName) {
return i18n.translate('xpack.maps.styles.vector.labelSizeLabel', {
defaultMessage: 'Label size',
});
case VECTOR_STYLES.LABEL_BORDER_COLOR:
return i18n.translate('xpack.maps.styles.vector.labelBorderColorLabel', {
defaultMessage: 'Label border color',
});
case VECTOR_STYLES.LABEL_BORDER_SIZE:
return i18n.translate('xpack.maps.styles.vector.labelBorderWidthLabel', {
defaultMessage: 'Label border width',
});
default:
return styleName;
}

View file

@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiFormRow, EuiSelect } from '@elastic/eui';
import { LABEL_BORDER_SIZES, VECTOR_STYLES } from '../../vector_style_defaults';
import { getVectorStyleLabel } from '../get_vector_style_label';
import { i18n } from '@kbn/i18n';
const options = [
{
value: LABEL_BORDER_SIZES.NONE,
text: i18n.translate('xpack.maps.styles.labelBorderSize.noneLabel', {
defaultMessage: 'None',
}),
},
{
value: LABEL_BORDER_SIZES.SMALL,
text: i18n.translate('xpack.maps.styles.labelBorderSize.smallLabel', {
defaultMessage: 'Small',
}),
},
{
value: LABEL_BORDER_SIZES.MEDIUM,
text: i18n.translate('xpack.maps.styles.labelBorderSize.mediumLabel', {
defaultMessage: 'Medium',
}),
},
{
value: LABEL_BORDER_SIZES.LARGE,
text: i18n.translate('xpack.maps.styles.labelBorderSize.largeLabel', {
defaultMessage: 'Large',
}),
},
];
export function VectorStyleLabelBorderSizeEditor({ handlePropertyChange, styleProperty }) {
function onChange(e) {
const styleDescriptor = {
options: { size: e.target.value },
};
handlePropertyChange(styleProperty.getStyleName(), styleDescriptor);
}
return (
<EuiFormRow
label={getVectorStyleLabel(VECTOR_STYLES.LABEL_BORDER_SIZE)}
display="columnCompressed"
>
<EuiSelect
options={options}
value={styleProperty.getOptions().size}
onChange={onChange}
aria-label={i18n.translate('xpack.maps.styles.labelBorderSizeSelect.ariaLabel', {
defaultMessage: 'Select label border size',
})}
compressed
/>
</EuiFormRow>
);
}

View file

@ -12,6 +12,7 @@ import { VectorStyleColorEditor } from './color/vector_style_color_editor';
import { VectorStyleSizeEditor } from './size/vector_style_size_editor';
import { VectorStyleSymbolEditor } from './vector_style_symbol_editor';
import { VectorStyleLabelEditor } from './label/vector_style_label_editor';
import { VectorStyleLabelBorderSizeEditor } from './label/vector_style_label_border_size_editor';
import { VectorStyle } from '../vector_style';
import { OrientationEditor } from './orientation/orientation_editor';
import {
@ -248,6 +249,27 @@ export class VectorStyleEditor extends Component {
}
/>
<EuiSpacer size="m" />
<VectorStyleColorEditor
swatches={DEFAULT_LINE_COLORS}
onStaticStyleChange={this._onStaticStyleChange}
onDynamicStyleChange={this._onDynamicStyleChange}
styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_BORDER_COLOR]}
fields={this._getOrdinalFields()}
defaultStaticStyleOptions={
this.state.defaultStaticProperties[VECTOR_STYLES.LABEL_BORDER_COLOR].options
}
defaultDynamicStyleOptions={
this.state.defaultDynamicProperties[VECTOR_STYLES.LABEL_BORDER_COLOR].options
}
/>
<EuiSpacer size="m" />
<VectorStyleLabelBorderSizeEditor
handlePropertyChange={this.props.handlePropertyChange}
styleProperty={this.props.styleProperties[VECTOR_STYLES.LABEL_BORDER_SIZE]}
/>
<EuiSpacer size="m" />
</Fragment>
);
}

View file

@ -55,6 +55,11 @@ export class DynamicColorProperty extends DynamicStyleProperty {
mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha);
}
syncLabelBorderColorWithMb(mbLayerId, mbMap) {
const color = this._getMbColor();
mbMap.setPaintProperty(mbLayerId, 'text-halo-color', color);
}
isCustomColorRamp() {
return this._options.useCustomColorRamp;
}

View file

@ -4,7 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
// eslint-disable-next-line no-unused-vars
jest.mock('../components/vector_style_editor', () => ({
VectorStyleEditor: () => {
return <div>mockVectorStyleEditor</div>;
},
}));
import React from 'react';
import { shallow } from 'enzyme';

View file

@ -5,7 +5,7 @@
*/
import { DynamicStyleProperty } from './dynamic_style_property';
import { getComputedFieldName } from '../style_util';
import {
HALF_LARGE_MAKI_ICON_SIZE,
LARGE_MAKI_ICON_SIZE,
@ -63,7 +63,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty {
}
syncHaloWidthWithMb(mbLayerId, mbMap) {
const haloWidth = this._getMbSize();
const haloWidth = this.getMbSizeExpression();
mbMap.setPaintProperty(mbLayerId, 'icon-halo-width', haloWidth);
}
@ -76,7 +76,7 @@ export class DynamicSizeProperty extends DynamicStyleProperty {
mbMap.setLayoutProperty(symbolLayerId, 'icon-image', `${symbolId}-${iconPixels}`);
const halfIconPixels = iconPixels / 2;
const targetName = getComputedFieldName(VECTOR_STYLES.ICON_SIZE, this._options.field.name);
const targetName = this.getComputedFieldName();
// Using property state instead of feature-state because layout properties do not support feature-state
mbMap.setLayoutProperty(symbolLayerId, 'icon-size', [
'interpolate',
@ -94,29 +94,29 @@ export class DynamicSizeProperty extends DynamicStyleProperty {
}
syncCircleStrokeWidthWithMb(mbLayerId, mbMap) {
const lineWidth = this._getMbSize();
const lineWidth = this.getMbSizeExpression();
mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', lineWidth);
}
syncCircleRadiusWithMb(mbLayerId, mbMap) {
const circleRadius = this._getMbSize();
const circleRadius = this.getMbSizeExpression();
mbMap.setPaintProperty(mbLayerId, 'circle-radius', circleRadius);
}
syncLineWidthWithMb(mbLayerId, mbMap) {
const lineWidth = this._getMbSize();
const lineWidth = this.getMbSizeExpression();
mbMap.setPaintProperty(mbLayerId, 'line-width', lineWidth);
}
syncLabelSizeWithMb(mbLayerId, mbMap) {
const lineWidth = this._getMbSize();
const lineWidth = this.getMbSizeExpression();
mbMap.setLayoutProperty(mbLayerId, 'text-size', lineWidth);
}
_getMbSize() {
getMbSizeExpression() {
if (this._isSizeDynamicConfigComplete(this._options)) {
return this._getMbDataDrivenSize({
targetName: getComputedFieldName(this._styleName, this._options.field.name),
targetName: this.getComputedFieldName(),
minSize: this._options.minSize,
maxSize: this._options.maxSize,
});

View file

@ -8,7 +8,7 @@ import _ from 'lodash';
import { AbstractStyleProperty } from './style_property';
import { DEFAULT_SIGMA } from '../vector_style_defaults';
import { STYLE_TYPE } from '../../../../../common/constants';
import { scaleValue } from '../style_util';
import { scaleValue, getComputedFieldName } from '../style_util';
import React from 'react';
import { OrdinalLegend } from './components/ordinal_legend';
import { CategoricalLegend } from './components/categorical_legend';
@ -31,6 +31,13 @@ export class DynamicStyleProperty extends AbstractStyleProperty {
return this._field;
}
getComputedFieldName() {
if (!this.isComplete()) {
return null;
}
return getComputedFieldName(this._styleName, this.getField().getName());
}
isDynamic() {
return true;
}

View file

@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import _ from 'lodash';
import { AbstractStyleProperty } from './style_property';
import { DEFAULT_LABEL_SIZE, LABEL_BORDER_SIZES } from '../vector_style_defaults';
const SMALL_SIZE = 1 / 16;
const MEDIUM_SIZE = 1 / 8;
const LARGE_SIZE = 1 / 5; // halo of 1/4 is just a square. Use smaller ratio to preserve contour on letters
function getWidthRatio(size) {
switch (size) {
case LABEL_BORDER_SIZES.LARGE:
return LARGE_SIZE;
case LABEL_BORDER_SIZES.MEDIUM:
return MEDIUM_SIZE;
default:
return SMALL_SIZE;
}
}
export class LabelBorderSizeProperty extends AbstractStyleProperty {
constructor(options, styleName, labelSizeProperty) {
super(options, styleName);
this._labelSizeProperty = labelSizeProperty;
}
syncLabelBorderSizeWithMb(mbLayerId, mbMap) {
const widthRatio = getWidthRatio(this.getOptions().size);
if (this.getOptions().size === LABEL_BORDER_SIZES.NONE) {
mbMap.setPaintProperty(mbLayerId, 'text-halo-width', 0);
} else if (this._labelSizeProperty.isDynamic() && this._labelSizeProperty.isComplete()) {
const labelSizeExpression = this._labelSizeProperty.getMbSizeExpression();
mbMap.setPaintProperty(mbLayerId, 'text-halo-width', [
'max',
['*', labelSizeExpression, widthRatio],
1,
]);
} else {
const labelSize = _.get(this._labelSizeProperty.getOptions(), 'size', DEFAULT_LABEL_SIZE);
const labelBorderSize = Math.max(labelSize * widthRatio, 1);
mbMap.setPaintProperty(mbLayerId, 'text-halo-width', labelBorderSize);
}
}
}

View file

@ -39,4 +39,8 @@ export class StaticColorProperty extends StaticStyleProperty {
mbMap.setPaintProperty(mbLayerId, 'text-color', this._options.color);
mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha);
}
syncLabelBorderColorWithMb(mbLayerId, mbMap) {
mbMap.setPaintProperty(mbLayerId, 'text-halo-color', this._options.color);
}
}

View file

@ -38,6 +38,7 @@ import { StaticOrientationProperty } from './properties/static_orientation_prope
import { DynamicOrientationProperty } from './properties/dynamic_orientation_property';
import { StaticTextProperty } from './properties/static_text_property';
import { DynamicTextProperty } from './properties/dynamic_text_property';
import { LabelBorderSizeProperty } from './properties/label_border_size_property';
import { extractColorFromStyleProperty } from './components/legend/extract_color_from_style_property';
const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT];
@ -100,6 +101,15 @@ export class VectorStyle extends AbstractStyle {
this._descriptor.properties[VECTOR_STYLES.LABEL_COLOR],
VECTOR_STYLES.LABEL_COLOR
);
this._labelBorderColorStyleProperty = this._makeColorProperty(
this._descriptor.properties[VECTOR_STYLES.LABEL_BORDER_COLOR],
VECTOR_STYLES.LABEL_BORDER_COLOR
);
this._labelBorderSizeStyleProperty = new LabelBorderSizeProperty(
this._descriptor.properties[VECTOR_STYLES.LABEL_BORDER_SIZE].options,
VECTOR_STYLES.LABEL_BORDER_SIZE,
this._labelSizeStyleProperty
);
}
_getAllStyleProperties() {
@ -112,6 +122,8 @@ export class VectorStyle extends AbstractStyle {
this._labelStyleProperty,
this._labelSizeStyleProperty,
this._labelColorStyleProperty,
this._labelBorderColorStyleProperty,
this._labelBorderSizeStyleProperty,
];
}
@ -537,6 +549,8 @@ export class VectorStyle extends AbstractStyle {
this._labelStyleProperty.syncTextFieldWithMb(textLayerId, mbMap);
this._labelColorStyleProperty.syncLabelColorWithMb(textLayerId, mbMap, alpha);
this._labelSizeStyleProperty.syncLabelSizeWithMb(textLayerId, mbMap);
this._labelBorderSizeStyleProperty.syncLabelBorderSizeWithMb(textLayerId, mbMap);
this._labelBorderColorStyleProperty.syncLabelBorderColorWithMb(textLayerId, mbMap);
}
setMBSymbolPropertiesForPoints({ mbMap, symbolLayerId, alpha }) {

View file

@ -102,6 +102,17 @@ describe('getDescriptorWithMissingStylePropsRemoved', () => {
},
type: 'STATIC',
},
labelBorderColor: {
options: {
color: '#FFFFFF',
},
type: 'STATIC',
},
labelBorderSize: {
options: {
size: 'SMALL',
},
},
labelColor: {
options: {
color: '#000000',

View file

@ -16,6 +16,14 @@ export const MAX_SIZE = 64;
export const DEFAULT_MIN_SIZE = 4;
export const DEFAULT_MAX_SIZE = 32;
export const DEFAULT_SIGMA = 3;
export const DEFAULT_LABEL_SIZE = 14;
export const LABEL_BORDER_SIZES = {
NONE: 'NONE',
SMALL: 'SMALL',
MEDIUM: 'MEDIUM',
LARGE: 'LARGE',
};
export const VECTOR_STYLES = {
SYMBOL: 'symbol',
@ -27,6 +35,8 @@ export const VECTOR_STYLES = {
LABEL_TEXT: 'labelText',
LABEL_COLOR: 'labelColor',
LABEL_SIZE: 'labelSize',
LABEL_BORDER_COLOR: 'labelBorderColor',
LABEL_BORDER_SIZE: 'labelBorderSize',
};
export const LINE_STYLES = [VECTOR_STYLES.LINE_COLOR, VECTOR_STYLES.LINE_WIDTH];
@ -45,6 +55,11 @@ export function getDefaultProperties(mapColors = []) {
symbolId: DEFAULT_ICON,
},
},
[VECTOR_STYLES.LABEL_BORDER_SIZE]: {
options: {
size: LABEL_BORDER_SIZES.SMALL,
},
},
};
}
@ -103,7 +118,13 @@ export function getDefaultStaticProperties(mapColors = []) {
[VECTOR_STYLES.LABEL_SIZE]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
size: 14,
size: DEFAULT_LABEL_SIZE,
},
},
[VECTOR_STYLES.LABEL_BORDER_COLOR]: {
type: VectorStyle.STYLE_TYPE.STATIC,
options: {
color: isDarkMode ? '#000000' : '#FFFFFF',
},
},
};
@ -158,7 +179,7 @@ export function getDefaultDynamicProperties() {
},
},
[VECTOR_STYLES.ICON_ORIENTATION]: {
type: VectorStyle.STYLE_TYPE.STATIC,
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
field: undefined,
fieldMetaOptions: {
@ -168,13 +189,13 @@ export function getDefaultDynamicProperties() {
},
},
[VECTOR_STYLES.LABEL_TEXT]: {
type: VectorStyle.STYLE_TYPE.STATIC,
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
field: undefined,
},
},
[VECTOR_STYLES.LABEL_COLOR]: {
type: VectorStyle.STYLE_TYPE.STATIC,
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
color: COLOR_GRADIENTS[0].value,
field: undefined,
@ -185,7 +206,7 @@ export function getDefaultDynamicProperties() {
},
},
[VECTOR_STYLES.LABEL_SIZE]: {
type: VectorStyle.STYLE_TYPE.STATIC,
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
minSize: DEFAULT_MIN_SIZE,
maxSize: DEFAULT_MAX_SIZE,
@ -196,5 +217,16 @@ export function getDefaultDynamicProperties() {
},
},
},
[VECTOR_STYLES.LABEL_BORDER_COLOR]: {
type: VectorStyle.STYLE_TYPE.DYNAMIC,
options: {
color: COLOR_GRADIENTS[0].value,
field: undefined,
fieldMetaOptions: {
isEnabled: true,
sigma: DEFAULT_SIGMA,
},
},
},
};
}