[Canvas] Expression progress (#104457)
* Added `expression_progress` plugin. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
|
@ -116,5 +116,6 @@ pageLoadAssetSize:
|
|||
expressionRepeatImage: 22341
|
||||
expressionImage: 19288
|
||||
expressionMetric: 22238
|
||||
expressionShape: 30033
|
||||
expressionShape: 34008
|
||||
interactiveSetup: 18532
|
||||
|
|
@ -8,4 +8,10 @@
|
|||
|
||||
export const PLUGIN_ID = 'expressionShape';
|
||||
export const PLUGIN_NAME = 'expressionShape';
|
||||
|
||||
export const SVG = 'SVG';
|
||||
export const CSS = 'CSS';
|
||||
export const FONT_FAMILY = '`font-family`';
|
||||
export const FONT_WEIGHT = '`font-weight`';
|
||||
export const BOOLEAN_TRUE = '`true`';
|
||||
export const BOOLEAN_FALSE = '`false`';
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
*/
|
||||
|
||||
export { shapeFunction } from './shape_function';
|
||||
export { progressFunction } from './progress_function';
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ExecutionContext } from '../../../expressions';
|
||||
import { functionWrapper, fontStyle } from '../../../presentation_util/common/lib';
|
||||
import { progressFunction, errors } from './progress_function';
|
||||
|
||||
describe('progress', () => {
|
||||
const fn = functionWrapper(progressFunction);
|
||||
const value = 0.33;
|
||||
|
||||
it('returns a render as progress', () => {
|
||||
const result = fn(0.2, {}, {} as ExecutionContext);
|
||||
expect(result).toHaveProperty('type', 'render');
|
||||
expect(result).toHaveProperty('as', 'progress');
|
||||
});
|
||||
|
||||
it('sets the progress to context', () => {
|
||||
const result = fn(0.58, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('value', 0.58);
|
||||
});
|
||||
|
||||
it(`throws when context is outside of the valid range`, async () => {
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await fn(3, {}, {} as ExecutionContext);
|
||||
} catch (e: any) {
|
||||
expect(e.message).toEqual(errors.invalidValue(3).message);
|
||||
}
|
||||
});
|
||||
|
||||
describe('args', () => {
|
||||
describe('shape', () => {
|
||||
it('sets the progress element shape', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
shape: 'wheel',
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value).toHaveProperty('shape', 'wheel');
|
||||
});
|
||||
|
||||
it(`defaults to 'gauge'`, () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('shape', 'gauge');
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', () => {
|
||||
it('sets the maximum value', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
max: 2,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value).toHaveProperty('max', 2);
|
||||
});
|
||||
|
||||
it('defaults to 1', () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('max', 1);
|
||||
});
|
||||
|
||||
it('throws if max <= 0', async () => {
|
||||
expect.assertions(1);
|
||||
try {
|
||||
await fn(value, { max: -0.5 }, {} as ExecutionContext);
|
||||
} catch (e: any) {
|
||||
expect(e.message).toEqual(errors.invalidMaxValue(-0.5).message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueColor', () => {
|
||||
it('sets the color of the progress bar', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
valueColor: '#000000',
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value).toHaveProperty('valueColor', '#000000');
|
||||
});
|
||||
|
||||
it(`defaults to '#1785b0'`, () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('valueColor', '#1785b0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('barColor', () => {
|
||||
it('sets the color of the background bar', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
barColor: '#FFFFFF',
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value).toHaveProperty('barColor', '#FFFFFF');
|
||||
});
|
||||
|
||||
it(`defaults to '#f0f0f0'`, () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('barColor', '#f0f0f0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueWeight', () => {
|
||||
it('sets the thickness of the bars', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
valuWeight: 100,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
|
||||
expect(result.value).toHaveProperty('valuWeight', 100);
|
||||
});
|
||||
|
||||
it(`defaults to 20`, () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('barWeight', 20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('barWeight', () => {
|
||||
it('sets the thickness of the bars', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
barWeight: 50,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
|
||||
expect(result.value).toHaveProperty('barWeight', 50);
|
||||
});
|
||||
|
||||
it(`defaults to 20`, () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('barWeight', 20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('label', () => {
|
||||
it('sets the label of the progress', () => {
|
||||
const result = fn(value, { label: 'foo' }, {} as ExecutionContext);
|
||||
|
||||
expect(result.value).toHaveProperty('label', 'foo');
|
||||
});
|
||||
|
||||
it('hides the label if false', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
label: false,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value).toHaveProperty('label', '');
|
||||
});
|
||||
|
||||
it('defaults to true which sets the context as the label', () => {
|
||||
const result = fn(value, {}, {} as ExecutionContext);
|
||||
expect(result.value).toHaveProperty('label', '0.33');
|
||||
});
|
||||
});
|
||||
|
||||
describe('font', () => {
|
||||
it('sets the font style for the label', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
font: fontStyle,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
|
||||
expect(result.value).toHaveProperty('font');
|
||||
expect(Object.keys(result.value.font).sort()).toEqual(Object.keys(fontStyle).sort());
|
||||
expect(Object.keys(result.value.font.spec).sort()).toEqual(
|
||||
Object.keys(fontStyle.spec).sort()
|
||||
);
|
||||
});
|
||||
|
||||
it('sets fill to color', () => {
|
||||
const result = fn(
|
||||
value,
|
||||
{
|
||||
font: fontStyle,
|
||||
},
|
||||
{} as ExecutionContext
|
||||
);
|
||||
expect(result.value.font.spec).toHaveProperty('fill', fontStyle.spec.color);
|
||||
});
|
||||
|
||||
// TODO: write test when using an instance of the interpreter
|
||||
// it("sets a default style for the label when not provided", () => {});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { get } from 'lodash';
|
||||
import { Style, openSans } from '../../../expressions/common';
|
||||
import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../constants';
|
||||
import { ExpressionProgressFunction, Progress } from '../types';
|
||||
|
||||
export const strings = {
|
||||
help: i18n.translate('expressionShape.functions.progressHelpText', {
|
||||
defaultMessage: 'Configures a progress element.',
|
||||
}),
|
||||
args: {
|
||||
barColor: i18n.translate('expressionShape.functions.progress.args.barColorHelpText', {
|
||||
defaultMessage: 'The color of the background bar.',
|
||||
}),
|
||||
barWeight: i18n.translate('expressionShape.functions.progress.args.barWeightHelpText', {
|
||||
defaultMessage: 'The thickness of the background bar.',
|
||||
}),
|
||||
font: i18n.translate('expressionShape.functions.progress.args.fontHelpText', {
|
||||
defaultMessage:
|
||||
'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
|
||||
values: {
|
||||
CSS,
|
||||
FONT_FAMILY,
|
||||
FONT_WEIGHT,
|
||||
},
|
||||
}),
|
||||
label: i18n.translate('expressionShape.functions.progress.args.labelHelpText', {
|
||||
defaultMessage:
|
||||
'To show or hide the label, use {BOOLEAN_TRUE} or {BOOLEAN_FALSE}. Alternatively, provide a string to display as a label.',
|
||||
values: {
|
||||
BOOLEAN_TRUE,
|
||||
BOOLEAN_FALSE,
|
||||
},
|
||||
}),
|
||||
max: i18n.translate('expressionShape.functions.progress.args.maxHelpText', {
|
||||
defaultMessage: 'The maximum value of the progress element.',
|
||||
}),
|
||||
shape: i18n.translate('expressionShape.functions.progress.args.shapeHelpText', {
|
||||
defaultMessage: `Select {list}, or {end}.`,
|
||||
values: {
|
||||
list: Object.values(Progress)
|
||||
.slice(0, -1)
|
||||
.map((shape) => `\`"${shape}"\``)
|
||||
.join(', '),
|
||||
end: `\`"${Object.values(Progress).slice(-1)[0]}"\``,
|
||||
},
|
||||
}),
|
||||
valueColor: i18n.translate('expressionShape.functions.progress.args.valueColorHelpText', {
|
||||
defaultMessage: 'The color of the progress bar.',
|
||||
}),
|
||||
valueWeight: i18n.translate('expressionShape.functions.progress.args.valueWeightHelpText', {
|
||||
defaultMessage: 'The thickness of the progress bar.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const errors = {
|
||||
invalidMaxValue: (max: number) =>
|
||||
new Error(
|
||||
i18n.translate('expressionShape.functions.progress.invalidMaxValueErrorMessage', {
|
||||
defaultMessage: "Invalid {arg} value: '{max, number}'. '{arg}' must be greater than 0",
|
||||
values: {
|
||||
arg: 'max',
|
||||
max,
|
||||
},
|
||||
})
|
||||
),
|
||||
invalidValue: (value: number, max: number = 1) =>
|
||||
new Error(
|
||||
i18n.translate('expressionShape.functions.progress.invalidValueErrorMessage', {
|
||||
defaultMessage:
|
||||
"Invalid value: '{value, number}'. Value must be between 0 and {max, number}",
|
||||
values: {
|
||||
value,
|
||||
max,
|
||||
},
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
export const progressFunction: ExpressionProgressFunction = () => {
|
||||
const { help, args: argHelp } = strings;
|
||||
|
||||
return {
|
||||
name: 'progress',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
inputTypes: ['number'],
|
||||
help,
|
||||
args: {
|
||||
shape: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: argHelp.shape,
|
||||
options: Object.values(Progress),
|
||||
default: 'gauge',
|
||||
},
|
||||
barColor: {
|
||||
types: ['string'],
|
||||
help: argHelp.barColor,
|
||||
default: `#f0f0f0`,
|
||||
},
|
||||
barWeight: {
|
||||
types: ['number'],
|
||||
help: argHelp.barWeight,
|
||||
default: 20,
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: argHelp.font,
|
||||
default: `{font size=24 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
label: {
|
||||
types: ['boolean', 'string'],
|
||||
help: argHelp.label,
|
||||
default: true,
|
||||
},
|
||||
max: {
|
||||
types: ['number'],
|
||||
help: argHelp.max,
|
||||
default: 1,
|
||||
},
|
||||
valueColor: {
|
||||
types: ['string'],
|
||||
help: argHelp.valueColor,
|
||||
default: `#1785b0`,
|
||||
},
|
||||
valueWeight: {
|
||||
types: ['number'],
|
||||
help: argHelp.valueWeight,
|
||||
default: 20,
|
||||
},
|
||||
},
|
||||
fn: (value, args) => {
|
||||
if (args.max <= 0) {
|
||||
throw errors.invalidMaxValue(args.max);
|
||||
}
|
||||
if (value > args.max || value < 0) {
|
||||
throw errors.invalidValue(value, args.max);
|
||||
}
|
||||
|
||||
let label = '';
|
||||
if (args.label) {
|
||||
label = typeof args.label === 'string' ? args.label : `${value}`;
|
||||
}
|
||||
|
||||
let font: Style = {} as Style;
|
||||
|
||||
if (get(args, 'font.spec')) {
|
||||
font = { ...args.font };
|
||||
font.spec.fill = args.font.spec.color; // SVG <text> uses fill for font color
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'progress',
|
||||
value: {
|
||||
value,
|
||||
...args,
|
||||
label,
|
||||
font,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
|
@ -9,4 +9,4 @@
|
|||
export * from './constants';
|
||||
export * from './types';
|
||||
|
||||
export { getAvailableShapes } from './lib/available_shapes';
|
||||
export { getAvailableShapes, getAvailableProgressShapes } from './lib/available_shapes';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Shape } from '../types';
|
||||
import { Shape, Progress } from '../types';
|
||||
|
||||
export const getAvailableShapes = () => Object.values(Shape);
|
||||
export const getAvailableProgressShapes = () => Object.values(Progress);
|
||||
|
|
13
src/plugins/expression_shape/common/lib/get_id.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
export function getId(type: string): string {
|
||||
return `${type}-${uuid()}`;
|
||||
}
|
|
@ -8,3 +8,4 @@
|
|||
|
||||
export * from './view_box';
|
||||
export * from './available_shapes';
|
||||
export * from './get_id';
|
||||
|
|
|
@ -9,7 +9,10 @@
|
|||
import { ParentNodeParams, ViewBoxParams } from '../types';
|
||||
|
||||
export function viewBoxToString(viewBox?: ViewBoxParams): undefined | string {
|
||||
if (!viewBox) return;
|
||||
if (!viewBox) {
|
||||
return;
|
||||
}
|
||||
|
||||
return `${viewBox?.minX} ${viewBox?.minY} ${viewBox?.width} ${viewBox?.height}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { ExpressionFunctionDefinition } from 'src/plugins/expressions';
|
||||
|
||||
import { ExpressionFunctionDefinition, ExpressionValueRender, Style } from '../../../expressions';
|
||||
|
||||
export enum Shape {
|
||||
ARROW = 'arrow',
|
||||
|
@ -44,3 +45,36 @@ export type ExpressionShapeFunction = () => ExpressionFunctionDefinition<
|
|||
Arguments,
|
||||
Output
|
||||
>;
|
||||
|
||||
export enum Progress {
|
||||
GAUGE = 'gauge',
|
||||
HORIZONTAL_BAR = 'horizontalBar',
|
||||
HORIZONTAL_PILL = 'horizontalPill',
|
||||
SEMICIRCLE = 'semicircle',
|
||||
UNICORN = 'unicorn',
|
||||
VERTICAL_BAR = 'verticalBar',
|
||||
VERTICAL_PILL = 'verticalPill',
|
||||
WHEEL = 'wheel',
|
||||
}
|
||||
|
||||
export interface ProgressArguments {
|
||||
barColor: string;
|
||||
barWeight: number;
|
||||
font: Style;
|
||||
label: boolean | string;
|
||||
max: number;
|
||||
shape: Progress;
|
||||
valueColor: string;
|
||||
valueWeight: number;
|
||||
}
|
||||
|
||||
export type ProgressOutput = ProgressArguments & {
|
||||
value: number;
|
||||
};
|
||||
|
||||
export type ExpressionProgressFunction = () => ExpressionFunctionDefinition<
|
||||
'progress',
|
||||
number,
|
||||
ProgressArguments,
|
||||
ExpressionValueRender<ProgressArguments>
|
||||
>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { Shape } from './expression_functions';
|
||||
import { Shape, ProgressOutput as Arguments } from './expression_functions';
|
||||
|
||||
export type OriginString = 'bottom' | 'left' | 'top' | 'right';
|
||||
export interface ShapeRendererConfig {
|
||||
|
@ -33,3 +33,5 @@ export interface ViewBoxParams {
|
|||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export type ProgressRendererConfig = Arguments;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { lazy } from 'react';
|
||||
|
||||
export const LazyProgressComponent = lazy(() => import('./progress_component'));
|
||||
export const LazyProgressDrawer = lazy(() => import('./progress_drawer'));
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { CSSProperties, RefCallback, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useResizeObserver } from '@elastic/eui';
|
||||
import { IInterpreterRenderHandlers } from '../../../../expressions';
|
||||
import { NodeDimensions, ProgressRendererConfig } from '../../../common/types';
|
||||
import { ShapeRef, SvgConfig, SvgTextAttributes } from '../reusable/types';
|
||||
import { getShapeContentElement } from '../reusable/shape_factory';
|
||||
import { withSuspense } from '../../../../presentation_util/public';
|
||||
import { getTextAttributes, getViewBox } from './utils';
|
||||
import { getId } from '../../../common/lib';
|
||||
import { getDefaultShapeData } from '../reusable';
|
||||
import { LazyProgressDrawer } from '../..';
|
||||
|
||||
const ProgressDrawer = withSuspense(LazyProgressDrawer);
|
||||
|
||||
interface ProgressComponentProps extends ProgressRendererConfig {
|
||||
onLoaded: IInterpreterRenderHandlers['done'];
|
||||
parentNode: HTMLElement;
|
||||
}
|
||||
|
||||
function ProgressComponent({
|
||||
onLoaded,
|
||||
parentNode,
|
||||
shape: shapeType,
|
||||
value,
|
||||
max,
|
||||
valueColor,
|
||||
barColor,
|
||||
valueWeight,
|
||||
barWeight,
|
||||
label,
|
||||
font,
|
||||
}: ProgressComponentProps) {
|
||||
const parentNodeDimensions = useResizeObserver(parentNode);
|
||||
const [dimensions, setDimensions] = useState<NodeDimensions>({
|
||||
width: parentNode.offsetWidth,
|
||||
height: parentNode.offsetHeight,
|
||||
});
|
||||
const [shapeData, setShapeData] = useState<SvgConfig>(getDefaultShapeData());
|
||||
const shapeRef = useCallback<RefCallback<ShapeRef>>((node) => {
|
||||
if (node !== null) {
|
||||
setShapeData(node.getData());
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [totalLength, setTotalLength] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
setDimensions({
|
||||
width: parentNode.offsetWidth,
|
||||
height: parentNode.offsetHeight,
|
||||
});
|
||||
onLoaded();
|
||||
}, [onLoaded, parentNode, parentNodeDimensions]);
|
||||
|
||||
const progressRef = useRef<
|
||||
SVGCircleElement & SVGPathElement & SVGPolygonElement & SVGRectElement
|
||||
>(null);
|
||||
const textRef = useRef<SVGTextElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setTotalLength(progressRef.current ? progressRef.current.getTotalLength() : 0);
|
||||
}, [shapeType, shapeData, progressRef]);
|
||||
|
||||
const BarProgress = shapeData.shapeType ? getShapeContentElement(shapeData.shapeType) : null;
|
||||
|
||||
const shapeContentAttributes = {
|
||||
fill: 'none',
|
||||
stroke: barColor,
|
||||
strokeWidth: `${barWeight}px`,
|
||||
};
|
||||
|
||||
const percent = value / max;
|
||||
const to = totalLength * (1 - percent);
|
||||
|
||||
const barProgressAttributes = {
|
||||
...shapeData.shapeProps,
|
||||
fill: 'none',
|
||||
stroke: valueColor,
|
||||
strokeWidth: `${valueWeight}px`,
|
||||
strokeDasharray: totalLength,
|
||||
strokeDashoffset: Math.max(0, to),
|
||||
};
|
||||
|
||||
const { width: labelWidth, height: labelHeight } = textRef.current
|
||||
? textRef.current.getBBox()
|
||||
: { width: 0, height: 0 };
|
||||
|
||||
const offset = Math.max(valueWeight, barWeight);
|
||||
|
||||
const updatedTextAttributes = shapeData.textAttributes
|
||||
? getTextAttributes(shapeType, shapeData.textAttributes, offset, label)
|
||||
: {};
|
||||
|
||||
const textAttributes: SvgTextAttributes = {
|
||||
style: font.spec as CSSProperties,
|
||||
...updatedTextAttributes,
|
||||
};
|
||||
|
||||
const updatedViewBox = getViewBox(shapeType, shapeData.viewBox, offset, labelWidth, labelHeight);
|
||||
const shapeAttributes = {
|
||||
id: getId('svg'),
|
||||
...(dimensions || {}),
|
||||
viewBox: updatedViewBox,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shapeAligner">
|
||||
<ProgressDrawer
|
||||
shapeType={shapeType}
|
||||
shapeContentAttributes={{ ...shapeContentAttributes, ref: progressRef }}
|
||||
shapeAttributes={shapeAttributes}
|
||||
textAttributes={{ ...textAttributes, ref: textRef }}
|
||||
ref={shapeRef}
|
||||
>
|
||||
{BarProgress && <BarProgress {...barProgressAttributes} />}
|
||||
</ProgressDrawer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// default export required for React.Lazy
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { ProgressComponent as default };
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React, { Ref } from 'react';
|
||||
import { ShapeDrawer, ShapeRef, ShapeDrawerComponentProps } from '../reusable';
|
||||
import { getShape } from './shapes';
|
||||
|
||||
const ProgressDrawerComponent = React.forwardRef(
|
||||
(props: ShapeDrawerComponentProps, ref: Ref<ShapeRef>) => (
|
||||
<ShapeDrawer {...props} ref={ref} getShape={getShape} />
|
||||
)
|
||||
);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { ProgressDrawerComponent as default };
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const Gauge = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 120,
|
||||
height: 120,
|
||||
},
|
||||
shapeProps: {
|
||||
d: 'M 15 100 A 60 60 0 1 1 105 100',
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
textAttributes: {
|
||||
x: '60',
|
||||
y: '60',
|
||||
textAnchor: 'middle',
|
||||
dominantBaseline: 'central',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const HorizontalBar = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 208,
|
||||
height: 1,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 0 1 L 200 1',
|
||||
},
|
||||
textAttributes: {
|
||||
x: 208,
|
||||
y: 0,
|
||||
textAnchor: 'start',
|
||||
dominantBaseline: 'central',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const HorizontalPill = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 208,
|
||||
height: 1,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 0 1 L 200 1',
|
||||
strokeLinecap: 'round',
|
||||
},
|
||||
textAttributes: {
|
||||
x: 208,
|
||||
y: 0,
|
||||
textAnchor: 'start',
|
||||
dominantBaseline: 'central',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ShapeType } from '../../reusable';
|
||||
import { Gauge as gauge } from './gauge';
|
||||
import { HorizontalBar as horizontalBar } from './horizontal_bar';
|
||||
import { HorizontalPill as horizontalPill } from './horizontal_pill';
|
||||
import { Semicircle as semicircle } from './semicircle';
|
||||
import { Unicorn as unicorn } from './unicorn';
|
||||
import { VerticalBar as verticalBar } from './vertical_bar';
|
||||
import { VerticalPill as verticalPill } from './vertical_pill';
|
||||
import { Wheel as wheel } from './wheel';
|
||||
|
||||
const shapes: { [key: string]: ShapeType } = {
|
||||
gauge,
|
||||
horizontalBar,
|
||||
horizontalPill,
|
||||
semicircle,
|
||||
unicorn,
|
||||
verticalBar,
|
||||
verticalPill,
|
||||
wheel,
|
||||
};
|
||||
|
||||
export const getShape = (shapeType: string) => shapes[shapeType];
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const Semicircle = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 120,
|
||||
height: 60,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 0 60 A 60 60 0 1 1 120 60',
|
||||
},
|
||||
textAttributes: {
|
||||
x: 60,
|
||||
y: 60,
|
||||
textAnchor: 'middle',
|
||||
dy: '-1',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const Unicorn = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 200,
|
||||
height: 200,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d:
|
||||
'M 123 189 C 93 141 129 126 102 96 L 78 102 L 48 117 L 42 129 Q 30 132 21 126 L 18 114 L 27 90 L 42 72 L 48 57 L 3 6 L 57 42 L 63 33 L 60 15 L 69 27 L 69 15 L 84 27 Q 162 36 195 108 Q 174 159 123 189 Z',
|
||||
},
|
||||
textAttributes: {
|
||||
x: '0',
|
||||
y: '200',
|
||||
textAnchor: 'start',
|
||||
dominantBaseline: 'text-after-edge',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const VerticalBar = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: -8,
|
||||
width: 1,
|
||||
height: 208,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 1 200 L 1 0',
|
||||
},
|
||||
textAttributes: {
|
||||
x: '0',
|
||||
y: '-8',
|
||||
textAnchor: 'middle',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const VerticalPill = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: -8,
|
||||
width: 1,
|
||||
height: 208,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 1 200 L 1 0',
|
||||
strokeLinecap: 'round',
|
||||
},
|
||||
textAttributes: {
|
||||
x: '0',
|
||||
y: '-8',
|
||||
textAnchor: 'middle',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { createShape } from '../../reusable/shape_factory';
|
||||
import { SvgElementTypes } from '../../reusable';
|
||||
|
||||
export const Wheel = createShape({
|
||||
viewBox: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
width: 120,
|
||||
height: 120,
|
||||
},
|
||||
shapeType: SvgElementTypes.path,
|
||||
shapeProps: {
|
||||
d: 'M 60 0 A 60 60 0 1 1 60 120 A 60 60 0 1 1 60 0 Z',
|
||||
},
|
||||
textAttributes: {
|
||||
x: '60',
|
||||
y: '60',
|
||||
textAnchor: 'middle',
|
||||
dominantBaseline: 'central',
|
||||
},
|
||||
});
|
146
src/plugins/expression_shape/public/components/progress/utils.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Progress, ViewBoxParams } from '../../../common';
|
||||
import { SvgTextAttributes } from '../reusable';
|
||||
|
||||
type GetViewBox = (
|
||||
shapeType: Progress,
|
||||
initialViewBox: ViewBoxParams,
|
||||
offset: number,
|
||||
labelWidth: number,
|
||||
labelHeight: number
|
||||
) => ViewBoxParams;
|
||||
|
||||
type GetViewBoxArguments = Parameters<GetViewBox>;
|
||||
type GetViewBoxParam = (...args: GetViewBoxArguments) => number;
|
||||
|
||||
const getMinX: GetViewBoxParam = (shapeType, viewBox, offset = 0) => {
|
||||
let { minX } = viewBox;
|
||||
|
||||
if (shapeType !== Progress.HORIZONTAL_BAR) {
|
||||
minX -= offset / 2;
|
||||
}
|
||||
|
||||
return minX;
|
||||
};
|
||||
|
||||
const getMinY: GetViewBoxParam = (shapeType, viewBox, offset = 0, labelWidth, labelHeight = 0) => {
|
||||
let { minY } = viewBox;
|
||||
|
||||
if (shapeType === Progress.SEMICIRCLE) {
|
||||
minY -= offset / 2;
|
||||
}
|
||||
if (shapeType !== Progress.SEMICIRCLE && shapeType !== Progress.VERTICAL_BAR) {
|
||||
minY -= offset / 2;
|
||||
}
|
||||
if (shapeType === Progress.VERTICAL_BAR || shapeType === Progress.VERTICAL_PILL) {
|
||||
minY -= labelHeight;
|
||||
}
|
||||
|
||||
return minY;
|
||||
};
|
||||
|
||||
const getWidth: GetViewBoxParam = (shapeType, viewBox, offset = 0, labelWidth = 0) => {
|
||||
let { width } = viewBox;
|
||||
|
||||
if (shapeType !== Progress.HORIZONTAL_BAR) {
|
||||
width += offset;
|
||||
}
|
||||
if (shapeType === Progress.HORIZONTAL_BAR || shapeType === Progress.HORIZONTAL_PILL) {
|
||||
width += labelWidth;
|
||||
}
|
||||
|
||||
return width;
|
||||
};
|
||||
|
||||
const getHeight: GetViewBoxParam = (
|
||||
shapeType,
|
||||
viewBox,
|
||||
offset = 0,
|
||||
labelWidth = 0,
|
||||
labelHeight = 0
|
||||
) => {
|
||||
let { height } = viewBox;
|
||||
|
||||
if (shapeType === Progress.SEMICIRCLE) {
|
||||
height += offset / 2;
|
||||
}
|
||||
if (shapeType !== Progress.SEMICIRCLE && shapeType !== Progress.VERTICAL_BAR) {
|
||||
height += offset;
|
||||
}
|
||||
if (shapeType === Progress.VERTICAL_BAR || shapeType === Progress.VERTICAL_PILL) {
|
||||
height += labelHeight;
|
||||
}
|
||||
|
||||
return height;
|
||||
};
|
||||
|
||||
const updateMinxAndWidthIfNecessary = (
|
||||
shapeType: Progress,
|
||||
labelWidth: number,
|
||||
minX: number,
|
||||
width: number
|
||||
) => {
|
||||
if (
|
||||
(shapeType === Progress.VERTICAL_BAR || shapeType === Progress.VERTICAL_PILL) &&
|
||||
labelWidth > width
|
||||
) {
|
||||
minX = -labelWidth / 2;
|
||||
width = labelWidth;
|
||||
}
|
||||
return [minX, width];
|
||||
};
|
||||
|
||||
export const getViewBox: GetViewBox = function (
|
||||
shapeType,
|
||||
viewBox,
|
||||
offset = 0,
|
||||
labelWidth = 0,
|
||||
labelHeight = 0
|
||||
): ViewBoxParams {
|
||||
const args: GetViewBoxArguments = [shapeType, viewBox, offset, labelWidth, labelHeight];
|
||||
const minX = getMinX(...args);
|
||||
const minY = getMinY(...args);
|
||||
const width = getWidth(...args);
|
||||
const height = getHeight(...args);
|
||||
const [updatedMinX, updatedWidth] = updateMinxAndWidthIfNecessary(
|
||||
shapeType,
|
||||
labelWidth,
|
||||
minX,
|
||||
width
|
||||
);
|
||||
return { minX: updatedMinX, minY, width: updatedWidth, height };
|
||||
};
|
||||
|
||||
export function getTextAttributes(
|
||||
shapeType: Progress,
|
||||
textAttributes: SvgTextAttributes,
|
||||
offset: number = 0,
|
||||
label: string | boolean = ''
|
||||
) {
|
||||
if (!label) {
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
let { x, y, textContent } = textAttributes;
|
||||
|
||||
textContent = label ? label.toString() : '';
|
||||
|
||||
if (shapeType === Progress.HORIZONTAL_PILL) {
|
||||
x = parseInt(String(x)!, 10) + offset / 2;
|
||||
}
|
||||
if (shapeType === Progress.VERTICAL_PILL) {
|
||||
y = parseInt(String(y)!, 10) - offset / 2;
|
||||
}
|
||||
if (shapeType === Progress.HORIZONTAL_BAR || shapeType === Progress.HORIZONTAL_PILL) {
|
||||
x = parseInt(String(x)!, 10);
|
||||
}
|
||||
|
||||
return { x, y, textContent };
|
||||
}
|
|
@ -10,32 +10,59 @@ import React from 'react';
|
|||
import { viewBoxToString } from '../../../common/lib';
|
||||
import { ShapeProps, SvgConfig, SvgElementTypes } from './types';
|
||||
|
||||
const getShapeComponent = (svgParams: SvgConfig) =>
|
||||
function Shape({ shapeAttributes, shapeContentAttributes }: ShapeProps) {
|
||||
const { viewBox: initialViewBox, shapeProps, shapeType } = svgParams;
|
||||
export const getShapeComponent = (svgParams: SvgConfig) =>
|
||||
function Shape({
|
||||
shapeAttributes,
|
||||
shapeContentAttributes,
|
||||
children,
|
||||
textAttributes,
|
||||
}: ShapeProps) {
|
||||
const {
|
||||
viewBox: initialViewBox,
|
||||
shapeProps: defaultShapeContentAttributes,
|
||||
textAttributes: defaultTextAttributes,
|
||||
shapeType,
|
||||
} = svgParams;
|
||||
|
||||
const viewBox = shapeAttributes?.viewBox
|
||||
? viewBoxToString(shapeAttributes?.viewBox)
|
||||
: viewBoxToString(initialViewBox);
|
||||
|
||||
const SvgContentElement = getShapeContentElement(shapeType);
|
||||
|
||||
const TextElement = textAttributes
|
||||
? React.forwardRef<SVGTextElement>((props, ref) => <text {...props} ref={ref} />)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" {...{ ...(shapeAttributes || {}), viewBox }}>
|
||||
<SvgContentElement {...{ ...(shapeContentAttributes || {}), ...shapeProps }} />
|
||||
<SvgContentElement
|
||||
{...{ ...defaultShapeContentAttributes, ...(shapeContentAttributes || {}) }}
|
||||
/>
|
||||
{children}
|
||||
{TextElement && (
|
||||
<TextElement {...{ ...(defaultTextAttributes || {}), ...(textAttributes || {}) }}>
|
||||
{textAttributes?.textContent}
|
||||
</TextElement>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
function getShapeContentElement(type?: SvgElementTypes) {
|
||||
export function getShapeContentElement(type?: SvgElementTypes) {
|
||||
switch (type) {
|
||||
case SvgElementTypes.circle:
|
||||
return (props: SvgConfig['shapeProps']) => <circle {...props} />;
|
||||
return React.forwardRef<SVGCircleElement | null>((props, ref) => (
|
||||
<circle {...props} ref={ref} />
|
||||
));
|
||||
case SvgElementTypes.rect:
|
||||
return (props: SvgConfig['shapeProps']) => <rect {...props} />;
|
||||
return React.forwardRef<SVGRectElement | null>((props, ref) => <rect {...props} ref={ref} />);
|
||||
case SvgElementTypes.path:
|
||||
return (props: SvgConfig['shapeProps']) => <path {...props} />;
|
||||
return React.forwardRef<SVGPathElement | null>((props, ref) => <path {...props} ref={ref} />);
|
||||
default:
|
||||
return (props: SvgConfig['shapeProps']) => <polygon {...props} />;
|
||||
return React.forwardRef<SVGPolygonElement | null>((props, ref) => (
|
||||
<polygon {...props} ref={ref} />
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,16 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Ref, SVGProps } from 'react';
|
||||
import { Component, CSSProperties, Ref, SVGProps } from 'react';
|
||||
import { ViewBoxParams } from '../../../common/types';
|
||||
import type { ShapeType } from './shape_factory';
|
||||
|
||||
export interface ShapeProps {
|
||||
export type ShapeProps = {
|
||||
shapeAttributes?: ShapeAttributes;
|
||||
shapeContentAttributes?: ShapeContentAttributes;
|
||||
}
|
||||
shapeContentAttributes?: ShapeContentAttributes &
|
||||
SpecificShapeContentAttributes & { ref?: React.RefObject<any> };
|
||||
textAttributes?: SvgTextAttributes;
|
||||
} & Component['props'] & { ref?: React.RefObject<any> };
|
||||
|
||||
export enum SvgElementTypes {
|
||||
polygon,
|
||||
|
@ -39,36 +41,48 @@ export interface ShapeContentAttributes {
|
|||
vectorEffect?: SVGProps<SVGElement>['vectorEffect'];
|
||||
strokeMiterlimit?: SVGProps<SVGElement>['strokeMiterlimit'];
|
||||
}
|
||||
export interface SvgConfig {
|
||||
shapeType?: SvgElementTypes;
|
||||
viewBox: ViewBoxParams;
|
||||
shapeProps: ShapeContentAttributes &
|
||||
SpecificShapeContentAttributes &
|
||||
Component['props'] & { ref?: React.RefObject<any> };
|
||||
textAttributes?: SvgTextAttributes;
|
||||
}
|
||||
|
||||
interface CircleParams {
|
||||
export type SvgTextAttributes = Partial<Element> & {
|
||||
x?: SVGProps<SVGTextElement>['x'];
|
||||
y?: SVGProps<SVGTextElement>['y'];
|
||||
textAnchor?: SVGProps<SVGTextElement>['textAnchor'];
|
||||
dominantBaseline?: SVGProps<SVGTextElement>['dominantBaseline'];
|
||||
dx?: SVGProps<SVGTextElement>['dx'];
|
||||
dy?: SVGProps<SVGTextElement>['dy'];
|
||||
} & { style?: CSSProperties } & { ref?: React.RefObject<SVGTextElement> };
|
||||
|
||||
export interface CircleParams {
|
||||
r: SVGProps<SVGCircleElement>['r'];
|
||||
cx: SVGProps<SVGCircleElement>['cx'];
|
||||
cy: SVGProps<SVGCircleElement>['cy'];
|
||||
}
|
||||
|
||||
interface RectParams {
|
||||
export interface RectParams {
|
||||
x: SVGProps<SVGRectElement>['x'];
|
||||
y: SVGProps<SVGRectElement>['y'];
|
||||
width: SVGProps<SVGRectElement>['width'];
|
||||
height: SVGProps<SVGRectElement>['height'];
|
||||
}
|
||||
|
||||
interface PathParams {
|
||||
export interface PathParams {
|
||||
d: SVGProps<SVGPathElement>['d'];
|
||||
strokeLinecap?: SVGProps<SVGPathElement>['strokeLinecap'];
|
||||
}
|
||||
|
||||
interface PolygonParams {
|
||||
export interface PolygonParams {
|
||||
points?: SVGProps<SVGPolygonElement>['points'];
|
||||
strokeLinejoin?: SVGProps<SVGPolygonElement>['strokeLinejoin'];
|
||||
}
|
||||
|
||||
type SpecificShapeContentAttributes = CircleParams | RectParams | PathParams | PolygonParams;
|
||||
|
||||
export interface SvgConfig {
|
||||
shapeType?: SvgElementTypes;
|
||||
viewBox: ViewBoxParams;
|
||||
shapeProps: ShapeContentAttributes & SpecificShapeContentAttributes;
|
||||
}
|
||||
export type SpecificShapeContentAttributes = CircleParams | RectParams | PathParams | PolygonParams;
|
||||
|
||||
export type ShapeDrawerProps = {
|
||||
shapeType: string;
|
||||
|
@ -80,4 +94,6 @@ export interface ShapeRef {
|
|||
getData: () => SvgConfig;
|
||||
}
|
||||
|
||||
export type ShapeDrawerComponentProps = Omit<ShapeDrawerProps, 'getShape'>;
|
||||
|
||||
export type { ShapeType };
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import React, { Ref } from 'react';
|
||||
import { ShapeDrawer, ShapeRef } from '../reusable';
|
||||
import { ShapeDrawer, ShapeRef, ShapeDrawerComponentProps } from '../reusable';
|
||||
import { getShape } from './shapes';
|
||||
import { ShapeDrawerComponentProps } from './types';
|
||||
|
||||
const ShapeDrawerComponent = React.forwardRef(
|
||||
(props: ShapeDrawerComponentProps, ref: Ref<ShapeRef>) => (
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import { IInterpreterRenderHandlers } from '../../../../../../src/plugins/expressions';
|
||||
import { ShapeRendererConfig } from '../../../common/types';
|
||||
import { ShapeDrawerProps } from '../reusable/types';
|
||||
|
||||
export interface ShapeComponentProps extends ShapeRendererConfig {
|
||||
onLoaded: IInterpreterRenderHandlers['done'];
|
||||
|
@ -19,4 +18,3 @@ export interface Dimensions {
|
|||
width: number;
|
||||
height: number;
|
||||
}
|
||||
export type ShapeDrawerComponentProps = Omit<ShapeDrawerProps, 'getShape'>;
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { progress } from '../';
|
||||
import { Render } from '../../__stories__/render';
|
||||
import { Shape } from '../../../functions/common/progress';
|
||||
import { Render } from '../../../../presentation_util/public/__stories__';
|
||||
import { progressRenderer } from '../progress_renderer';
|
||||
import { Progress } from '../../../common';
|
||||
|
||||
storiesOf('renderers/progress', module).add('default', () => {
|
||||
const config = {
|
||||
|
@ -22,11 +23,11 @@ storiesOf('renderers/progress', module).add('default', () => {
|
|||
},
|
||||
label: '66%',
|
||||
max: 1,
|
||||
shape: Shape.UNICORN,
|
||||
shape: Progress.UNICORN,
|
||||
value: 0.66,
|
||||
valueColor: '#000',
|
||||
valueWeight: 15,
|
||||
};
|
||||
|
||||
return <Render renderer={progress} config={config} />;
|
||||
return <Render renderer={progressRenderer} config={config} />;
|
||||
});
|
|
@ -7,7 +7,8 @@
|
|||
*/
|
||||
|
||||
import { shapeRenderer } from './shape_renderer';
|
||||
import { progressRenderer } from './progress_renderer';
|
||||
|
||||
export const renderers = [shapeRenderer];
|
||||
export const renderers = [shapeRenderer, progressRenderer];
|
||||
|
||||
export { shapeRenderer };
|
||||
export { shapeRenderer, progressRenderer };
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ProgressRendererConfig } from '../../common/types';
|
||||
import { LazyProgressComponent } from '../components/progress';
|
||||
import { withSuspense } from '../../../presentation_util/public';
|
||||
|
||||
const ProgressComponent = withSuspense(LazyProgressComponent);
|
||||
|
||||
const strings = {
|
||||
getDisplayName: () =>
|
||||
i18n.translate('expressionShape.renderer.progress.displayName', {
|
||||
defaultMessage: 'Progress',
|
||||
}),
|
||||
getHelpDescription: () =>
|
||||
i18n.translate('expressionShape.renderer.progress.helpDescription', {
|
||||
defaultMessage: 'Render a basic progress',
|
||||
}),
|
||||
};
|
||||
|
||||
export const progressRenderer = (): ExpressionRenderDefinition<ProgressRendererConfig> => ({
|
||||
name: 'progress',
|
||||
displayName: strings.getDisplayName(),
|
||||
help: strings.getHelpDescription(),
|
||||
reuseDomNode: true,
|
||||
render: async (
|
||||
domNode: HTMLElement,
|
||||
config: ProgressRendererConfig,
|
||||
handlers: IInterpreterRenderHandlers
|
||||
) => {
|
||||
handlers.onDestroy(() => {
|
||||
unmountComponentAtNode(domNode);
|
||||
});
|
||||
|
||||
render(
|
||||
<ProgressComponent {...config} parentNode={domNode} onLoaded={handlers.done} />,
|
||||
domNode
|
||||
);
|
||||
},
|
||||
});
|
|
@ -16,6 +16,7 @@ export function plugin() {
|
|||
|
||||
export * from './expression_renderers';
|
||||
export { LazyShapeDrawer } from './components/shape';
|
||||
export { LazyProgressDrawer } from './components/progress';
|
||||
export { getDefaultShapeData } from './components/reusable';
|
||||
export * from './components/shape/types';
|
||||
export * from './components/reusable/types';
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
import { CoreSetup, CoreStart, Plugin } from '../../../core/public';
|
||||
import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public';
|
||||
import { shapeRenderer } from './expression_renderers';
|
||||
import { shapeFunction } from '../common/expression_functions';
|
||||
import { shapeRenderer, progressRenderer } from './expression_renderers';
|
||||
import { shapeFunction, progressFunction } from '../common/expression_functions';
|
||||
|
||||
interface SetupDeps {
|
||||
expressions: ExpressionsSetup;
|
||||
|
@ -26,7 +26,9 @@ export class ExpressionShapePlugin
|
|||
implements Plugin<ExpressionShapePluginSetup, ExpressionShapePluginStart, SetupDeps, StartDeps> {
|
||||
public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionShapePluginSetup {
|
||||
expressions.registerFunction(shapeFunction);
|
||||
expressions.registerFunction(progressFunction);
|
||||
expressions.registerRenderer(shapeRenderer);
|
||||
expressions.registerRenderer(progressRenderer);
|
||||
}
|
||||
|
||||
public start(core: CoreStart): ExpressionShapePluginStart {}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { CoreSetup, CoreStart, Plugin } from '../../../core/public';
|
||||
import { ExpressionsServerStart, ExpressionsServerSetup } from '../../expressions/server';
|
||||
import { shapeFunction } from '../common/expression_functions';
|
||||
import { shapeFunction, progressFunction } from '../common/expression_functions';
|
||||
|
||||
interface SetupDeps {
|
||||
expressions: ExpressionsServerSetup;
|
||||
|
@ -25,6 +25,7 @@ export class ExpressionShapePlugin
|
|||
implements Plugin<ExpressionShapePluginSetup, ExpressionShapePluginStart, SetupDeps, StartDeps> {
|
||||
public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionShapePluginSetup {
|
||||
expressions.registerFunction(shapeFunction);
|
||||
expressions.registerFunction(progressFunction);
|
||||
}
|
||||
|
||||
public start(core: CoreStart): ExpressionShapePluginStart {}
|
||||
|
|
|
@ -35,7 +35,6 @@ import { lte } from './lte';
|
|||
import { mapCenter } from './map_center';
|
||||
import { neq } from './neq';
|
||||
import { ply } from './ply';
|
||||
import { progress } from './progress';
|
||||
import { render } from './render';
|
||||
import { replace } from './replace';
|
||||
import { rounddate } from './rounddate';
|
||||
|
@ -83,7 +82,6 @@ export const functions = [
|
|||
mapCenter,
|
||||
neq,
|
||||
ply,
|
||||
progress,
|
||||
render,
|
||||
replace,
|
||||
rounddate,
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import {
|
||||
functionWrapper,
|
||||
fontStyle,
|
||||
} from '../../../../../../src/plugins/presentation_util/common/lib';
|
||||
import { getFunctionErrors } from '../../../i18n';
|
||||
import { progress } from './progress';
|
||||
|
||||
const errors = getFunctionErrors().progress;
|
||||
|
||||
// TODO: this test was not running and is not up to date
|
||||
describe.skip('progress', () => {
|
||||
const fn = functionWrapper(progress);
|
||||
|
||||
const value = 0.33;
|
||||
|
||||
it('returns a render as progress', () => {
|
||||
const result = fn(0.2);
|
||||
expect(result).to.have.property('type', 'render').and.to.have.property('as', 'progress');
|
||||
});
|
||||
|
||||
it('sets the progress to context', () => {
|
||||
const result = fn(0.58);
|
||||
expect(result.value).to.have.property('value', 0.58);
|
||||
});
|
||||
|
||||
it(`throws when context is outside of the valid range`, () => {
|
||||
expect(fn)
|
||||
.withArgs(3)
|
||||
.to.throwException(new RegExp(errors.invalidValue(3).message));
|
||||
});
|
||||
|
||||
describe('args', () => {
|
||||
describe('shape', () => {
|
||||
it('sets the progress element shape', () => {
|
||||
const result = fn(value, {
|
||||
shape: 'wheel',
|
||||
});
|
||||
expect(result.value).to.have.property('shape', 'wheel');
|
||||
});
|
||||
|
||||
it(`defaults to 'gauge'`, () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('shape', 'gauge');
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', () => {
|
||||
it('sets the maximum value', () => {
|
||||
const result = fn(value, {
|
||||
max: 2,
|
||||
});
|
||||
expect(result.value).to.have.property('max', 2);
|
||||
});
|
||||
|
||||
it('defaults to 1', () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('max', 1);
|
||||
});
|
||||
|
||||
it('throws if max <= 0', () => {
|
||||
expect(fn)
|
||||
.withArgs(value, { max: -0.5 })
|
||||
.to.throwException(new RegExp(errors.invalidMaxValue(-0.5).message));
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueColor', () => {
|
||||
it('sets the color of the progress bar', () => {
|
||||
const result = fn(value, {
|
||||
valueColor: '#000000',
|
||||
});
|
||||
expect(result.value).to.have.property('valueColor', '#000000');
|
||||
});
|
||||
|
||||
it(`defaults to '#1785b0'`, () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('valueColor', '#1785b0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('barColor', () => {
|
||||
it('sets the color of the background bar', () => {
|
||||
const result = fn(value, {
|
||||
barColor: '#FFFFFF',
|
||||
});
|
||||
expect(result.value).to.have.property('barColor', '#FFFFFF');
|
||||
});
|
||||
|
||||
it(`defaults to '#f0f0f0'`, () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('barColor', '#f0f0f0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('valueWeight', () => {
|
||||
it('sets the thickness of the bars', () => {
|
||||
const result = fn(value, {
|
||||
valuWeight: 100,
|
||||
});
|
||||
|
||||
expect(result.value).to.have.property('valuWeight', 100);
|
||||
});
|
||||
|
||||
it(`defaults to 20`, () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('barWeight', 20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('barWeight', () => {
|
||||
it('sets the thickness of the bars', () => {
|
||||
const result = fn(value, {
|
||||
barWeight: 50,
|
||||
});
|
||||
|
||||
expect(result.value).to.have.property('barWeight', 50);
|
||||
});
|
||||
|
||||
it(`defaults to 20`, () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('barWeight', 20);
|
||||
});
|
||||
});
|
||||
|
||||
describe('label', () => {
|
||||
it('sets the label of the progress', () => {
|
||||
const result = fn(value, { label: 'foo' });
|
||||
|
||||
expect(result.value).to.have.property('label', 'foo');
|
||||
});
|
||||
|
||||
it('hides the label if false', () => {
|
||||
const result = fn(value, {
|
||||
label: false,
|
||||
});
|
||||
expect(result.value).to.have.property('label', '');
|
||||
});
|
||||
|
||||
it('defaults to true which sets the context as the label', () => {
|
||||
const result = fn(value);
|
||||
expect(result.value).to.have.property('label', '0.33');
|
||||
});
|
||||
});
|
||||
|
||||
describe('font', () => {
|
||||
it('sets the font style for the label', () => {
|
||||
const result = fn(value, {
|
||||
font: fontStyle,
|
||||
});
|
||||
|
||||
expect(result.value).to.have.property('font');
|
||||
expect(result.value.font).to.have.keys(Object.keys(fontStyle));
|
||||
expect(result.value.font.spec).to.have.keys(Object.keys(fontStyle.spec));
|
||||
});
|
||||
|
||||
it('sets fill to color', () => {
|
||||
const result = fn(value, {
|
||||
font: fontStyle,
|
||||
});
|
||||
expect(result.value.font.spec).to.have.property('fill', fontStyle.spec.color);
|
||||
});
|
||||
|
||||
// TODO: write test when using an instance of the interpreter
|
||||
// it("sets a default style for the label when not provided", () => {});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash';
|
||||
import { openSans } from '../../../common/lib/fonts';
|
||||
import { Render, Style, ExpressionFunctionDefinition } from '../../../types';
|
||||
import { getFunctionHelp, getFunctionErrors } from '../../../i18n';
|
||||
|
||||
export enum Shape {
|
||||
GAUGE = 'gauge',
|
||||
HORIZONTAL_BAR = 'horizontalBar',
|
||||
HORIZONTAL_PILL = 'horizontalPill',
|
||||
SEMICIRCLE = 'semicircle',
|
||||
UNICORN = 'unicorn',
|
||||
VERTICAL_BAR = 'verticalBar',
|
||||
VERTICAL_PILL = 'verticalPill',
|
||||
WHEEL = 'wheel',
|
||||
}
|
||||
|
||||
export interface Arguments {
|
||||
barColor: string;
|
||||
barWeight: number;
|
||||
font: Style;
|
||||
label: boolean | string;
|
||||
max: number;
|
||||
shape: Shape;
|
||||
valueColor: string;
|
||||
valueWeight: number;
|
||||
}
|
||||
|
||||
export type Output = Arguments & {
|
||||
value: number;
|
||||
};
|
||||
|
||||
export function progress(): ExpressionFunctionDefinition<
|
||||
'progress',
|
||||
number,
|
||||
Arguments,
|
||||
Render<Arguments>
|
||||
> {
|
||||
const { help, args: argHelp } = getFunctionHelp().progress;
|
||||
const errors = getFunctionErrors().progress;
|
||||
|
||||
return {
|
||||
name: 'progress',
|
||||
aliases: [],
|
||||
type: 'render',
|
||||
inputTypes: ['number'],
|
||||
help,
|
||||
args: {
|
||||
shape: {
|
||||
aliases: ['_'],
|
||||
types: ['string'],
|
||||
help: argHelp.shape,
|
||||
options: Object.values(Shape),
|
||||
default: 'gauge',
|
||||
},
|
||||
barColor: {
|
||||
types: ['string'],
|
||||
help: argHelp.barColor,
|
||||
default: `#f0f0f0`,
|
||||
},
|
||||
barWeight: {
|
||||
types: ['number'],
|
||||
help: argHelp.barWeight,
|
||||
default: 20,
|
||||
},
|
||||
font: {
|
||||
types: ['style'],
|
||||
help: argHelp.font,
|
||||
default: `{font size=24 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
label: {
|
||||
types: ['boolean', 'string'],
|
||||
help: argHelp.label,
|
||||
default: true,
|
||||
},
|
||||
max: {
|
||||
types: ['number'],
|
||||
help: argHelp.max,
|
||||
default: 1,
|
||||
},
|
||||
valueColor: {
|
||||
types: ['string'],
|
||||
help: argHelp.valueColor,
|
||||
default: `#1785b0`,
|
||||
},
|
||||
valueWeight: {
|
||||
types: ['number'],
|
||||
help: argHelp.valueWeight,
|
||||
default: 20,
|
||||
},
|
||||
},
|
||||
fn: (value, args) => {
|
||||
if (args.max <= 0) {
|
||||
throw errors.invalidMaxValue(args.max);
|
||||
}
|
||||
if (value > args.max || value < 0) {
|
||||
throw errors.invalidValue(value, args.max);
|
||||
}
|
||||
|
||||
let label = '';
|
||||
if (args.label) {
|
||||
label = typeof args.label === 'string' ? args.label : `${value}`;
|
||||
}
|
||||
|
||||
let font: Style = {} as Style;
|
||||
|
||||
if (get(args, 'font.spec')) {
|
||||
font = { ...args.font };
|
||||
font.spec.fill = args.font.spec.color; // SVG <text> uses fill for font color
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'progress',
|
||||
value: {
|
||||
value,
|
||||
...args,
|
||||
label,
|
||||
font,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
|
@ -8,10 +8,9 @@
|
|||
import { markdown } from './markdown';
|
||||
import { pie } from './pie';
|
||||
import { plot } from './plot';
|
||||
import { progress } from './progress';
|
||||
import { text } from './text';
|
||||
import { table } from './table';
|
||||
|
||||
export const renderFunctions = [markdown, pie, plot, progress, table, text];
|
||||
export const renderFunctions = [markdown, pie, plot, table, text];
|
||||
|
||||
export const renderFunctionFactories = [];
|
||||
|
|
|
@ -10,7 +10,10 @@ import { metricRenderer } from '../../../../../src/plugins/expression_metric/pub
|
|||
import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public';
|
||||
import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public';
|
||||
import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public';
|
||||
import { shapeRenderer } from '../../../../../src/plugins/expression_shape/public';
|
||||
import {
|
||||
shapeRenderer,
|
||||
progressRenderer,
|
||||
} from '../../../../../src/plugins/expression_shape/public';
|
||||
|
||||
export const renderFunctions = [
|
||||
debugRenderer,
|
||||
|
@ -20,6 +23,7 @@ export const renderFunctions = [
|
|||
revealImageRenderer,
|
||||
shapeRenderer,
|
||||
repeatImageRenderer,
|
||||
progressRenderer,
|
||||
];
|
||||
|
||||
export const renderFunctionFactories = [];
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots renderers/progress default 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"height": "200px",
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</div>
|
||||
`;
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getId } from '../../../public/lib/get_id';
|
||||
import { RendererStrings } from '../../../i18n';
|
||||
import { shapes } from './shapes';
|
||||
import { Output as Arguments } from '../../functions/common/progress';
|
||||
import { RendererFactory } from '../../../types';
|
||||
|
||||
const { progress: strings } = RendererStrings;
|
||||
|
||||
export const progress: RendererFactory<Arguments> = () => ({
|
||||
name: 'progress',
|
||||
displayName: strings.getDisplayName(),
|
||||
help: strings.getHelpDescription(),
|
||||
reuseDomNode: true,
|
||||
render(domNode, config, handlers) {
|
||||
const { shape, value, max, valueColor, barColor, valueWeight, barWeight, label, font } = config;
|
||||
const percent = value / max;
|
||||
const shapeDef = shapes[shape];
|
||||
const offset = Math.max(valueWeight, barWeight);
|
||||
|
||||
if (shapeDef) {
|
||||
const parser = new DOMParser();
|
||||
const shapeSvg = parser
|
||||
.parseFromString(shapeDef, 'image/svg+xml')
|
||||
.getElementsByTagName('svg')
|
||||
.item(0)!;
|
||||
|
||||
const initialViewBox = shapeSvg
|
||||
.getAttribute('viewBox')!
|
||||
.split(' ')
|
||||
.map((v) => parseInt(v, 10));
|
||||
let [minX, minY, width, height] = initialViewBox;
|
||||
|
||||
if (shape !== 'horizontalBar') {
|
||||
minX -= offset / 2;
|
||||
width += offset;
|
||||
}
|
||||
|
||||
if (shape === 'semicircle') {
|
||||
minY -= offset / 2;
|
||||
height += offset / 2;
|
||||
} else if (shape !== 'verticalBar') {
|
||||
minY -= offset / 2;
|
||||
height += offset;
|
||||
}
|
||||
|
||||
shapeSvg.setAttribute('className', 'canvasProgress');
|
||||
|
||||
const svgId = getId('svg');
|
||||
shapeSvg.id = svgId;
|
||||
|
||||
const bar = shapeSvg.getElementsByTagName('path').item(0)!;
|
||||
bar.setAttribute('className', 'canvasProgress__background');
|
||||
bar.setAttribute('fill', 'none');
|
||||
bar.setAttribute('stroke', barColor);
|
||||
bar.setAttribute('stroke-width', `${barWeight}px`);
|
||||
|
||||
const valueSvg = bar.cloneNode(true) as SVGPathElement;
|
||||
valueSvg.setAttribute('className', 'canvasProgress__value');
|
||||
valueSvg.setAttribute('stroke', valueColor);
|
||||
valueSvg.setAttribute('stroke-width', `${valueWeight}px`);
|
||||
|
||||
const length = valueSvg.getTotalLength();
|
||||
const to = length * (1 - percent);
|
||||
valueSvg.setAttribute('stroke-dasharray', String(length));
|
||||
valueSvg.setAttribute('stroke-dashoffset', String(Math.max(0, to)));
|
||||
|
||||
shapeSvg.appendChild(valueSvg);
|
||||
|
||||
const text = shapeSvg.getElementsByTagName('text').item(0);
|
||||
|
||||
if (label && text) {
|
||||
text.textContent = String(label);
|
||||
text.setAttribute('className', 'canvasProgress__label');
|
||||
|
||||
if (shape === 'horizontalPill') {
|
||||
text.setAttribute('x', String(parseInt(text.getAttribute('x')!, 10) + offset / 2));
|
||||
}
|
||||
if (shape === 'verticalPill') {
|
||||
text.setAttribute('y', String(parseInt(text.getAttribute('y')!, 10) - offset / 2));
|
||||
}
|
||||
|
||||
Object.assign(text.style, font.spec);
|
||||
shapeSvg.appendChild(text);
|
||||
domNode.appendChild(shapeSvg);
|
||||
|
||||
const { width: labelWidth, height: labelHeight } = text.getBBox();
|
||||
|
||||
if (shape === 'horizontalBar' || shape === 'horizontalPill') {
|
||||
text.setAttribute('x', String(parseInt(text.getAttribute('x')!, 10)));
|
||||
width += labelWidth;
|
||||
}
|
||||
if (shape === 'verticalBar' || shape === 'verticalPill') {
|
||||
if (labelWidth > width) {
|
||||
minX = -labelWidth / 2;
|
||||
width = labelWidth;
|
||||
}
|
||||
minY -= labelHeight;
|
||||
height += labelHeight;
|
||||
}
|
||||
}
|
||||
|
||||
shapeSvg.setAttribute('viewBox', [minX, minY, width, height].join(' '));
|
||||
shapeSvg.setAttribute('width', String(domNode.offsetWidth));
|
||||
shapeSvg.setAttribute('height', String(domNode.offsetHeight));
|
||||
|
||||
if (domNode.firstChild) {
|
||||
domNode.removeChild(domNode.firstChild);
|
||||
}
|
||||
domNode.appendChild(shapeSvg);
|
||||
|
||||
handlers.onResize(() => {
|
||||
shapeSvg.setAttribute('width', String(domNode.offsetWidth));
|
||||
shapeSvg.setAttribute('height', String(domNode.offsetHeight));
|
||||
});
|
||||
}
|
||||
|
||||
handlers.done();
|
||||
},
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
|
||||
<path d="M 15 100 A 60 60 0 1 1 105 100" />
|
||||
<text x="60" y="60" text-anchor='middle' dominant-baseline='central' />
|
||||
</svg>
|
Before Width: | Height: | Size: 190 B |
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 208 1">
|
||||
<path d="M 0 1 L 200 1" />
|
||||
<text x="208" y="0" text-anchor='start' dominant-baseline='central' />
|
||||
</svg>
|
Before Width: | Height: | Size: 170 B |
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 208 1">
|
||||
<path d="M 0 1 L 200 1" stroke-linecap="round" />
|
||||
<text x="208" y="0" text-anchor='start' dominant-baseline='central' />
|
||||
</svg>
|
Before Width: | Height: | Size: 193 B |
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import gauge from '!!raw-loader!./gauge.svg';
|
||||
import horizontalBar from '!!raw-loader!./horizontal_bar.svg';
|
||||
import horizontalPill from '!!raw-loader!./horizontal_pill.svg';
|
||||
import semicircle from '!!raw-loader!./semicircle.svg';
|
||||
import unicorn from '!!raw-loader!./unicorn.svg';
|
||||
import verticalBar from '!!raw-loader!./vertical_bar.svg';
|
||||
import verticalPill from '!!raw-loader!./vertical_pill.svg';
|
||||
import wheel from '!!raw-loader!./wheel.svg';
|
||||
|
||||
export const shapes = {
|
||||
gauge,
|
||||
horizontalBar,
|
||||
horizontalPill,
|
||||
semicircle,
|
||||
unicorn,
|
||||
verticalBar,
|
||||
verticalPill,
|
||||
wheel,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60">
|
||||
<path d="M 0 60 A 60 60 0 1 1 120 60" />
|
||||
<text x="60" y="60" text-anchor='middle' dy="-1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 166 B |
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
||||
<path d="M 123 189 C 93 141 129 126 102 96 L 78 102 L 48 117 L 42 129 Q 30 132 21 126 L 18 114 L 27 90 L 42 72 L 48 57 L 3 6 L 57 42 L 63 33 L 60 15 L 69 27 L 69 15 L 84 27 Q 162 36 195 108 Q 174 159 123 189 Z" />
|
||||
<text x="0" y="200" text-anchor='start' dominant-baseline='text-after-edge' />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 368 B |
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -8 1 208">
|
||||
<path d="M 1 200 L 1 0" />
|
||||
<text x="0" y="-8" text-anchor='middle'/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 143 B |
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -8 1 208">
|
||||
<path d="M 1 200 L 1 0" stroke-linecap="round" />
|
||||
<text x="0" y="-8" text-anchor='middle' />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 167 B |
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120">
|
||||
<path d="M 60 0 A 60 60 0 1 1 60 120 A 60 60 0 1 1 60 0 Z" />
|
||||
<text x="60" y="60" text-anchor='middle' dominant-baseline='central' />
|
||||
</svg>
|
Before Width: | Height: | Size: 208 B |
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { openSans } from '../../../common/lib/fonts';
|
||||
import { shapes } from '../../renderers/progress/shapes';
|
||||
import { getAvailableProgressShapes } from '../../../../../../src/plugins/expression_shape/common';
|
||||
import { ViewStrings } from '../../../i18n';
|
||||
|
||||
const { Progress: strings } = ViewStrings;
|
||||
|
@ -23,7 +23,7 @@ export const progress = () => ({
|
|||
help: strings.getShapeHelp(),
|
||||
argType: 'select',
|
||||
options: {
|
||||
choices: Object.keys(shapes).map((key) => ({
|
||||
choices: getAvailableProgressShapes().map((key) => ({
|
||||
value: key,
|
||||
//turns camel into title case
|
||||
name: key[0].toUpperCase() + key.slice(1).replace(/([A-Z])/g, ' $1'),
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { progress } from '../../../canvas_plugin_src/functions/common/progress';
|
||||
import { FunctionHelp } from '../function_help';
|
||||
import { FunctionFactory } from '../../../types';
|
||||
|
||||
import { Shape } from '../../../canvas_plugin_src/functions/common/progress';
|
||||
import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../../constants';
|
||||
|
||||
export const help: FunctionHelp<FunctionFactory<typeof progress>> = {
|
||||
help: i18n.translate('xpack.canvas.functions.progressHelpText', {
|
||||
defaultMessage: 'Configures a progress element.',
|
||||
}),
|
||||
args: {
|
||||
barColor: i18n.translate('xpack.canvas.functions.progress.args.barColorHelpText', {
|
||||
defaultMessage: 'The color of the background bar.',
|
||||
}),
|
||||
barWeight: i18n.translate('xpack.canvas.functions.progress.args.barWeightHelpText', {
|
||||
defaultMessage: 'The thickness of the background bar.',
|
||||
}),
|
||||
font: i18n.translate('xpack.canvas.functions.progress.args.fontHelpText', {
|
||||
defaultMessage:
|
||||
'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
|
||||
values: {
|
||||
CSS,
|
||||
FONT_FAMILY,
|
||||
FONT_WEIGHT,
|
||||
},
|
||||
}),
|
||||
label: i18n.translate('xpack.canvas.functions.progress.args.labelHelpText', {
|
||||
defaultMessage:
|
||||
'To show or hide the label, use {BOOLEAN_TRUE} or {BOOLEAN_FALSE}. Alternatively, provide a string to display as a label.',
|
||||
values: {
|
||||
BOOLEAN_TRUE,
|
||||
BOOLEAN_FALSE,
|
||||
},
|
||||
}),
|
||||
max: i18n.translate('xpack.canvas.functions.progress.args.maxHelpText', {
|
||||
defaultMessage: 'The maximum value of the progress element.',
|
||||
}),
|
||||
shape: i18n.translate('xpack.canvas.functions.progress.args.shapeHelpText', {
|
||||
defaultMessage: `Select {list}, or {end}.`,
|
||||
values: {
|
||||
list: Object.values(Shape)
|
||||
.slice(0, -1)
|
||||
.map((shape) => `\`"${shape}"\``)
|
||||
.join(', '),
|
||||
end: `\`"${Object.values(Shape).slice(-1)[0]}"\``,
|
||||
},
|
||||
}),
|
||||
valueColor: i18n.translate('xpack.canvas.functions.progress.args.valueColorHelpText', {
|
||||
defaultMessage: 'The color of the progress bar.',
|
||||
}),
|
||||
valueWeight: i18n.translate('xpack.canvas.functions.progress.args.valueWeightHelpText', {
|
||||
defaultMessage: 'The thickness of the progress bar.',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const errors = {
|
||||
invalidMaxValue: (max: number) =>
|
||||
new Error(
|
||||
i18n.translate('xpack.canvas.functions.progress.invalidMaxValueErrorMessage', {
|
||||
defaultMessage: "Invalid {arg} value: '{max, number}'. '{arg}' must be greater than 0",
|
||||
values: {
|
||||
arg: 'max',
|
||||
max,
|
||||
},
|
||||
})
|
||||
),
|
||||
invalidValue: (value: number, max: number = 1) =>
|
||||
new Error(
|
||||
i18n.translate('xpack.canvas.functions.progress.invalidValueErrorMessage', {
|
||||
defaultMessage:
|
||||
"Invalid value: '{value, number}'. Value must be between 0 and {max, number}",
|
||||
values: {
|
||||
value,
|
||||
max,
|
||||
},
|
||||
})
|
||||
),
|
||||
};
|
|
@ -17,7 +17,6 @@ import { errors as getCell } from './dict/get_cell';
|
|||
import { errors as joinRows } from './dict/join_rows';
|
||||
import { errors as ply } from './dict/ply';
|
||||
import { errors as pointseries } from './dict/pointseries';
|
||||
import { errors as progress } from './dict/progress';
|
||||
import { errors as timefilter } from './dict/timefilter';
|
||||
import { errors as to } from './dict/to';
|
||||
|
||||
|
@ -34,7 +33,6 @@ export const getFunctionErrors = () => ({
|
|||
joinRows,
|
||||
ply,
|
||||
pointseries,
|
||||
progress,
|
||||
timefilter,
|
||||
to,
|
||||
});
|
||||
|
|
|
@ -51,7 +51,6 @@ import { help as pie } from './dict/pie';
|
|||
import { help as plot } from './dict/plot';
|
||||
import { help as ply } from './dict/ply';
|
||||
import { help as pointseries } from './dict/pointseries';
|
||||
import { help as progress } from './dict/progress';
|
||||
import { help as render } from './dict/render';
|
||||
import { help as replace } from './dict/replace';
|
||||
import { help as rounddate } from './dict/rounddate';
|
||||
|
@ -207,7 +206,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({
|
|||
plot,
|
||||
ply,
|
||||
pointseries,
|
||||
progress,
|
||||
render,
|
||||
replace,
|
||||
rounddate,
|
||||
|
|
|
@ -89,16 +89,6 @@ export const RendererStrings = {
|
|||
defaultMessage: 'Render an XY plot from your data',
|
||||
}),
|
||||
},
|
||||
progress: {
|
||||
getDisplayName: () =>
|
||||
i18n.translate('xpack.canvas.renderer.progress.displayName', {
|
||||
defaultMessage: 'Progress indicator',
|
||||
}),
|
||||
getHelpDescription: () =>
|
||||
i18n.translate('xpack.canvas.renderer.progress.helpDescription', {
|
||||
defaultMessage: 'Render a progress indicator that reveals a percentage of an element',
|
||||
}),
|
||||
},
|
||||
table: {
|
||||
getDisplayName: () =>
|
||||
i18n.translate('xpack.canvas.renderer.table.displayName', {
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
import { Dispatch } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { getId } from '../../lib/get_id';
|
||||
// @ts-expect-error untyped local
|
||||
import { findExistingAsset } from '../../lib/find_existing_asset';
|
||||
import { VALID_IMAGE_TYPES } from '../../../common/lib/constants';
|
||||
import { encode } from '../../../../../../src/plugins/presentation_util/public';
|
||||
import { getId } from '../../lib/get_id';
|
||||
// @ts-expect-error untyped local
|
||||
import { elementsRegistry } from '../../lib/elements_registry';
|
||||
// @ts-expect-error untyped local
|
||||
|
|
|
@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { CANVAS, JSON as JSONString } from '../../../../i18n/constants';
|
||||
import { useNotifyService } from '../../../services';
|
||||
import { getId } from '../../../lib/get_id';
|
||||
|
||||
import { useCreateWorkpad } from './use_create_workpad';
|
||||
import type { CanvasWorkpad } from '../../../../types';
|
||||
|
||||
|
|
|
@ -37,7 +37,9 @@ export const ShapePreview: FC<Props> = ({ shape }) => {
|
|||
const [shapeData, setShapeData] = useState<SvgConfig>(getDefaultShapeData());
|
||||
|
||||
const shapeRef = useCallback<RefCallback<ShapeRef>>((node) => {
|
||||
if (node !== null) setShapeData(node.getData());
|
||||
if (node !== null) {
|
||||
setShapeData(node.getData());
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!shape) return <div className="canvasShapePreview" />;
|
||||
|
|
|
@ -15,11 +15,11 @@ import {
|
|||
EuiContextMenuPanelItemDescriptor,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getId } from '../../../lib/get_id';
|
||||
import { Popover, ClosePopoverFn } from '../../popover';
|
||||
import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib';
|
||||
import { ElementSpec } from '../../../../types';
|
||||
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
|
||||
import { getId } from '../../../lib/get_id';
|
||||
import { AssetManager } from '../../asset_manager';
|
||||
import { SavedElementsModal } from '../../saved_elements_modal';
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getId as rawGetId } from '../get_id';
|
||||
|
||||
import { insideAABB, landmarkPoint, shapesAt } from './geometry';
|
||||
|
||||
import {
|
||||
|
@ -40,8 +42,6 @@ import {
|
|||
removeDuplicates,
|
||||
} from './functional';
|
||||
|
||||
import { getId as rawGetId } from './../../lib/get_id';
|
||||
|
||||
const idMap = {};
|
||||
const getId = (name, extension) => {
|
||||
// ensures that `axisAlignedBoundingBoxShape` is pure-ish - a new call with the same input will not yield a new id
|
||||
|
|
|
@ -9,7 +9,6 @@ import moment from 'moment';
|
|||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { PluginServiceFactory } from '../../../../../../src/plugins/presentation_util/public';
|
||||
|
||||
import { getId } from '../../lib/get_id';
|
||||
// @ts-expect-error
|
||||
import { getDefaultWorkpad } from '../../state/defaults';
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { markdown } from '../canvas_plugin_src/renderers/markdown';
|
||||
import { pie } from '../canvas_plugin_src/renderers/pie';
|
||||
import { plot } from '../canvas_plugin_src/renderers/plot';
|
||||
import { progress } from '../canvas_plugin_src/renderers/progress';
|
||||
import { table } from '../canvas_plugin_src/renderers/table';
|
||||
import { text } from '../canvas_plugin_src/renderers/text';
|
||||
import { imageRenderer as image } from '../../../../src/plugins/expression_image/public';
|
||||
|
@ -18,7 +17,10 @@ import {
|
|||
} from '../../../../src/plugins/expression_error/public';
|
||||
import { repeatImageRenderer as repeatImage } from '../../../../src/plugins/expression_repeat_image/public';
|
||||
import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public';
|
||||
import { shapeRenderer as shape } from '../../../../src/plugins/expression_shape/public';
|
||||
import {
|
||||
shapeRenderer as shape,
|
||||
progressRenderer as progress,
|
||||
} from '../../../../src/plugins/expression_shape/public';
|
||||
import { metricRenderer as metric } from '../../../../src/plugins/expression_metric/public';
|
||||
|
||||
/**
|
||||
|
|
|
@ -6727,17 +6727,17 @@
|
|||
"xpack.canvas.functions.pointseries.args.yHelpText": "Y軸の値です。",
|
||||
"xpack.canvas.functions.pointseries.unwrappedExpressionErrorMessage": "表現は {fn} などの関数で囲む必要があります",
|
||||
"xpack.canvas.functions.pointseriesHelpText": "{DATATABLE} を点の配列モデルに変換します。現在 {TINYMATH} 式でディメンションのメジャーを区別します。{TINYMATH_URL} をご覧ください。引数に {TINYMATH} 式が入力された場合、その引数をメジャーとして使用し、そうでない場合はディメンションになります。ディメンションを組み合わせて固有のキーを作成します。その後メジャーはそれらのキーで、指定された {TINYMATH} 関数を使用して複製されます。",
|
||||
"xpack.canvas.functions.progress.args.barColorHelpText": "背景バーの色です。",
|
||||
"xpack.canvas.functions.progress.args.barWeightHelpText": "背景バーの太さです。",
|
||||
"xpack.canvas.functions.progress.args.fontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
|
||||
"xpack.canvas.functions.progress.args.labelHelpText": "ラベルの表示・非表示を切り替えるには、{BOOLEAN_TRUE}または{BOOLEAN_FALSE}を使用します。また、ラベルとして表示する文字列を入力することもできます。",
|
||||
"xpack.canvas.functions.progress.args.maxHelpText": "進捗エレメントの最高値です。",
|
||||
"xpack.canvas.functions.progress.args.shapeHelpText": "{list} または {end} を選択します。",
|
||||
"xpack.canvas.functions.progress.args.valueColorHelpText": "進捗バーの色です。",
|
||||
"xpack.canvas.functions.progress.args.valueWeightHelpText": "進捗バーの太さです。",
|
||||
"xpack.canvas.functions.progress.invalidMaxValueErrorMessage": "無効な {arg} 値:「{max, number}」。「{arg}」は 0 より大きい必要があります",
|
||||
"xpack.canvas.functions.progress.invalidValueErrorMessage": "無効な値:「{value, number}」。値は 0 と {max, number} の間でなければなりません",
|
||||
"xpack.canvas.functions.progressHelpText": "進捗エレメントを構成します。",
|
||||
"expressionShape.functions.progress.args.barColorHelpText": "背景バーの色です。",
|
||||
"expressionShape.functions.progress.args.barWeightHelpText": "背景バーの太さです。",
|
||||
"expressionShape.functions.progress.args.fontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
|
||||
"expressionShape.functions.progress.args.labelHelpText": "ラベルの表示・非表示を切り替えるには、{BOOLEAN_TRUE}または{BOOLEAN_FALSE}を使用します。また、ラベルとして表示する文字列を入力することもできます。",
|
||||
"expressionShape.functions.progress.args.maxHelpText": "進捗エレメントの最高値です。",
|
||||
"expressionShape.functions.progress.args.shapeHelpText": "{list} または {end} を選択します。",
|
||||
"expressionShape.functions.progress.args.valueColorHelpText": "進捗バーの色です。",
|
||||
"expressionShape.functions.progress.args.valueWeightHelpText": "進捗バーの太さです。",
|
||||
"expressionShape.functions.progress.invalidMaxValueErrorMessage": "無効な {arg} 値:「{max, number}」。「{arg}」は 0 より大きい必要があります",
|
||||
"expressionShape.functions.progress.invalidValueErrorMessage": "無効な値:「{value, number}」。値は 0 と {max, number} の間でなければなりません",
|
||||
"expressionShape.functions.progressHelpText": "進捗エレメントを構成します。",
|
||||
"xpack.canvas.functions.render.args.asHelpText": "レンダリングに使用するエレメントタイプです。代わりに {plotFn} や {shapeFn} などの特殊な関数を使用するほうがいいでしょう。",
|
||||
"xpack.canvas.functions.render.args.containerStyleHelpText": "背景、境界、透明度を含む、コンテナーのスタイルです。",
|
||||
"xpack.canvas.functions.render.args.cssHelpText": "このエレメントの対象となるカスタム {CSS} のブロックです。",
|
||||
|
@ -6926,8 +6926,8 @@
|
|||
"xpack.canvas.renderer.pie.helpDescription": "データから円グラフをレンダリングします",
|
||||
"xpack.canvas.renderer.plot.displayName": "座標プロット",
|
||||
"xpack.canvas.renderer.plot.helpDescription": "データから XY プロットをレンダリングします",
|
||||
"xpack.canvas.renderer.progress.displayName": "進捗インジケーター",
|
||||
"xpack.canvas.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします",
|
||||
"expressionShape.renderer.progress.displayName": "進捗インジケーター",
|
||||
"expressionShape.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします",
|
||||
"xpack.canvas.renderer.table.displayName": "データテーブル",
|
||||
"xpack.canvas.renderer.table.helpDescription": "表形式データを {HTML} としてレンダリングします",
|
||||
"xpack.canvas.renderer.text.displayName": "プレインテキスト",
|
||||
|
|
|
@ -6767,17 +6767,17 @@
|
|||
"xpack.canvas.functions.pointseries.args.yHelpText": "Y 轴上的值。",
|
||||
"xpack.canvas.functions.pointseries.unwrappedExpressionErrorMessage": "表达式必须包装在函数中,例如 {fn}",
|
||||
"xpack.canvas.functions.pointseriesHelpText": "将 {DATATABLE} 转成点序列模型。当前我们通过寻找 {TINYMATH} 表达式来区分度量和维度。请参阅 {TINYMATH_URL}。如果在参数中输入 {TINYMATH} 表达式,我们将该参数视为度量,否则该参数为维度。维度将进行组合以创建唯一键。然后,这些键使用指定的 {TINYMATH} 函数消除重复的度量",
|
||||
"xpack.canvas.functions.progress.args.barColorHelpText": "背景条形的颜色。",
|
||||
"xpack.canvas.functions.progress.args.barWeightHelpText": "背景条形的粗细。",
|
||||
"xpack.canvas.functions.progress.args.fontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
|
||||
"xpack.canvas.functions.progress.args.labelHelpText": "要显示或隐藏标签,请使用 {BOOLEAN_TRUE} 或 {BOOLEAN_FALSE}。或者,提供字符串以显示为标签。",
|
||||
"xpack.canvas.functions.progress.args.maxHelpText": "进度元素的最大值。",
|
||||
"xpack.canvas.functions.progress.args.shapeHelpText": "选择 {list} 或 {end}。",
|
||||
"xpack.canvas.functions.progress.args.valueColorHelpText": "进度条的颜色。",
|
||||
"xpack.canvas.functions.progress.args.valueWeightHelpText": "进度条的粗细。",
|
||||
"xpack.canvas.functions.progress.invalidMaxValueErrorMessage": "无效的 {arg} 值:“{max, number}”。“{arg}”必须大于 0",
|
||||
"xpack.canvas.functions.progress.invalidValueErrorMessage": "无效的值:“{value, number}”。值必须介于 0 和 {max, number} 之间",
|
||||
"xpack.canvas.functions.progressHelpText": "配置进度元素。",
|
||||
"expressionShape.functions.progress.args.barColorHelpText": "背景条形的颜色。",
|
||||
"expressionShape.functions.progress.args.barWeightHelpText": "背景条形的粗细。",
|
||||
"expressionShape.functions.progress.args.fontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
|
||||
"expressionShape.functions.progress.args.labelHelpText": "要显示或隐藏标签,请使用 {BOOLEAN_TRUE} 或 {BOOLEAN_FALSE}。或者,提供字符串以显示为标签。",
|
||||
"expressionShape.functions.progress.args.maxHelpText": "进度元素的最大值。",
|
||||
"expressionShape.functions.progress.args.shapeHelpText": "选择 {list} 或 {end}。",
|
||||
"expressionShape.functions.progress.args.valueColorHelpText": "进度条的颜色。",
|
||||
"expressionShape.functions.progress.args.valueWeightHelpText": "进度条的粗细。",
|
||||
"expressionShape.functions.progress.invalidMaxValueErrorMessage": "无效的 {arg} 值:“{max, number}”。“{arg}”必须大于 0",
|
||||
"expressionShape.functions.progress.invalidValueErrorMessage": "无效的值:“{value, number}”。值必须介于 0 和 {max, number} 之间",
|
||||
"expressionShape.functions.progressHelpText": "配置进度元素。",
|
||||
"xpack.canvas.functions.render.args.asHelpText": "要渲染的元素类型。您可能需要专门的函数,例如 {plotFn} 或 {shapeFn}。",
|
||||
"xpack.canvas.functions.render.args.containerStyleHelpText": "容器的样式,包括背景、边框和透明度。",
|
||||
"xpack.canvas.functions.render.args.cssHelpText": "要限定于元素的任何定制 {CSS} 块。",
|
||||
|
@ -6966,8 +6966,8 @@
|
|||
"xpack.canvas.renderer.pie.helpDescription": "根据您的数据呈现饼图",
|
||||
"xpack.canvas.renderer.plot.displayName": "坐标图",
|
||||
"xpack.canvas.renderer.plot.helpDescription": "根据您的数据呈现 XY 坐标图",
|
||||
"xpack.canvas.renderer.progress.displayName": "进度指示",
|
||||
"xpack.canvas.renderer.progress.helpDescription": "呈现显示元素百分比的进度指示",
|
||||
"expressionShape.renderer.progress.displayName": "进度指示",
|
||||
"expressionShape.renderer.progress.helpDescription": "呈现显示元素百分比的进度指示",
|
||||
"xpack.canvas.renderer.table.displayName": "数据表",
|
||||
"xpack.canvas.renderer.table.helpDescription": "将表格数据呈现为 {HTML}",
|
||||
"xpack.canvas.renderer.text.displayName": "纯文本",
|
||||
|
|