* Add auto update property for gradient scales

* Update label

* Update styles

* Added numerical color legend orientation

* Removed 'Add legend' button for boolean scales

* Clean code

* Added legend height and width

* Updated migrator

* Added const value and update migrator

Co-authored-by: Ramil Minyukov (Akvelon INC) <v-rminyukov@microsoft.com>
Co-authored-by: bongshin <bongshin@microsoft.com>
This commit is contained in:
Ramil Minyukov 2022-01-25 17:08:04 +03:00 коммит произвёл GitHub
Родитель 842467a807
Коммит f11d3b7101
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 190 добавлений и 50 удалений

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "charticulator",
"version": "2.1.4",
"version": "2.1.5",
"private": true,
"author": {
"name": "Donghao Ren",

Просмотреть файл

@ -34,6 +34,8 @@ import { TickFormatType } from "../../core/specification/types";
import { SymbolElementProperties } from "../../core/prototypes/marks/symbol.attrs";
import { LinearBooleanScaleMode } from "../../core/prototypes/scales/linear";
import { parseDerivedColumnsExpression } from "../../core/prototypes/plot_segments/utils";
import { OrientationType } from "../../core/prototypes/legends/types";
import { NumericalColorLegendClass } from "../../core/prototypes/legends/color_legend";
/** Upgrade old versions of chart spec and state to newer version */
export class Migrator {
@ -176,6 +178,13 @@ export class Migrator {
state = this.setMissedSortProperties(state);
}
if (
compareVersion(state.version, "2.1.5") < 0 &&
compareVersion(targetVersion, "2.1.5") >= 0
) {
state = this.setMissedLegendProperties(state);
}
// After migration, set version to targetVersion
state.version = targetVersion;
@ -924,4 +933,20 @@ export class Migrator {
}
return state;
}
public setMissedLegendProperties(state: AppStoreState) {
for (const element of state.chart.elements) {
if (Prototypes.isType(element.classID, "legend.numerical-color")) {
const legend = element as ChartElement<LegendProperties>;
if (legend.properties.orientation === undefined) {
legend.properties.orientation = OrientationType.VERTICAL;
}
if (legend.properties.length === undefined) {
legend.properties.length =
NumericalColorLegendClass.defaultLegendLength;
}
}
}
return state;
}
}

Просмотреть файл

@ -56,7 +56,9 @@ export class ScaleEditor extends React.Component<
let canAddLegend = true;
if (
scale.classID.startsWith("scale.format") ||
scale.classID === "scale.categorical<string,image>"
scale.classID === "scale.categorical<string,image>" ||
scale.classID === "scale.categorical<string,boolean>" ||
scale.classID === "scale.linear<number,boolean>"
) {
canAddLegend = false;
}

Просмотреть файл

@ -20,6 +20,7 @@ import {
labelRender,
PlaceholderStyle,
} from "./fluentui_customized_components";
import { CSSProperties } from "react";
export interface InputNumberProps {
defaultValue?: number;
@ -43,6 +44,8 @@ export interface InputNumberProps {
label?: string;
stopPropagation?: boolean;
styles?: CSSProperties;
}
export const FluentInputNumber: React.FC<InputNumberProps> = (props) => {
@ -197,7 +200,7 @@ export const FluentInputNumber: React.FC<InputNumberProps> = (props) => {
{props.showSlider ? (
<Label styles={defaultLabelStyle}>{props.label}</Label>
) : null}
<FluentRowLayout>
<FluentRowLayout style={props.styles}>
<FluentLayoutItem flex={1}>
{props.showUpdown ? (
renderUpdown()

Просмотреть файл

@ -604,7 +604,7 @@ export class FluentUIWidgetManager
{options.headerLabel ? (
<Label styles={defaultLabelStyle}>{options.headerLabel}</Label>
) : null}
<FluentCheckbox>
<FluentCheckbox style={options.styles}>
<Checkbox
checked={this.getPropertyValue(property) as boolean}
label={options.label}

Просмотреть файл

@ -541,18 +541,6 @@ export class ChartTemplate {
)
);
if (
inference.autoDomainMin &&
object.properties.domainMin !== undefined
) {
vectors.push([object.properties.domainMin]);
}
if (
inference.autoDomainMax &&
object.properties.domainMax != undefined
) {
vectors.push([object.properties.domainMax]);
}
const vector = vectors.reduce((a, b) => a.concat(b), []);
const scaleClass = Prototypes.ObjectClasses.Create(null, object, {
attributes: {},

Просмотреть файл

@ -70,6 +70,7 @@ export interface InputBooleanOptions {
observerConfig?: ObserverConfig;
checkBoxStyles?: ICheckboxStyles;
onChange?: (value: boolean) => void;
styles?: CSSProperties;
}
export interface RowOptions {
@ -144,6 +145,7 @@ export interface InputNumberOptions {
stopPropagation?: boolean;
observerConfig?: ObserverConfig;
styles?: CSSProperties;
}
export interface InputDateOptions {

Просмотреть файл

@ -8,6 +8,7 @@ import { LegendClass, LegendProperties } from "./legend";
import { Controls } from "..";
import { strings } from "../../../strings";
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
import { OrientationType } from "./types";
export interface CategoricalLegendItem {
type: "number" | "color" | "boolean";
@ -23,7 +24,7 @@ export class CategoricalLegendClass extends LegendClass {
public static defaultProperties: LegendProperties = {
...LegendClass.defaultProperties,
orientation: "vertical",
orientation: OrientationType.VERTICAL,
};
protected textMeasure = new Graphics.TextMeasurer();
@ -93,7 +94,7 @@ export class CategoricalLegendClass extends LegendClass {
public getLineWidth(): number {
let width = 0;
const items = this.getLegendItems();
if (this.object.properties.orientation === "horizontal") {
if (this.object.properties.orientation === OrientationType.HORIZONTAL) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
const metrics = this.textMeasure.measure(item.label);
@ -116,7 +117,7 @@ export class CategoricalLegendClass extends LegendClass {
public getLegendSize(): [number, number] {
const items = this.getLegendItems();
if (
this.object.properties.orientation === "vertical" ||
this.object.properties.orientation === OrientationType.VERTICAL ||
this.object.properties.orientation === undefined
) {
return [
@ -215,7 +216,7 @@ export class CategoricalLegendClass extends LegendClass {
}
break;
}
if (this.object.properties.orientation === "horizontal") {
if (this.object.properties.orientation === OrientationType.HORIZONTAL) {
gItem.transform = {
x: itemGroupOffset,
y: 0,
@ -238,7 +239,7 @@ export class CategoricalLegendClass extends LegendClass {
public getLayoutBox(): { x1: number; y1: number; x2: number; y2: number } {
if (
this.object.properties.orientation === "vertical" ||
this.object.properties.orientation === OrientationType.VERTICAL ||
this.object.properties.orientation === undefined
) {
return super.getLayoutBox();
@ -300,7 +301,7 @@ export class CategoricalLegendClass extends LegendClass {
strings.objects.legend.vertical,
strings.objects.legend.horizontal,
],
options: ["vertical", "horizontal"],
options: [OrientationType.VERTICAL, OrientationType.HORIZONTAL],
label: strings.objects.legend.orientation,
}
),

Просмотреть файл

@ -6,20 +6,53 @@ import * as Graphics from "../../graphics";
import * as Specification from "../../specification";
import { AxisRenderer } from "../plot_segments/axis";
import { LegendClass } from "./legend";
import { LegendClass, LegendProperties } from "./legend";
import { Controls } from "../common";
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
import { strings } from "../../../strings";
import { OrientationType } from "./types";
export class NumericalColorLegendClass extends LegendClass {
public static classID: string = "legend.numerical-color";
public static type: string = "legend";
public static defaultLegendLength: number = 100;
public static defaultProperties: LegendProperties = {
...LegendClass.defaultProperties,
orientation: OrientationType.VERTICAL,
length: NumericalColorLegendClass.defaultLegendLength,
};
private gradientWidth: number = 12;
public getLineHeight(): number {
return this.object.properties.fontSize + 25 + this.gradientWidth;
}
public getLegendSize(): [number, number] {
return [100, 100];
const props = this.object.properties;
const length = props.length
? +props.length
: NumericalColorLegendClass.defaultLegendLength;
if (this.isHorizontalOrientation()) {
return [length, this.getLineHeight()];
}
return [this.getLineHeight(), length];
}
private isHorizontalOrientation(): boolean {
const props = this.object.properties;
return props.orientation === OrientationType.HORIZONTAL;
}
public getGraphics(): Graphics.Element {
const height = this.getLegendSize()[1];
const height = this.isHorizontalOrientation()
? this.getLegendSize()[0]
: this.getLegendSize()[1];
const marginLeft = 5;
const gradientWidth = 12;
const axisMargin = 2;
const horizontalShift = this.getLegendSize()[1] - gradientWidth;
const scale = this.getScale();
if (!scale) {
@ -39,9 +72,18 @@ export class NumericalColorLegendClass extends LegendClass {
lineColor: this.object.properties.textColor,
});
const g = Graphics.makeGroup([]);
g.elements.push(
axisRenderer.renderLine(marginLeft + gradientWidth + 2, 0, 90, 1)
);
if (this.isHorizontalOrientation()) {
g.elements.push(axisRenderer.renderLine(0, -axisMargin, 0, 1));
} else {
g.elements.push(
axisRenderer.renderLine(
marginLeft + gradientWidth + axisMargin,
0,
90,
1
)
);
}
const ticks = height * 2;
const interp = interpolateColors(range.colors, range.colorspace);
@ -50,15 +92,68 @@ export class NumericalColorLegendClass extends LegendClass {
const color = interp(t);
const y1 = (i / ticks) * height;
const y2 = Math.min(height, ((i + 1.5) / ticks) * height);
g.elements.push(
Graphics.makeRect(marginLeft, y1, marginLeft + gradientWidth, y2, {
fillColor: color,
})
);
if (this.isHorizontalOrientation()) {
g.elements.push(
Graphics.makeRect(y1, 0, y2, gradientWidth, {
fillColor: color,
})
);
} else {
g.elements.push(
Graphics.makeRect(marginLeft, y1, marginLeft + gradientWidth, y2, {
fillColor: color,
})
);
}
}
const { x1, y1 } = this.getLayoutBox();
g.transform = { x: x1, y: y1, angle: 0 };
if (this.isHorizontalOrientation()) {
g.transform = { x: x1, y: y1 + horizontalShift, angle: 0 };
} else {
g.transform = { x: x1, y: y1, angle: 0 };
}
return g;
}
public getAttributePanelWidgets(
manager: Controls.WidgetManager & CharticulatorPropertyAccessors
): Controls.Widget[] {
const widgets = super.getAttributePanelWidgets(manager);
return [
...widgets,
manager.verticalGroup(
{
header: strings.objects.legend.numericalColorLegend,
},
[
manager.inputNumber(
{ property: "length" },
{
label: this.isHorizontalOrientation()
? strings.objects.width
: strings.objects.height,
updownTick: 10,
showUpdown: true,
}
),
manager.inputSelect(
{ property: "orientation" },
{
type: "radio",
showLabel: false,
icons: ["AlignHorizontalCenter", "AlignVerticalCenter"],
labels: [
strings.objects.legend.vertical,
strings.objects.legend.horizontal,
],
options: [OrientationType.VERTICAL, OrientationType.HORIZONTAL],
label: strings.objects.legend.orientation,
}
),
]
),
];
}
}

Просмотреть файл

@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
export enum OrientationType {
VERTICAL = "vertical",
HORIZONTAL = "horizontal",
}

Просмотреть файл

@ -1469,17 +1469,6 @@ export function buildAxisAppearanceWidgets(
allowNull: true,
}
),
manager.inputFormat(
{
property: axisProperty,
field: "tickFormat",
},
{
blank: strings.core.auto,
isDateField: false,
label: strings.objects.axes.tickFormat,
}
),
manager.inputNumber(
{
property: axisProperty,

Просмотреть файл

@ -19,8 +19,8 @@ import { InferParametersOptions } from "./scale";
export interface LinearScaleProperties extends Specification.AttributeMap {
domainMin: number;
domainMax: number;
autoDomainMin: number;
autoDomainMax: number;
autoDomainMin: boolean;
autoDomainMax: boolean;
}
export interface LinearScaleAttributes extends Specification.AttributeMap {
@ -154,7 +154,11 @@ export class LinearScale extends ScaleClass<
),
manager.inputNumber(
{ property: "domainMax" },
{ label: strings.objects.dataAxis.end, stopPropagation: true }
{
label: strings.objects.dataAxis.end,
stopPropagation: true,
styles: { marginBottom: "0.5rem" },
}
),
manager.sectionHeader(strings.objects.dataAxis.autoUpdateValues),
manager.inputBoolean(
@ -296,7 +300,6 @@ export class LinearColorScale extends ScaleClass<
const s = new Scale.LinearScale();
const values = <number[]>column.filter((x) => typeof x == "number");
s.inferParameters(values);
s.adjustDomain(options);
if (options.extendScaleMin || props.domainMin === undefined) {
props.domainMin = s.domainMin;
@ -321,7 +324,31 @@ export class LinearColorScale extends ScaleClass<
),
manager.inputNumber(
{ property: "domainMax" },
{ stopPropagation: true, label: strings.objects.dataAxis.end }
{
stopPropagation: true,
label: strings.objects.dataAxis.end,
styles: { marginBottom: "0.5rem" },
}
),
manager.sectionHeader(strings.objects.dataAxis.autoUpdateValues),
manager.inputBoolean(
{
property: "autoDomainMin",
},
{
type: "checkbox",
label: strings.objects.dataAxis.autoMin,
}
),
manager.inputBoolean(
{
property: "autoDomainMax",
},
{
type: "checkbox",
label: strings.objects.dataAxis.autoMax,
styles: { marginBottom: "0.5rem" },
}
),
manager.sectionHeader(strings.objects.dataAxis.gradient),
manager.inputColorGradient(

Просмотреть файл

@ -492,6 +492,7 @@ export const strings = {
labels: "Labels",
layout: "Layout",
categoricalLegend: "Categorical legend",
numericalColorLegend: "Numerical color legend",
ordering: "Ordering",
},
links: {