mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Maps] Enable borders for icon symbols (#43066)
* [Maps] Enable borders on icons * [Maps] Enable borders on icons * Review feedback * Remove unnecessary Fragment
This commit is contained in:
parent
c29a17ce3c
commit
3503aa16ad
9 changed files with 61 additions and 30 deletions
|
@ -24,6 +24,10 @@ Use *Icon* to symbolize Points as icons.
|
|||
|
||||
*Fill color*:: The fill color of the point features.
|
||||
|
||||
*Border color*:: The border color of the point features.
|
||||
|
||||
*Border width*:: The border width of the point features.
|
||||
|
||||
*Symbol orientation*:: The symbol orientation rotating the icon clockwise.
|
||||
|
||||
*Symbol size*:: The radius of the symbol size, in pixels.
|
||||
|
|
|
@ -38,6 +38,8 @@ exports[`Renders PolygonIcon with correct styles when not line only or not point
|
|||
exports[`Renders SymbolIcon with correct styles when isPointOnly and symbolId provided 1`] = `
|
||||
<SymbolIcon
|
||||
fill="#ff0000"
|
||||
stroke="rgb(106,173,213)"
|
||||
strokeWidth="1px"
|
||||
symbolId="airfield-15"
|
||||
/>
|
||||
`;
|
||||
|
|
|
@ -15,31 +15,35 @@ export class SymbolIcon extends Component {
|
|||
imgDataUrl: undefined,
|
||||
prevSymbolId: undefined,
|
||||
prevFill: undefined,
|
||||
prevStroke: undefined,
|
||||
prevStrokeWidth: undefined,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
this._loadSymbol(this.props.symbolId, this.props.fill);
|
||||
this._loadSymbol(this.props.symbolId, this.props.fill, this.props.stroke, this.props.strokeWidth);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this._loadSymbol(this.props.symbolId, this.props.fill);
|
||||
this._loadSymbol(this.props.symbolId, this.props.fill, this.props.stroke, this.props.strokeWidth);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
async _loadSymbol(nextSymbolId, nextFill) {
|
||||
async _loadSymbol(nextSymbolId, nextFill, nextStroke, nextStrokeWidth) {
|
||||
if (nextSymbolId === this.state.prevSymbolId
|
||||
&& nextFill === this.state.prevFill) {
|
||||
&& nextFill === this.state.prevFill
|
||||
&& nextStroke === this.state.prevStroke
|
||||
&& nextStrokeWidth === this.state.prevStrokeWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
let imgDataUrl;
|
||||
try {
|
||||
const svg = getMakiSymbolSvg(nextSymbolId);
|
||||
const styledSvg = await styleSvg(svg, nextFill);
|
||||
const styledSvg = await styleSvg(svg, nextFill, nextStroke, nextStrokeWidth);
|
||||
imgDataUrl = buildSrcUrl(styledSvg);
|
||||
} catch (error) {
|
||||
// ignore failures - component will just not display an icon
|
||||
|
@ -49,7 +53,9 @@ export class SymbolIcon extends Component {
|
|||
this.setState({
|
||||
imgDataUrl,
|
||||
prevSymbolId: nextSymbolId,
|
||||
prevFill: nextFill
|
||||
prevFill: nextFill,
|
||||
prevStroke: nextStroke,
|
||||
prevStrokeWidth: nextStrokeWidth
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +74,6 @@ export class SymbolIcon extends Component {
|
|||
SymbolIcon.propTypes = {
|
||||
symbolId: PropTypes.string.isRequired,
|
||||
fill: PropTypes.string.isRequired,
|
||||
stroke: PropTypes.string.isRequired,
|
||||
strokeWidth: PropTypes.string.isRequired
|
||||
};
|
||||
|
|
|
@ -75,6 +75,8 @@ export class VectorIcon extends Component {
|
|||
<SymbolIcon
|
||||
symbolId={this.props.symbolId}
|
||||
fill={style.fill}
|
||||
stroke={style.stroke}
|
||||
strokeWidth={style.strokeWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { VectorStyleSymbolEditor } from './vector_style_symbol_editor';
|
|||
import { OrientationEditor } from './orientation/orientation_editor';
|
||||
import { getDefaultDynamicProperties, getDefaultStaticProperties } from '../../vector_style_defaults';
|
||||
import { VECTOR_SHAPE_TYPES } from '../../../sources/vector_feature_types';
|
||||
import { SYMBOLIZE_AS_CIRCLE } from '../../vector_constants';
|
||||
import { SYMBOLIZE_AS_ICON } from '../../vector_constants';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SYMBOL_OPTIONS } from '../../symbol_utils';
|
||||
|
||||
|
@ -140,23 +140,8 @@ export class VectorStyleEditor extends Component {
|
|||
}
|
||||
|
||||
_renderPointProperties() {
|
||||
let lineColor;
|
||||
let lineWidth;
|
||||
let iconOrientation;
|
||||
if (this.props.styleProperties.symbol.options.symbolizeAs === SYMBOLIZE_AS_CIRCLE) {
|
||||
lineColor = (
|
||||
<Fragment>
|
||||
{this._renderLineColor()}
|
||||
<EuiSpacer size="m" />
|
||||
</Fragment>
|
||||
);
|
||||
lineWidth = (
|
||||
<Fragment>
|
||||
{this._renderLineWidth()}
|
||||
<EuiSpacer size="m" />
|
||||
</Fragment>
|
||||
);
|
||||
} else {
|
||||
if (this.props.styleProperties.symbol.options.symbolizeAs === SYMBOLIZE_AS_ICON) {
|
||||
iconOrientation = (
|
||||
<Fragment>
|
||||
<OrientationEditor
|
||||
|
@ -185,9 +170,11 @@ export class VectorStyleEditor extends Component {
|
|||
{this._renderFillColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{lineColor}
|
||||
{this._renderLineColor()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{lineWidth}
|
||||
{this._renderLineWidth()}
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
{iconOrientation}
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ export function VectorStyleSymbolEditor({ styleOptions, handlePropertyChange, sy
|
|||
<SymbolIcon
|
||||
symbolId={value}
|
||||
fill={isDarkMode ? 'rgb(223, 229, 239)' : 'rgb(52, 55, 65)'}
|
||||
stroke={isDarkMode ? 'rgb(255, 255, 255)' : 'rgb(0, 0, 0)'}
|
||||
strokeWidth={'1px'}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
|
|
|
@ -62,11 +62,19 @@ export function buildSrcUrl(svgString) {
|
|||
return domUrl.createObjectURL(svg);
|
||||
}
|
||||
|
||||
export async function styleSvg(svgString, fill) {
|
||||
export async function styleSvg(svgString, fill, stroke, strokeWidth) {
|
||||
const svgXml = await parseXmlString(svgString);
|
||||
let style = '';
|
||||
if (fill) {
|
||||
svgXml.svg.$.style = `fill: ${fill};`;
|
||||
style += `fill:${fill};`;
|
||||
}
|
||||
if (stroke) {
|
||||
style += `stroke:${stroke};`;
|
||||
}
|
||||
if (strokeWidth) {
|
||||
style += `stroke-width:${strokeWidth};`;
|
||||
}
|
||||
if (style) svgXml.svg.$.style = style;
|
||||
const builder = new xml2js.Builder();
|
||||
return builder.buildObject(svgXml);
|
||||
}
|
||||
|
|
|
@ -14,16 +14,30 @@ describe('getMakiSymbolSvg', () => {
|
|||
});
|
||||
|
||||
describe('styleSvg', () => {
|
||||
it('Should not add style property when fill not provided', async () => {
|
||||
it('Should not add style property when style not provided', async () => {
|
||||
const unstyledSvgString = '<svg version="1.1" width="11px" height="11px" viewBox="0 0 11 11"><path/></svg>';
|
||||
const styledSvg = await styleSvg(unstyledSvgString);
|
||||
expect(styledSvg.split('\n')[1]).toBe('<svg version=\"1.1\" width=\"11px\" height=\"11px\" viewBox=\"0 0 11 11\">');
|
||||
});
|
||||
|
||||
it('Should add style property to svg element', async () => {
|
||||
it('Should add fill style property to svg element', async () => {
|
||||
const unstyledSvgString = '<svg version="1.1" width="11px" height="11px" viewBox="0 0 11 11"><path/></svg>';
|
||||
const styledSvg = await styleSvg(unstyledSvgString, 'red');
|
||||
// eslint-disable-next-line max-len
|
||||
expect(styledSvg.split('\n')[1]).toBe('<svg version=\"1.1\" width=\"11px\" height=\"11px\" viewBox=\"0 0 11 11\" style=\"fill: red;\">');
|
||||
expect(styledSvg.split('\n')[1]).toBe('<svg version=\"1.1\" width=\"11px\" height=\"11px\" viewBox=\"0 0 11 11\" style=\"fill:red;\">');
|
||||
});
|
||||
|
||||
it('Should add stroke style property to svg element', async () => {
|
||||
const unstyledSvgString = '<svg version="1.1" width="11px" height="11px" viewBox="0 0 11 11"><path/></svg>';
|
||||
const styledSvg = await styleSvg(unstyledSvgString, 'red', 'white');
|
||||
// eslint-disable-next-line max-len
|
||||
expect(styledSvg.split('\n')[1]).toBe('<svg version=\"1.1\" width=\"11px\" height=\"11px\" viewBox=\"0 0 11 11\" style=\"fill:red;stroke:white;\">');
|
||||
});
|
||||
|
||||
it('Should add stroke-width style property to svg element', async () => {
|
||||
const unstyledSvgString = '<svg version="1.1" width="11px" height="11px" viewBox="0 0 11 11"><path/></svg>';
|
||||
const styledSvg = await styleSvg(unstyledSvgString, 'red', 'white', '2px');
|
||||
// eslint-disable-next-line max-len
|
||||
expect(styledSvg.split('\n')[1]).toBe('<svg version=\"1.1\" width=\"11px\" height=\"11px\" viewBox=\"0 0 11 11\" style=\"fill:red;stroke:white;stroke-width:2px;\">');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -546,8 +546,12 @@ export class VectorStyle extends AbstractStyle {
|
|||
const symbolId = this._descriptor.properties.symbol.options.symbolId;
|
||||
mbMap.setLayoutProperty(symbolLayerId, 'icon-anchor', getMakiSymbolAnchor(symbolId));
|
||||
const color = this._getMBColor(this._descriptor.properties.fillColor);
|
||||
const haloColor = this._getMBColor(this._descriptor.properties.lineColor);
|
||||
const haloWidth = this._getMbSize(this._descriptor.properties.lineWidth);
|
||||
// icon-color is only supported on SDF icons.
|
||||
mbMap.setPaintProperty(symbolLayerId, 'icon-color', color);
|
||||
mbMap.setPaintProperty(symbolLayerId, 'icon-halo-color', haloColor);
|
||||
mbMap.setPaintProperty(symbolLayerId, 'icon-halo-width', haloWidth);
|
||||
mbMap.setPaintProperty(symbolLayerId, 'icon-opacity', alpha);
|
||||
|
||||
// circle sizing is by radius
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue