Refactoring and eslint rules (#882)
* Updated color picker menu * Removed picker column * Fixed part of eslint rules * Fixed ban-types eslint rules * Fixed hasOwnProperty method * Fixed unused var rule * Removed old ui manager * Added ImageUploader catch block Co-authored-by: Ramil Minyukov (Akvelon INC) <v-rminyukov@microsoft.com> Co-authored-by: bongshin <bongshin@microsoft.com>
This commit is contained in:
Родитель
5bba2ba68b
Коммит
aa57f387c3
|
@ -1,454 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { Color, getColorConverter } from "../../core";
|
||||
import {
|
||||
ColorPalette,
|
||||
addPowerBIThemeColors,
|
||||
predefinedPalettes,
|
||||
} from "../resources";
|
||||
import { classNames } from "../utils";
|
||||
import { Button } from "../views/panels/widgets/controls";
|
||||
import { ColorSpaceDescription, ColorSpacePicker } from "./color_space_picker";
|
||||
import { AppStore } from "../stores";
|
||||
|
||||
const sRGB_to_HCL = getColorConverter("sRGB", "hcl");
|
||||
const HCL_to_sRGB = getColorConverter("hcl", "sRGB");
|
||||
|
||||
export function colorToCSS(color: Color) {
|
||||
return `rgb(${color.r.toFixed(0)},${color.g.toFixed(0)},${color.b.toFixed(
|
||||
0
|
||||
)})`;
|
||||
}
|
||||
|
||||
function HSVtoRGB(
|
||||
h: number,
|
||||
s: number,
|
||||
v: number
|
||||
): [number, number, number, boolean] {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
v /= 100;
|
||||
let r, g, b;
|
||||
const i = Math.floor(h * 6);
|
||||
const f = h * 6 - i;
|
||||
const p = v * (1 - s);
|
||||
const q = v * (1 - f * s);
|
||||
const t = v * (1 - (1 - f) * s);
|
||||
switch (i % 6) {
|
||||
case 0:
|
||||
(r = v), (g = t), (b = p);
|
||||
break;
|
||||
case 1:
|
||||
(r = q), (g = v), (b = p);
|
||||
break;
|
||||
case 2:
|
||||
(r = p), (g = v), (b = t);
|
||||
break;
|
||||
case 3:
|
||||
(r = p), (g = q), (b = v);
|
||||
break;
|
||||
case 4:
|
||||
(r = t), (g = p), (b = v);
|
||||
break;
|
||||
case 5:
|
||||
(r = v), (g = p), (b = q);
|
||||
break;
|
||||
}
|
||||
return [
|
||||
Math.max(0, Math.min(255, r * 255)),
|
||||
Math.max(0, Math.min(255, g * 255)),
|
||||
Math.max(0, Math.min(255, b * 255)),
|
||||
false,
|
||||
];
|
||||
}
|
||||
|
||||
function RGBtoHSV(r: number, g: number, b: number): [number, number, number] {
|
||||
const max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b),
|
||||
d = max - min,
|
||||
s = max === 0 ? 0 : d / max,
|
||||
v = max / 255;
|
||||
let h;
|
||||
|
||||
switch (max) {
|
||||
case min:
|
||||
h = 0;
|
||||
break;
|
||||
case r:
|
||||
h = g - b + d * (g < b ? 6 : 0);
|
||||
h /= 6 * d;
|
||||
break;
|
||||
case g:
|
||||
h = b - r + d * 2;
|
||||
h /= 6 * d;
|
||||
break;
|
||||
case b:
|
||||
h = r - g + d * 4;
|
||||
h /= 6 * d;
|
||||
break;
|
||||
}
|
||||
|
||||
return [h * 360, s * 100, v * 100];
|
||||
}
|
||||
|
||||
export class HSVColorPicker extends React.Component<
|
||||
{
|
||||
defaultValue: Color;
|
||||
onChange?: (newValue: Color) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
public static colorSpaces: ColorSpaceDescription[] = [
|
||||
{
|
||||
name: "Hue",
|
||||
description: "Saturation, Value | Hue",
|
||||
dimension1: { name: "Hue", range: [360, 0] },
|
||||
dimension2: { name: "Saturation", range: [0, 100] },
|
||||
dimension3: { name: "Value", range: [100, 0] },
|
||||
toRGB: HSVtoRGB,
|
||||
fromRGB: RGBtoHSV,
|
||||
},
|
||||
{
|
||||
name: "Saturation",
|
||||
description: "Hue, Value | Saturation",
|
||||
dimension1: { name: "Saturation", range: [100, 0] },
|
||||
dimension2: { name: "Hue", range: [360, 0] },
|
||||
dimension3: { name: "Value", range: [100, 0] },
|
||||
toRGB: (x1, x2, x3) => HSVtoRGB(x2, x1, x3),
|
||||
fromRGB: (r, g, b) => {
|
||||
const [h, s, v] = RGBtoHSV(r, g, b);
|
||||
return [s, h, v];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Value",
|
||||
description: "Hue, Saturation | Value",
|
||||
dimension1: { name: "Value", range: [100, 0] },
|
||||
dimension2: { name: "Hue", range: [360, 0] },
|
||||
dimension3: { name: "Saturation", range: [100, 0] },
|
||||
toRGB: (x1, x2, x3) => HSVtoRGB(x2, x3, x1),
|
||||
fromRGB: (r, g, b) => {
|
||||
const [h, s, v] = RGBtoHSV(r, g, b);
|
||||
return [v, h, s];
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<ColorSpacePicker
|
||||
{...this.props}
|
||||
colorSpaces={HSVColorPicker.colorSpaces}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class HCLColorPicker extends React.Component<
|
||||
{
|
||||
defaultValue: Color;
|
||||
onChange?: (newValue: Color) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
public static colorSpaces: ColorSpaceDescription[] = [
|
||||
{
|
||||
name: "Lightness",
|
||||
description: "Hue, Chroma | Lightness",
|
||||
dimension1: { name: "Lightness", range: [100, 0] },
|
||||
dimension2: { name: "Hue", range: [0, 360] },
|
||||
dimension3: { name: "Chroma", range: [100, 0] },
|
||||
toRGB: (x1: number, x2: number, x3: number) =>
|
||||
HCL_to_sRGB(x2, x3, x1) as [number, number, number, boolean],
|
||||
fromRGB: (r: number, g: number, b: number) => {
|
||||
const [h, c, l] = sRGB_to_HCL(r, g, b);
|
||||
return [l, h, c];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Hue",
|
||||
description: "Chroma, Lightness | Hue",
|
||||
dimension1: { name: "Hue", range: [0, 360] },
|
||||
dimension2: { name: "Chroma", range: [0, 100] },
|
||||
dimension3: { name: "Lightness", range: [100, 0] },
|
||||
toRGB: (x1: number, x2: number, x3: number) =>
|
||||
HCL_to_sRGB(x1, x2, x3) as [number, number, number, boolean],
|
||||
fromRGB: (r: number, g: number, b: number) =>
|
||||
sRGB_to_HCL(r, g, b) as [number, number, number],
|
||||
},
|
||||
{
|
||||
name: "Chroma",
|
||||
description: "Hue, Lightness | Chroma",
|
||||
dimension1: { name: "Chroma", range: [100, 0] },
|
||||
dimension2: { name: "Hue", range: [0, 360] },
|
||||
dimension3: { name: "Lightness", range: [100, 0] },
|
||||
toRGB: (x1: number, x2: number, x3: number) =>
|
||||
HCL_to_sRGB(x2, x1, x3) as [number, number, number, boolean],
|
||||
fromRGB: (r: number, g: number, b: number) => {
|
||||
const [h, c, l] = sRGB_to_HCL(r, g, b);
|
||||
return [c, h, l];
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<ColorSpacePicker
|
||||
{...this.props}
|
||||
colorSpaces={HCLColorPicker.colorSpaces}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ColorPickerProps {
|
||||
defaultValue?: Color;
|
||||
allowNull?: boolean;
|
||||
onPick?: (color: Color) => void;
|
||||
store?: AppStore;
|
||||
}
|
||||
|
||||
export interface ColorPickerState {
|
||||
currentPalette?: ColorPalette;
|
||||
currentPicker?: string;
|
||||
currentColor?: Color;
|
||||
}
|
||||
|
||||
export interface ColorGridProps {
|
||||
defaultValue?: Color;
|
||||
colors: Color[][];
|
||||
onClick?: (color: Color) => void;
|
||||
}
|
||||
export class ColorGrid extends React.PureComponent<ColorGridProps, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="color-grid">
|
||||
{this.props.colors.map((colors, index) => (
|
||||
<div className="color-row" key={`m${index}`}>
|
||||
{colors.map((color, i) => (
|
||||
<span
|
||||
key={`m${i}`}
|
||||
className={classNames("color-item", [
|
||||
"active",
|
||||
this.props.defaultValue != null &&
|
||||
colorToCSS(this.props.defaultValue) == colorToCSS(color),
|
||||
])}
|
||||
onClick={() => {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(color);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span style={{ backgroundColor: colorToCSS(color) }} />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface PaletteListProps {
|
||||
selected: ColorPalette;
|
||||
palettes: ColorPalette[];
|
||||
onClick?: (palette: ColorPalette) => void;
|
||||
}
|
||||
|
||||
export class PaletteList extends React.PureComponent<PaletteListProps, {}> {
|
||||
public render() {
|
||||
const palettes = this.props.palettes;
|
||||
|
||||
const groups: [string, ColorPalette[]][] = [];
|
||||
const group2Index = new Map<string, number>();
|
||||
for (const p of palettes) {
|
||||
const groupName = p.name.split("/")[0];
|
||||
let group: ColorPalette[];
|
||||
if (group2Index.has(groupName)) {
|
||||
group = groups[group2Index.get(groupName)][1];
|
||||
} else {
|
||||
group = [];
|
||||
group2Index.set(groupName, groups.length);
|
||||
groups.push([groupName, group]);
|
||||
}
|
||||
group.push(p);
|
||||
}
|
||||
return (
|
||||
<ul>
|
||||
{groups.map((group, index) => {
|
||||
return (
|
||||
<li key={`m${index}`}>
|
||||
<div className="label">{group[0]}</div>
|
||||
<ul>
|
||||
{group[1].map((x) => (
|
||||
<li
|
||||
key={x.name}
|
||||
className={classNames("item", [
|
||||
"active",
|
||||
this.props.selected == x,
|
||||
])}
|
||||
onClick={() => this.props.onClick(x)}
|
||||
>
|
||||
{x.name.split("/")[1]}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ColorPicker extends React.Component<
|
||||
ColorPickerProps,
|
||||
ColorPickerState
|
||||
> {
|
||||
constructor(props: ColorPickerProps) {
|
||||
super(props);
|
||||
addPowerBIThemeColors();
|
||||
if (this.props.defaultValue) {
|
||||
const colorCSS = colorToCSS(this.props.defaultValue);
|
||||
let matchedPalette: ColorPalette = null;
|
||||
for (const p of predefinedPalettes.filter((x) => x.type == "palette")) {
|
||||
for (const g of p.colors) {
|
||||
for (const c of g) {
|
||||
if (colorToCSS(c) == colorCSS) {
|
||||
matchedPalette = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchedPalette) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchedPalette) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchedPalette) {
|
||||
this.state = {
|
||||
currentPalette: matchedPalette,
|
||||
currentPicker: null,
|
||||
currentColor: this.props.defaultValue,
|
||||
};
|
||||
} else {
|
||||
this.state = {
|
||||
currentPalette: null,
|
||||
currentPicker: "hcl",
|
||||
currentColor: this.props.defaultValue,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.state = {
|
||||
currentPalette: predefinedPalettes.filter(
|
||||
(x) => x.name == "Palette/ColorBrewer"
|
||||
)[0],
|
||||
currentPicker: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className="color-picker">
|
||||
<section className="color-picker-left">
|
||||
<div className="color-picker-palettes-list">
|
||||
<ul>
|
||||
<li>
|
||||
<div className="label">ColorPicker</div>
|
||||
<ul>
|
||||
<li
|
||||
className={classNames("item", [
|
||||
"active",
|
||||
this.state.currentPicker == "hcl",
|
||||
])}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
currentPalette: null,
|
||||
currentPicker: "hcl",
|
||||
});
|
||||
}}
|
||||
>
|
||||
HCL Picker
|
||||
</li>
|
||||
<li
|
||||
className={classNames("item", [
|
||||
"active",
|
||||
this.state.currentPicker == "hsv",
|
||||
])}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
currentPalette: null,
|
||||
currentPicker: "hsv",
|
||||
});
|
||||
}}
|
||||
>
|
||||
HSV Picker
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<PaletteList
|
||||
palettes={predefinedPalettes.filter((x) => x.type == "palette")}
|
||||
selected={this.state.currentPalette}
|
||||
onClick={(p) => {
|
||||
this.setState({ currentPalette: p, currentPicker: null });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{this.props.allowNull ? (
|
||||
<div className="color-picker-null">
|
||||
<Button
|
||||
text={"none"}
|
||||
icon="ChromeClose"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
currentColor: null,
|
||||
});
|
||||
this.props.onPick(null);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
<section className="colors">
|
||||
{this.state.currentPalette != null ? (
|
||||
<ColorGrid
|
||||
colors={this.state.currentPalette.colors}
|
||||
defaultValue={this.state.currentColor}
|
||||
onClick={(c) => {
|
||||
this.props.onPick(c);
|
||||
this.setState({ currentColor: c });
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{this.state.currentPicker == "hcl" ? (
|
||||
<HCLColorPicker
|
||||
defaultValue={this.state.currentColor || { r: 0, g: 0, b: 0 }}
|
||||
onChange={(c) => {
|
||||
this.props.onPick(c);
|
||||
this.setState({ currentColor: c });
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{this.state.currentPicker == "hsv" ? (
|
||||
<HSVColorPicker
|
||||
defaultValue={this.state.currentColor || { r: 0, g: 0, b: 0 }}
|
||||
onChange={(c) => {
|
||||
this.props.onPick(c);
|
||||
this.setState({ currentColor: c });
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,19 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as Hammer from "hammerjs";
|
||||
import * as React from "react";
|
||||
import {
|
||||
Color,
|
||||
colorFromHTMLColor,
|
||||
colorToHTMLColor,
|
||||
colorToHTMLColorHEX,
|
||||
prettyNumber,
|
||||
} from "../../core";
|
||||
import { Select } from "../views/panels/widgets/controls";
|
||||
import { Color, colorToHTMLColor } from "../../core";
|
||||
import { ColorSpaceSelect } from "./colors/color_space_select";
|
||||
import { ColorHexInput } from "./colors/color_hex_input";
|
||||
import { ColorDimensionInput } from "./colors/color_dimension_input";
|
||||
import { ColorRgbInput } from "./colors/color_rgb_input";
|
||||
|
||||
export interface ColorSpaceDescription {
|
||||
name: string;
|
||||
|
@ -42,14 +36,6 @@ export interface ColorSpacePickerState {
|
|||
x3: number;
|
||||
}
|
||||
|
||||
function clipToRange(num: number, range: [number, number]) {
|
||||
if (range[0] < range[1]) {
|
||||
return Math.max(range[0], Math.min(range[1], num));
|
||||
} else {
|
||||
return Math.max(range[1], Math.min(range[0], num));
|
||||
}
|
||||
}
|
||||
|
||||
// A general three component color picker
|
||||
export class ColorSpacePicker extends React.Component<
|
||||
ColorSpacePickerProps,
|
||||
|
@ -74,8 +60,6 @@ export class ColorSpacePicker extends React.Component<
|
|||
};
|
||||
}
|
||||
|
||||
public componentWillUpdate() {}
|
||||
|
||||
public reset() {
|
||||
const props = this.props;
|
||||
const defaultValue = props.defaultValue || { r: 0, g: 0, b: 0 };
|
||||
|
@ -171,7 +155,7 @@ export class ColorSpacePicker extends React.Component<
|
|||
/>
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
|
||||
public render() {
|
||||
const currentColor = this.state.desc.toRGB(
|
||||
this.state.x1,
|
||||
|
@ -186,24 +170,12 @@ export class ColorSpacePicker extends React.Component<
|
|||
<section className="palette-z">{this.renderZ()}</section>
|
||||
<section className="values">
|
||||
<div className="row">
|
||||
<Select
|
||||
value={this.state.desc.name}
|
||||
showText={true}
|
||||
options={this.props.colorSpaces.map((x) => x.name)}
|
||||
labels={this.props.colorSpaces.map((x) => x.description)}
|
||||
onChange={(v) => {
|
||||
for (const sp of this.props.colorSpaces) {
|
||||
if (sp.name == v) {
|
||||
const [r, g, b] = this.state.desc.toRGB(
|
||||
this.state.x1,
|
||||
this.state.x2,
|
||||
this.state.x3
|
||||
);
|
||||
const [x1, x2, x3] = sp.fromRGB(r, g, b);
|
||||
this.setState({ desc: sp, x1, x2, x3 });
|
||||
}
|
||||
}
|
||||
}}
|
||||
<ColorSpaceSelect
|
||||
colorSpaces={this.props.colorSpaces}
|
||||
state={this.state}
|
||||
updateState={(value: ColorSpacePickerState) =>
|
||||
this.setState(value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
|
@ -214,21 +186,10 @@ export class ColorSpacePicker extends React.Component<
|
|||
</span>
|
||||
</div>
|
||||
<div className="column">
|
||||
<label>HEX</label>
|
||||
<InputField
|
||||
defaultValue={colorToHTMLColorHEX(rgb)}
|
||||
onEnter={(v) => {
|
||||
const color = colorFromHTMLColor(v);
|
||||
if (color) {
|
||||
const [x1, x2, x3] = this.state.desc.fromRGB(
|
||||
color.r,
|
||||
color.g,
|
||||
color.b
|
||||
);
|
||||
this.setState({ x1, x2, x3 }, () => this.raiseChange());
|
||||
|
||||
return true;
|
||||
}
|
||||
<ColorHexInput
|
||||
state={this.state}
|
||||
updateState={(value) => {
|
||||
this.setState(value, () => this.raiseChange());
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -237,116 +198,42 @@ export class ColorSpacePicker extends React.Component<
|
|||
<div className="columns">
|
||||
<div className="column">
|
||||
<div className="row">
|
||||
<label>{this.state.desc.dimension1.name}</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(this.state.x1, 1)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = clipToRange(
|
||||
num,
|
||||
this.state.desc.dimension1.range
|
||||
);
|
||||
this.setState({ x1: num }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
<ColorDimensionInput
|
||||
defaultValue={this.state.x1}
|
||||
range={this.state.desc.dimension1.range}
|
||||
updateState={(num) => {
|
||||
this.setState({ x1: num }, () => this.raiseChange());
|
||||
}}
|
||||
title={this.state.desc.dimension1.name}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>{this.state.desc.dimension2.name}</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(this.state.x2, 1)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = clipToRange(
|
||||
num,
|
||||
this.state.desc.dimension2.range
|
||||
);
|
||||
this.setState({ x2: num }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
<ColorDimensionInput
|
||||
defaultValue={this.state.x2}
|
||||
range={this.state.desc.dimension2.range}
|
||||
updateState={(num) => {
|
||||
this.setState({ x2: num }, () => this.raiseChange());
|
||||
}}
|
||||
title={this.state.desc.dimension2.name}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>{this.state.desc.dimension3.name}</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(this.state.x3, 1)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = clipToRange(
|
||||
num,
|
||||
this.state.desc.dimension3.range
|
||||
);
|
||||
this.setState({ x3: num }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="column">
|
||||
<div className="row">
|
||||
<label>R</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.r, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.state.desc.fromRGB(
|
||||
num,
|
||||
rgb.g,
|
||||
rgb.b
|
||||
);
|
||||
this.setState({ x1, x2, x3 }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>G</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.g, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.state.desc.fromRGB(
|
||||
rgb.r,
|
||||
num,
|
||||
rgb.b
|
||||
);
|
||||
this.setState({ x1, x2, x3 }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>B</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.b, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.state.desc.fromRGB(
|
||||
rgb.r,
|
||||
rgb.g,
|
||||
num
|
||||
);
|
||||
this.setState({ x1, x2, x3 }, () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
<ColorDimensionInput
|
||||
defaultValue={this.state.x3}
|
||||
range={this.state.desc.dimension3.range}
|
||||
updateState={(num) => {
|
||||
this.setState({ x3: num }, () => this.raiseChange());
|
||||
}}
|
||||
title={this.state.desc.dimension3.name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ColorRgbInput
|
||||
state={this.state}
|
||||
updateState={(value) => {
|
||||
this.setState(value, () => this.raiseChange());
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
@ -360,7 +247,10 @@ export interface InputFieldProps {
|
|||
onEnter?: (value: string) => boolean;
|
||||
}
|
||||
|
||||
export class InputField extends React.Component<InputFieldProps, {}> {
|
||||
export class InputField extends React.Component<
|
||||
InputFieldProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public inputElement: HTMLInputElement;
|
||||
|
||||
public componentWillUpdate(newProps: InputFieldProps) {
|
||||
|
@ -448,7 +338,10 @@ interface XYCanvasProps {
|
|||
onMove: (nx: number, ny: number, isEnd: boolean) => void;
|
||||
}
|
||||
|
||||
class XYCanvas extends React.PureComponent<XYCanvasProps, {}> {
|
||||
class XYCanvas extends React.PureComponent<
|
||||
XYCanvasProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public refs: {
|
||||
canvasElement: HTMLCanvasElement;
|
||||
};
|
||||
|
@ -572,7 +465,10 @@ interface ZCanvasProps {
|
|||
onMove: (nz: number, isEnd: boolean) => void;
|
||||
}
|
||||
|
||||
class ZCanvas extends React.PureComponent<ZCanvasProps, {}> {
|
||||
class ZCanvas extends React.PureComponent<
|
||||
ZCanvasProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public refs: {
|
||||
canvasElement: HTMLCanvasElement;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import { prettyNumber } from "../../../core";
|
||||
import { InputField } from "../color_space_picker";
|
||||
|
||||
function clipToRange(num: number, range: [number, number]) {
|
||||
if (range[0] < range[1]) {
|
||||
return Math.max(range[0], Math.min(range[1], num));
|
||||
} else {
|
||||
return Math.max(range[1], Math.min(range[0], num));
|
||||
}
|
||||
}
|
||||
|
||||
interface ColorDimensionInputProps {
|
||||
title: string;
|
||||
defaultValue: number;
|
||||
range: [number, number];
|
||||
updateState: (state: number) => void;
|
||||
}
|
||||
|
||||
export class ColorDimensionInput extends React.Component<
|
||||
ColorDimensionInputProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<label>{this.props.title}</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(this.props.defaultValue, 1)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = clipToRange(num, this.props.range);
|
||||
this.props.updateState(num);
|
||||
// , () => this.raiseChange());
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import { colorFromHTMLColor, colorToHTMLColorHEX } from "../../../core";
|
||||
import { ColorSpacePickerState, InputField } from "../color_space_picker";
|
||||
|
||||
interface ColorHexInputProps {
|
||||
state: ColorSpacePickerState;
|
||||
updateState: (state: ColorSpacePickerState) => void;
|
||||
}
|
||||
|
||||
export class ColorHexInput extends React.Component<
|
||||
ColorHexInputProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
render() {
|
||||
const currentColor = this.props.state.desc.toRGB(
|
||||
this.props.state.x1,
|
||||
this.props.state.x2,
|
||||
this.props.state.x3
|
||||
);
|
||||
const rgb = { r: currentColor[0], g: currentColor[1], b: currentColor[2] };
|
||||
|
||||
return (
|
||||
<>
|
||||
<label>HEX</label>
|
||||
<InputField
|
||||
defaultValue={colorToHTMLColorHEX(rgb)}
|
||||
onEnter={(v) => {
|
||||
const color = colorFromHTMLColor(v);
|
||||
if (color) {
|
||||
const [x1, x2, x3] = this.props.state.desc.fromRGB(
|
||||
color.r,
|
||||
color.g,
|
||||
color.b
|
||||
);
|
||||
this.props.updateState({
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
desc: this.props.state.desc,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import { prettyNumber } from "../../../core";
|
||||
import { ColorSpacePickerState, InputField } from "../color_space_picker";
|
||||
|
||||
interface ColorRgbInputProps {
|
||||
state: ColorSpacePickerState;
|
||||
updateState: (state: ColorSpacePickerState) => void;
|
||||
}
|
||||
|
||||
export class ColorRgbInput extends React.Component<
|
||||
ColorRgbInputProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
render() {
|
||||
const currentColor = this.props.state.desc.toRGB(
|
||||
this.props.state.x1,
|
||||
this.props.state.x2,
|
||||
this.props.state.x3
|
||||
);
|
||||
const rgb = { r: currentColor[0], g: currentColor[1], b: currentColor[2] };
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="column">
|
||||
<div className="row">
|
||||
<label>R</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.r, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.props.state.desc.fromRGB(
|
||||
num,
|
||||
rgb.g,
|
||||
rgb.b
|
||||
);
|
||||
this.props.updateState({
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
desc: this.props.state.desc,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>G</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.g, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.props.state.desc.fromRGB(
|
||||
rgb.r,
|
||||
num,
|
||||
rgb.b
|
||||
);
|
||||
this.props.updateState({
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
desc: this.props.state.desc,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<label>B</label>
|
||||
<InputField
|
||||
defaultValue={prettyNumber(rgb.b, 0)}
|
||||
onEnter={(v) => {
|
||||
let num = parseFloat(v);
|
||||
if (num == num && num != null) {
|
||||
num = Math.max(0, Math.min(255, num));
|
||||
const [x1, x2, x3] = this.props.state.desc.fromRGB(
|
||||
rgb.r,
|
||||
rgb.g,
|
||||
num
|
||||
);
|
||||
this.props.updateState({
|
||||
x1,
|
||||
x2,
|
||||
x3,
|
||||
desc: this.props.state.desc,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import { ComboBox, IComboBoxOption } from "@fluentui/react";
|
||||
import { defultComponentsHeight } from "../../views/panels/widgets/controls/fluentui_customized_components";
|
||||
import { Color } from "../../../core";
|
||||
import {
|
||||
ColorSpaceDescription,
|
||||
ColorSpacePickerState,
|
||||
} from "../color_space_picker";
|
||||
|
||||
interface ColorSpaceSelectProps {
|
||||
onChange?: (newValue: Color) => void;
|
||||
colorSpaces: ColorSpaceDescription[];
|
||||
state: ColorSpacePickerState;
|
||||
updateState: (state: ColorSpacePickerState) => void;
|
||||
}
|
||||
|
||||
export class ColorSpaceSelect extends React.Component<
|
||||
ColorSpaceSelectProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
render() {
|
||||
const options: IComboBoxOption[] = this.props.colorSpaces.map((x) => {
|
||||
return { key: x.name, text: x.name };
|
||||
});
|
||||
|
||||
return (
|
||||
<ComboBox
|
||||
options={options}
|
||||
defaultSelectedKey={this.props.state.desc.name}
|
||||
onChange={(event, option) => {
|
||||
if (option) {
|
||||
for (const sp of this.props.colorSpaces) {
|
||||
if (sp.name == option.key) {
|
||||
const [r, g, b] = this.props.state.desc.toRGB(
|
||||
this.props.state.x1,
|
||||
this.props.state.x2,
|
||||
this.props.state.x3
|
||||
);
|
||||
const [x1, x2, x3] = sp.fromRGB(r, g, b);
|
||||
this.props.updateState({ desc: sp, x1, x2, x3 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
styles={{
|
||||
root: {
|
||||
...defultComponentsHeight,
|
||||
},
|
||||
input: {
|
||||
width: "100px !important",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -96,7 +93,7 @@ export interface ClickableSVGElementProps {
|
|||
|
||||
export class ClickableSVGElement extends React.Component<
|
||||
ClickableSVGElementProps,
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public refs: {
|
||||
container: SVGGElement;
|
||||
|
|
|
@ -14,7 +14,6 @@ export * from "./draggable";
|
|||
export * from "./selecting";
|
||||
export * from "./split_panes";
|
||||
export * from "./zoomable";
|
||||
export * from "./color_picker";
|
||||
export * from "./gradient_picker";
|
||||
export * from "./minimizable_panel";
|
||||
export * from "./error_boundary";
|
||||
|
|
|
@ -4,12 +4,7 @@
|
|||
import * as React from "react";
|
||||
import * as R from "../resources";
|
||||
|
||||
import {
|
||||
EventSubscription,
|
||||
Prototypes,
|
||||
MessageType,
|
||||
messageTypes,
|
||||
} from "../../core";
|
||||
import { EventSubscription, MessageType, messageTypes } from "../../core";
|
||||
|
||||
import { AppStore } from "../stores";
|
||||
import { ContextedComponent } from "../context_component";
|
||||
|
@ -17,13 +12,6 @@ import { Element } from "../../core/specification";
|
|||
import { SVGImageIcon } from ".";
|
||||
import { RemoveMessage } from "../actions/actions";
|
||||
|
||||
// eslint-disable-next-line
|
||||
function getObjectIcon(classID: string) {
|
||||
return R.getSVGIcon(
|
||||
Prototypes.ObjectClasses.GetMetadata(classID).iconPath || "object"
|
||||
);
|
||||
}
|
||||
|
||||
export class MessagePanel extends ContextedComponent<
|
||||
{
|
||||
store: AppStore;
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { getSVGIcon } from "../resources";
|
||||
|
@ -12,7 +9,10 @@ import { classNames } from "../utils";
|
|||
import { DefaultButton } from "@fluentui/react";
|
||||
import { PanelHeaderStyles } from "../views/panels/widgets/controls/fluentui_customized_components";
|
||||
|
||||
export class MinimizablePanelView extends React.Component<{}, {}> {
|
||||
export class MinimizablePanelView extends React.Component<
|
||||
Record<string, unknown>,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return <div className="minimizable-panel-view">{this.props.children}</div>;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
import * as Hammer from "hammerjs";
|
||||
|
||||
// eslint-disable-next-line
|
||||
export interface ScrollViewProps {}
|
||||
import { noop } from "../utils/noop";
|
||||
|
||||
export interface ScrollViewState {
|
||||
height: number;
|
||||
|
@ -12,7 +10,7 @@ export interface ScrollViewState {
|
|||
}
|
||||
|
||||
export class ScrollView extends React.Component<
|
||||
ScrollViewProps,
|
||||
Record<string, unknown>,
|
||||
ScrollViewState
|
||||
> {
|
||||
public refs: {
|
||||
|
@ -23,8 +21,7 @@ export class ScrollView extends React.Component<
|
|||
|
||||
public componentDidMount() {
|
||||
this.hammer = new Hammer(this.refs.container);
|
||||
// eslint-disable-next-line
|
||||
this.hammer.on("panstart", () => {});
|
||||
this.hammer.on("panstart", noop);
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
|
|
|
@ -2,15 +2,9 @@
|
|||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
|
||||
// eslint-disable-next-line
|
||||
export interface SplitPaneViewProps {}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export interface SplitPaneViewState {}
|
||||
|
||||
export class HorizontalSplitPaneView extends React.Component<
|
||||
SplitPaneViewProps,
|
||||
SplitPaneViewState
|
||||
Record<string, unknown>,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { SVGImageIcon } from "./icons";
|
||||
|
@ -14,7 +11,10 @@ export interface TabsViewProps {
|
|||
onSelect: (tabName: string) => void;
|
||||
}
|
||||
|
||||
export class TabsView extends React.Component<TabsViewProps, {}> {
|
||||
export class TabsView extends React.Component<
|
||||
TabsViewProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="charticulator__tabs-view">
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { EventEmitter, EventSubscription } from "../../core";
|
||||
|
@ -50,7 +47,6 @@ function findParentPopup(anchor: Element) {
|
|||
anchor = anchor.parentElement;
|
||||
}
|
||||
}
|
||||
export interface PopupResult {}
|
||||
|
||||
export class PopupContext extends EventEmitter {
|
||||
public readonly id: string;
|
||||
|
@ -183,7 +179,10 @@ export interface PopupViewProps {
|
|||
controller: PopupController;
|
||||
}
|
||||
|
||||
export class PopupContainer extends React.Component<PopupViewProps, {}> {
|
||||
export class PopupContainer extends React.Component<
|
||||
PopupViewProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public token: EventSubscription;
|
||||
private popupContainer: HTMLDivElement;
|
||||
constructor(props: PopupViewProps) {
|
||||
|
@ -300,13 +299,15 @@ export class PopupContainer extends React.Component<PopupViewProps, {}> {
|
|||
}
|
||||
}
|
||||
|
||||
interface PopupViewComponentProps {
|
||||
context: PopupContext;
|
||||
className?: string;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export class PopupView extends React.Component<
|
||||
{
|
||||
context: PopupContext;
|
||||
className?: string;
|
||||
width?: number;
|
||||
},
|
||||
{}
|
||||
PopupViewComponentProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
private popupContainer: HTMLDivElement;
|
||||
|
||||
|
@ -501,7 +502,7 @@ export class ModalView extends React.Component<
|
|||
context: PopupContext;
|
||||
type?: string;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
const type = this.props.type || "default";
|
||||
|
|
|
@ -70,8 +70,7 @@ export class ResizeListeners {
|
|||
}
|
||||
|
||||
private timerCallback() {
|
||||
// eslint-disable-next-line
|
||||
for (const [element, info] of this.entries) {
|
||||
for (const [, info] of this.entries) {
|
||||
info.timerCallback();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,16 @@ import {
|
|||
import { ColorFilter, NumberModifier } from "../../core/graphics";
|
||||
|
||||
// adapted from https://stackoverflow.com/a/20820649
|
||||
// eslint-disable-next-line
|
||||
function desaturate(color: Color, amount: number) {
|
||||
const { r, g, b } = color;
|
||||
const l = 0.3 * r + 0.6 * g + 0.1 * b;
|
||||
return {
|
||||
r: Math.min(r + amount * (l - r), 255),
|
||||
g: Math.min(g + amount * (l - g), 255),
|
||||
b: Math.min(b + amount * (l - b), 255),
|
||||
};
|
||||
}
|
||||
// probably useful
|
||||
// function desaturate(color: Color, amount: number) {
|
||||
// const { r, g, b } = color;
|
||||
// const l = 0.3 * r + 0.6 * g + 0.1 * b;
|
||||
// return {
|
||||
// r: Math.min(r + amount * (l - r), 255),
|
||||
// g: Math.min(g + amount * (l - g), 255),
|
||||
// b: Math.min(b + amount * (l - b), 255),
|
||||
// };
|
||||
// }
|
||||
|
||||
const srgb2lab = getColorConverter("sRGB", "lab");
|
||||
const lab2srgb = getColorConverter("lab", "sRGB");
|
||||
|
|
|
@ -95,8 +95,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
);
|
||||
|
||||
for (const key in action.mappings) {
|
||||
// eslint-disable-next-line
|
||||
if (action.mappings.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(action.mappings, key)) {
|
||||
const [value, mapping] = action.mappings[key];
|
||||
if (mapping != null) {
|
||||
if (mapping.type == MappingType._element) {
|
||||
|
@ -175,8 +174,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
}
|
||||
const layoutState = this.chartState.elements[idx];
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
// Remove current mapping and any snapping constraint
|
||||
|
@ -297,8 +295,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
this.saveHistory();
|
||||
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
this.chartState.attributes[key] = action.updates[key];
|
||||
|
|
|
@ -48,16 +48,14 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
this.saveHistory();
|
||||
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
delete action.glyph.mappings[key];
|
||||
}
|
||||
this.forAllGlyph(action.glyph, (glyphState) => {
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
glyphState.attributes[key] = action.updates[key];
|
||||
|
@ -96,8 +94,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
|
||||
let attributesSet = false;
|
||||
for (const attr in action.mappings) {
|
||||
// eslint-disable-next-line
|
||||
if (action.mappings.hasOwnProperty(attr)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.mappings, attr)) {
|
||||
const [value, mapping] = action.mappings[attr];
|
||||
if (mapping != null) {
|
||||
if (mapping.type == MappingType._element) {
|
||||
|
|
|
@ -22,8 +22,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
|
||||
MR.add(Actions.UpdateMarkAttribute, function (action) {
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
delete action.mark.mappings[key];
|
||||
|
@ -48,8 +47,7 @@ export default function (REG: ActionHandlerRegistry<AppStore, Actions.Action>) {
|
|||
)) {
|
||||
if (mark == action.mark) {
|
||||
for (const key in action.updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!action.updates.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(action.updates, key)) {
|
||||
continue;
|
||||
}
|
||||
markState.attributes[key] = action.updates[key];
|
||||
|
|
|
@ -586,8 +586,12 @@ export class AppStore extends BaseStore {
|
|||
if (!plotSegment) {
|
||||
return 0;
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
if (this.selectedGlyphIndex.hasOwnProperty(plotSegmentID)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
this.selectedGlyphIndex,
|
||||
plotSegmentID
|
||||
)
|
||||
) {
|
||||
const idx = this.selectedGlyphIndex[plotSegmentID];
|
||||
if (idx >= plotSegment.state.dataRowIndices.length) {
|
||||
this.selectedGlyphIndex[plotSegmentID] = 0;
|
||||
|
@ -769,8 +773,7 @@ export class AppStore extends BaseStore {
|
|||
|
||||
const findScale = (mappings: Specification.Mappings) => {
|
||||
for (const name in mappings) {
|
||||
// eslint-disable-next-line
|
||||
if (!mappings.hasOwnProperty(name)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(mappings, name)) {
|
||||
continue;
|
||||
}
|
||||
if (mappings[name].type == MappingType.scale) {
|
||||
|
|
|
@ -307,8 +307,7 @@ export class Migrator {
|
|||
for (const glyph of state.chart.glyphs) {
|
||||
for (const mark of glyph.marks) {
|
||||
for (const key in mark.mappings) {
|
||||
// eslint-disable-next-line
|
||||
if (mark.mappings.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(mark.mappings, key)) {
|
||||
const mapping = mark.mappings[key];
|
||||
if (mapping.type == MappingType.scale) {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
|
|
|
@ -78,8 +78,7 @@ export class ChartTemplateBuilder {
|
|||
}
|
||||
|
||||
public addTable(table: string) {
|
||||
// eslint-disable-next-line
|
||||
if (!this.tableColumns.hasOwnProperty(table)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.tableColumns, table)) {
|
||||
this.tableColumns[table] = new Set();
|
||||
}
|
||||
}
|
||||
|
@ -96,15 +95,14 @@ export class ChartTemplateBuilder {
|
|||
const notRawColumn = tableObject.columns.find(
|
||||
(col) => col.metadata.rawColumnName === column.name
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
if (this.tableColumns.hasOwnProperty(table)) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.tableColumns, table)) {
|
||||
this.tableColumns[table].add(notRawColumn.name);
|
||||
} else {
|
||||
this.tableColumns[table] = new Set([notRawColumn.name]);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
if (this.tableColumns.hasOwnProperty(table)) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.tableColumns, table)) {
|
||||
this.tableColumns[table].add(columnName);
|
||||
} else {
|
||||
this.tableColumns[table] = new Set([columnName]);
|
||||
|
@ -553,8 +551,7 @@ export class ChartTemplateBuilder {
|
|||
}
|
||||
|
||||
const mappings = item.object.mappings;
|
||||
// eslint-disable-next-line
|
||||
for (const [attr, mapping] of forEachMapping(mappings)) {
|
||||
for (const [, mapping] of forEachMapping(mappings)) {
|
||||
if (mapping.type == MappingType.scale) {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
scaleMapping.expression = this.trackColumnFromExpression(
|
||||
|
@ -588,8 +585,7 @@ export class ChartTemplateBuilder {
|
|||
template.tables = this.dataset.tables
|
||||
.map((table) => {
|
||||
if (
|
||||
// eslint-disable-next-line
|
||||
this.tableColumns.hasOwnProperty(table.name) &&
|
||||
Object.prototype.hasOwnProperty.call(this.tableColumns, table.name) &&
|
||||
(this.usedColumns[table.name] || noUsedColumns)
|
||||
) {
|
||||
return {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export function useLocalStorage<
|
||||
Type extends string | number | boolean | object
|
||||
Type extends string | number | boolean | Record<string, unknown>
|
||||
>(initialValue: Type, storageKey: string): [Type, (newValue: Type) => void] {
|
||||
const [currentValue, setCurrentValue] = useState<Type>(() => {
|
||||
try {
|
||||
|
|
|
@ -124,8 +124,7 @@ export function readFileAsDataUrl(file: File): Promise<string> {
|
|||
}
|
||||
|
||||
export function getExtensionFromFileName(filename: string) {
|
||||
// eslint-disable-next-line
|
||||
const m = filename.match(/\.([^\.]+)$/);
|
||||
const m = filename.match(/\.([^.]+)$/);
|
||||
if (m) {
|
||||
return m[1].toLowerCase();
|
||||
} else {
|
||||
|
@ -134,8 +133,7 @@ export function getExtensionFromFileName(filename: string) {
|
|||
}
|
||||
|
||||
export function getFileNameWithoutExtension(filename: string) {
|
||||
// eslint-disable-next-line
|
||||
return filename.replace(/\.([^\.]+)$/, "");
|
||||
return filename.replace(/\.([^.]+)$/, "");
|
||||
}
|
||||
|
||||
export function showOpenFileDialog(accept?: string[]): Promise<File> {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export const noop = () => {
|
||||
//do nothing
|
||||
};
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -13,7 +10,10 @@ export interface CanvasBarProps {
|
|||
onReset?: () => void;
|
||||
}
|
||||
|
||||
export class CanvasBar extends React.Component<CanvasBarProps, {}> {
|
||||
export class CanvasBar extends React.Component<
|
||||
CanvasBarProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
const width = this.props.canvasWidth;
|
||||
const height = 20;
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOMServer from "react-dom/server";
|
||||
|
@ -20,7 +17,7 @@ export interface ChartDisplayViewProps {
|
|||
|
||||
export class ChartDisplayView extends React.Component<
|
||||
ChartDisplayViewProps,
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
const chartState = this.props.manager.chartState;
|
||||
|
|
|
@ -399,8 +399,7 @@ export class ChartEditorView
|
|||
);
|
||||
const opt = JSON.parse(options);
|
||||
for (const key in opt) {
|
||||
// eslint-disable-next-line
|
||||
if (opt.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(opt, key)) {
|
||||
attributes[key] = opt[key];
|
||||
}
|
||||
}
|
||||
|
@ -787,8 +786,7 @@ export class ChartEditorView
|
|||
const updates = session.getUpdates(session.handleEnd(e));
|
||||
if (updates) {
|
||||
for (const name in updates) {
|
||||
// eslint-disable-next-line
|
||||
if (!updates.hasOwnProperty(name)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(updates, name)) {
|
||||
continue;
|
||||
}
|
||||
new Actions.SetChartAttribute(name, {
|
||||
|
|
|
@ -486,8 +486,7 @@ export class CreatingComponentFromCreatingInteraction extends React.Component<
|
|||
const mappings: { [name: string]: [number, Specification.Mapping] } = {};
|
||||
const attributes: { [name: string]: Specification.AttributeValue } = {};
|
||||
for (const attr in desc.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (inMappings.hasOwnProperty(attr)) {
|
||||
if (Object.prototype.hasOwnProperty.call(inMappings, attr)) {
|
||||
const name = desc.mapping[attr];
|
||||
mappings[name] = inMappings[attr];
|
||||
}
|
||||
|
|
|
@ -323,12 +323,10 @@ export class EditingLink extends React.Component<
|
|||
undefined
|
||||
);
|
||||
if (rowItem) {
|
||||
// eslint-disable-next-line
|
||||
const [iRow0, i0] = tables[0].id2RowGlyphIndex.get(
|
||||
const [, i0] = tables[0].id2RowGlyphIndex.get(
|
||||
rowItem.source_id?.toString()
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
const [iRow1, i1] = tables[1].id2RowGlyphIndex.get(
|
||||
const [, i1] = tables[1].id2RowGlyphIndex.get(
|
||||
rowItem.target_id?.toString()
|
||||
);
|
||||
glyphs = [
|
||||
|
|
|
@ -25,12 +25,9 @@ export interface HandlesViewProps {
|
|||
) => void;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export interface HandlesViewState {}
|
||||
|
||||
export class HandlesView extends React.Component<
|
||||
HandlesViewProps,
|
||||
HandlesViewState
|
||||
Record<string, unknown>
|
||||
> {
|
||||
// eslint-disable-next-line
|
||||
public renderHandle(handle: Prototypes.Handles.Description) {
|
||||
|
|
|
@ -543,8 +543,7 @@ export class SingleMarkView
|
|||
const opt = JSON.parse(data.options);
|
||||
this.scheduleAutoFit();
|
||||
for (const key in opt) {
|
||||
// eslint-disable-next-line
|
||||
if (opt.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(opt, key)) {
|
||||
attributes[key] = opt[key];
|
||||
}
|
||||
}
|
||||
|
@ -1238,8 +1237,7 @@ export class SingleMarkView
|
|||
|
||||
public renderSnappingGuidesLabels() {
|
||||
const allLabels: Prototypes.SnappingGuides.Description[] = [];
|
||||
// eslint-disable-next-line
|
||||
for (const [element, elementState] of zip(
|
||||
for (const [, elementState] of zip(
|
||||
this.props.glyph.marks,
|
||||
this.props.glyphState.marks
|
||||
)) {
|
||||
|
@ -1431,8 +1429,7 @@ export class SingleMarkView
|
|||
this.dispatch(new Actions.SetCurrentTool(null));
|
||||
const opt = JSON.parse(currentCreationOptions);
|
||||
for (const key in opt) {
|
||||
// eslint-disable-next-line
|
||||
if (opt.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(opt, key)) {
|
||||
attributes[key] = opt[key];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,6 @@ export interface DatasetViewProps {
|
|||
store: AppStore;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export interface DatasetViewState {}
|
||||
|
||||
/**
|
||||
* Component for displaying dataset on the left side of app
|
||||
*
|
||||
|
@ -54,7 +51,7 @@ export interface DatasetViewState {}
|
|||
*/
|
||||
export class DatasetView extends React.Component<
|
||||
DatasetViewProps,
|
||||
DatasetViewState
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public componentDidMount() {
|
||||
this.props.store.addListener(AppStore.EVENT_DATASET, () =>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import { DefaultButton } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
|
@ -27,6 +24,7 @@ import { AppStore } from "../../stores";
|
|||
import { ExportTemplateTarget } from "../../template";
|
||||
import { classNames } from "../../utils";
|
||||
import { InputImageProperty, Button } from "../panels/widgets/controls";
|
||||
import { noop } from "../../utils/noop";
|
||||
|
||||
export class InputGroup extends React.Component<
|
||||
{
|
||||
|
@ -34,7 +32,7 @@ export class InputGroup extends React.Component<
|
|||
label: string;
|
||||
onChange: (newValue: string) => void;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
private ref: HTMLInputElement;
|
||||
|
||||
|
@ -130,7 +128,7 @@ export class ExportHTMLView extends React.Component<
|
|||
{
|
||||
store: AppStore;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
@ -280,7 +278,7 @@ export class ExportTemplateView extends React.Component<
|
|||
exportKind: string;
|
||||
store: AppStore;
|
||||
},
|
||||
{}
|
||||
ExportTemplateViewState
|
||||
> {
|
||||
public state = this.getDefaultState(this.props.exportKind);
|
||||
|
||||
|
@ -513,9 +511,9 @@ export class ExportTemplateView extends React.Component<
|
|||
const keyAutoDomainMin = "autoDomainMin";
|
||||
const keyAutoDomainMax = "autoDomainMax";
|
||||
|
||||
let onClickAutoDomainMin = () => {};
|
||||
let onClickAutoDomainMin = noop;
|
||||
|
||||
let onClickAutoDomainMax = () => {};
|
||||
let onClickAutoDomainMax = noop;
|
||||
|
||||
let getAutoDomainMinPropertyValue: () => boolean = null;
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { strings } from "../../../strings";
|
||||
|
@ -10,12 +7,14 @@ import { Actions } from "../../actions";
|
|||
import { AppStore } from "../../stores";
|
||||
import { ImportDataView } from "./import_data_view";
|
||||
|
||||
interface FileViewNewProps {
|
||||
store: AppStore;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export class FileViewNew extends React.Component<
|
||||
{
|
||||
store: AppStore;
|
||||
onClose: () => void;
|
||||
},
|
||||
{}
|
||||
FileViewNewProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { useContext } from "react";
|
||||
|
@ -171,7 +168,7 @@ export class FileViewOptions extends React.Component<
|
|||
onClose: () => void;
|
||||
store: AppStore;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
@ -43,12 +40,14 @@ import { getConfig } from "../config";
|
|||
import { EditorType } from "../stores/app_store";
|
||||
import { DeleteDialog } from "./panels/delete_dialog";
|
||||
|
||||
interface HelpButtonProps {
|
||||
hideReportIssues: boolean;
|
||||
handlers: MenuBarHandlers;
|
||||
}
|
||||
|
||||
export class HelpButton extends React.Component<
|
||||
{
|
||||
hideReportIssues: boolean;
|
||||
handlers: MenuBarHandlers;
|
||||
},
|
||||
{}
|
||||
HelpButtonProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
const contactUsLinkProps: React.AnchorHTMLAttributes<HTMLAnchorElement> = {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../resources";
|
||||
|
@ -30,7 +27,7 @@ export class AttributePanel extends React.Component<
|
|||
{
|
||||
store: AppStore;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public tokens: EventSubscription[] = [];
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../resources";
|
||||
|
@ -21,7 +18,10 @@ import {
|
|||
import { classNames } from "../../utils";
|
||||
import { Button } from "./widgets/controls";
|
||||
|
||||
export class ObjectListEditor extends ContextedComponent<{}, {}> {
|
||||
export class ObjectListEditor extends ContextedComponent<
|
||||
Record<string, unknown>,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
private tokens: EventSubscription[];
|
||||
|
||||
public componentDidMount() {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import { classNames } from "../../utils";
|
||||
|
@ -20,7 +17,7 @@ export interface PanelRadioControlProps {
|
|||
|
||||
export class PanelRadioControl extends React.Component<
|
||||
PanelRadioControlProps,
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
const mainClass = this.props.asList
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -27,11 +24,9 @@ export interface ScaleEditorProps {
|
|||
plotSegment: ObjectClass;
|
||||
}
|
||||
|
||||
export interface ScaleEditorState {}
|
||||
|
||||
export class ScaleEditor extends React.Component<
|
||||
ScaleEditorProps,
|
||||
ScaleEditorState
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public token: EventSubscription;
|
||||
|
||||
|
@ -74,9 +69,7 @@ export class ScaleEditor extends React.Component<
|
|||
}
|
||||
const currentSelection = this.props.store.currentMappingAttributeFocus;
|
||||
return (
|
||||
<ScaleEditorWrapper
|
||||
className="scale-editor-view"
|
||||
>
|
||||
<ScaleEditorWrapper className="scale-editor-view">
|
||||
<div className="attribute-editor">
|
||||
<section className="attribute-editor-element">
|
||||
<div className="header">
|
||||
|
|
|
@ -23,13 +23,6 @@ import { classNames } from "../../utils";
|
|||
import { FunctionCall, Variable } from "../../../core/expression";
|
||||
import { ColumnMetadata } from "../../../core/dataset";
|
||||
|
||||
// eslint-disable-next-line
|
||||
function getObjectIcon(classID: string) {
|
||||
return R.getSVGIcon(
|
||||
Prototypes.ObjectClasses.GetMetadata(classID).iconPath || "object"
|
||||
);
|
||||
}
|
||||
|
||||
export class ScalesPanel extends ContextedComponent<
|
||||
{
|
||||
store: AppStore;
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../../../resources";
|
||||
|
@ -18,7 +15,10 @@ export interface ButtonProps {
|
|||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export class Button extends React.Component<ButtonProps, {}> {
|
||||
export class Button extends React.Component<
|
||||
ButtonProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<button
|
||||
|
@ -76,7 +76,10 @@ export interface CheckBoxProps {
|
|||
onChange?: (newValue: boolean) => void;
|
||||
}
|
||||
|
||||
export class CheckBox extends React.Component<CheckBoxProps, {}> {
|
||||
export class CheckBox extends React.Component<
|
||||
CheckBoxProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<span
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../../../resources";
|
||||
import * as globals from "../../../../globals";
|
||||
import { SVGImageIcon } from "../../../../components";
|
||||
import { PopupView } from "../../../../controllers";
|
||||
import { classNames } from "../../../../utils";
|
||||
import {
|
||||
IComboBoxOption,
|
||||
ComboBox as FluentCombobox,
|
||||
|
@ -22,188 +14,12 @@ import {
|
|||
defultComponentsHeight,
|
||||
} from "./fluentui_customized_components";
|
||||
|
||||
export interface ComboBoxOptionProps {
|
||||
onClick: () => void;
|
||||
selected: boolean;
|
||||
}
|
||||
export interface ComboBoxProps {
|
||||
defaultValue: string;
|
||||
options?: string[];
|
||||
renderOptionItem?: (
|
||||
option: string,
|
||||
props: ComboBoxOptionProps
|
||||
) => JSX.Element;
|
||||
optionsOnly?: boolean;
|
||||
onEnter?: (value: string) => boolean;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
export interface ComboBoxState {
|
||||
value: string;
|
||||
}
|
||||
export class ComboBox extends React.Component<ComboBoxProps, ComboBoxState> {
|
||||
protected refContainer: HTMLSpanElement;
|
||||
protected refInput: HTMLInputElement;
|
||||
public state: ComboBoxState = {
|
||||
value: this.props.defaultValue,
|
||||
};
|
||||
|
||||
public componentWillReceiveProps(newProps: ComboBoxProps) {
|
||||
this.setState({
|
||||
value: newProps.defaultValue,
|
||||
});
|
||||
}
|
||||
|
||||
protected tryEmitValue(val: string) {
|
||||
if (!this.props.onEnter) {
|
||||
return;
|
||||
}
|
||||
const ok = this.props.onEnter(val);
|
||||
if (ok) {
|
||||
this.setState({
|
||||
value: val,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected reset() {
|
||||
this.setState({
|
||||
value: this.props.defaultValue,
|
||||
});
|
||||
}
|
||||
|
||||
protected handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
value: e.target.value,
|
||||
});
|
||||
};
|
||||
protected handleFocus = () => {
|
||||
this.refInput.select();
|
||||
};
|
||||
protected handleBlur = () => {
|
||||
this.tryEmitValue(this.refInput.value);
|
||||
};
|
||||
protected handleKeydown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key == "Enter") {
|
||||
this.tryEmitValue(this.refInput.value);
|
||||
} else if (e.key == "Escape") {
|
||||
this.reset();
|
||||
}
|
||||
};
|
||||
|
||||
protected renderOptions(onSelect: () => void) {
|
||||
const renderOptionItem =
|
||||
this.props.renderOptionItem ||
|
||||
((x: string, props: ComboBoxOptionProps) => (
|
||||
<span
|
||||
className={classNames("el-default-option-item", [
|
||||
"is-active",
|
||||
props.selected,
|
||||
])}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
{x}
|
||||
</span>
|
||||
));
|
||||
return (
|
||||
<span className="charticulator__widget-control-combo-box-suggestions">
|
||||
{this.props.options.map((x) => (
|
||||
<span
|
||||
className="charticulator__widget-control-combo-box-suggestions-option"
|
||||
key={x}
|
||||
>
|
||||
{renderOptionItem(x, {
|
||||
onClick: () => {
|
||||
if (onSelect) {
|
||||
onSelect();
|
||||
}
|
||||
this.tryEmitValue(x);
|
||||
},
|
||||
selected: this.state.value == x,
|
||||
})}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<span
|
||||
className="charticulator__widget-control-combo-box"
|
||||
ref={(e) => (this.refContainer = e)}
|
||||
>
|
||||
{this.props.optionsOnly ? (
|
||||
<span className="el-value">{this.state.value}</span>
|
||||
) : (
|
||||
<input
|
||||
ref={(e) => (this.refInput = e)}
|
||||
className="el-input"
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeydown}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className="el-dropdown"
|
||||
onClick={() => {
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
return (
|
||||
<PopupView
|
||||
className="charticulator__widget-control-combo-box-popup"
|
||||
context={context}
|
||||
width={this.refContainer.getBoundingClientRect().width}
|
||||
>
|
||||
{this.renderOptions(() => context.close())}
|
||||
</PopupView>
|
||||
);
|
||||
},
|
||||
{ anchor: this.refContainer }
|
||||
);
|
||||
}}
|
||||
>
|
||||
<SVGImageIcon url={R.getSVGIcon("general/dropdown")} />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ComboBoxFontFamilyProps {
|
||||
defaultValue: string;
|
||||
label?: string;
|
||||
onEnter?: (value: string) => boolean;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
export class ComboBoxFontFamily extends React.Component<
|
||||
ComboBoxFontFamilyProps,
|
||||
{}
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<ComboBox
|
||||
defaultValue={this.props.defaultValue}
|
||||
options={fontList}
|
||||
renderOptionItem={(x, props) => (
|
||||
<span
|
||||
className={classNames("el-default-option-item", [
|
||||
"is-active",
|
||||
props.selected,
|
||||
])}
|
||||
style={{ fontFamily: x }}
|
||||
{...props}
|
||||
>
|
||||
{x}
|
||||
</span>
|
||||
)}
|
||||
onEnter={this.props.onEnter}
|
||||
onCancel={this.props.onCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const FluentComboBoxFontFamily: React.FC<ComboBoxFontFamilyProps> = (
|
||||
props
|
||||
|
@ -215,9 +31,9 @@ export const FluentComboBoxFontFamily: React.FC<ComboBoxFontFamilyProps> = (
|
|||
const optionsWithCustomStyling: IComboBoxOption[] = React.useMemo<
|
||||
IComboBoxOption[]
|
||||
>(() => {
|
||||
const cuurentFontList = [...new Set([...fontList, currentValue])];
|
||||
const currentFontList = [...new Set([...fontList, currentValue])];
|
||||
|
||||
return cuurentFontList.map((fontName: string) => ({
|
||||
return currentFontList.map((fontName: string) => ({
|
||||
key: fontName,
|
||||
text: fontName,
|
||||
styles: {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../../../resources";
|
||||
|
@ -110,7 +108,9 @@ export class InputImage extends ContextedComponent<
|
|||
.then((r) => {
|
||||
this.emitOnChange(r);
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
ImageUploader.ParseFiles(e.dataTransfer.files).then((r) => {
|
||||
|
@ -193,7 +193,10 @@ export interface ImageChooserProps {
|
|||
onChoose?: (value: ImageDescription) => void;
|
||||
}
|
||||
|
||||
export class ImageChooser extends ContextedComponent<ImageChooserProps, {}> {
|
||||
export class ImageChooser extends ContextedComponent<
|
||||
ImageChooserProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="charticulator__image-chooser">
|
||||
|
@ -239,9 +242,6 @@ export class ImageUploader extends React.Component<
|
|||
public state: ImageUploaderState = { dragOver: false };
|
||||
protected refContainer: HTMLDivElement;
|
||||
|
||||
public componentDidMount() {}
|
||||
public componentWillUnmount() {}
|
||||
|
||||
public static ReadFileAsImage(
|
||||
name: string,
|
||||
file: File | Blob
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
|
@ -36,8 +35,6 @@ export const FluentInputExpression: React.FC<InputExpressionProps> = (
|
|||
props: InputExpressionProps
|
||||
) => {
|
||||
const [value, setValue] = React.useState(props.defaultValue);
|
||||
const [errorIndicator, setErrorIndicator] = React.useState(false);
|
||||
const [errorMessage, setErrorMessage] = React.useState(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.value || props.value == "") {
|
||||
|
@ -48,8 +45,6 @@ export const FluentInputExpression: React.FC<InputExpressionProps> = (
|
|||
const doEnter = React.useCallback(() => {
|
||||
if (props.allowNull && value?.trim() == "") {
|
||||
setValue("");
|
||||
setErrorIndicator(false);
|
||||
setErrorMessage(null);
|
||||
props.onEnter?.(null);
|
||||
} else {
|
||||
const result = props.validate(
|
||||
|
@ -57,22 +52,15 @@ export const FluentInputExpression: React.FC<InputExpressionProps> = (
|
|||
);
|
||||
if (result.pass) {
|
||||
setValue(result.formatted);
|
||||
setErrorIndicator(false);
|
||||
setErrorMessage(null);
|
||||
props.onEnter?.(result.formatted);
|
||||
} else {
|
||||
setErrorIndicator(true);
|
||||
setErrorMessage(result.error);
|
||||
}
|
||||
}
|
||||
}, [setValue, setErrorIndicator, setErrorMessage, props, value]);
|
||||
}, [setValue, props, value]);
|
||||
|
||||
const doCancel = React.useCallback(() => {
|
||||
setValue(props.defaultValue || "");
|
||||
setErrorIndicator(false);
|
||||
setErrorMessage(null);
|
||||
props.onCancel?.();
|
||||
}, [props, setValue, setErrorIndicator, setErrorMessage]);
|
||||
}, [props, setValue]);
|
||||
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-expression">
|
||||
|
@ -95,16 +83,14 @@ export const FluentInputExpression: React.FC<InputExpressionProps> = (
|
|||
// Check for parse errors while input
|
||||
if (props.allowNull && newValue.trim() == "") {
|
||||
setValue(newValue);
|
||||
setErrorIndicator(false);
|
||||
} else {
|
||||
const result = Expression.verifyUserExpression(
|
||||
Expression.verifyUserExpression(
|
||||
replaceTabBySymbol(replaceNewLineBySymbol(newValue)),
|
||||
{
|
||||
textExpression: props.textExpression,
|
||||
}
|
||||
);
|
||||
setValue(newValue);
|
||||
setErrorIndicator(!result.pass);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable max-lines-per-function */
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { TextField } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
|
||||
import * as React from "react";
|
||||
import * as R from "../../../../resources";
|
||||
|
@ -13,6 +11,7 @@ import { PopupView } from "../../../../controllers/popup_controller";
|
|||
import { classNames } from "../../../../utils";
|
||||
import { Button } from "./button";
|
||||
import { strings } from "../../../../../strings";
|
||||
import { noop } from "../../../../utils/noop";
|
||||
|
||||
export interface ImageDescription {
|
||||
src: string;
|
||||
|
@ -90,19 +89,21 @@ export class InputImage extends ContextedComponent<
|
|||
.split("\n")
|
||||
.map((x) => x.trim())
|
||||
.filter((x) => !x.startsWith("#"));
|
||||
ImageUploader.ParseURIs(uris)
|
||||
.then((r) => {
|
||||
this.emitOnChange(r);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
ImageUploader.ParseFiles(e.dataTransfer.files).then((r) => {
|
||||
ImageUploader.ParseURIs(uris).then((r) => {
|
||||
this.emitOnChange(r);
|
||||
});
|
||||
}
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
ImageUploader.ParseFiles(e.dataTransfer.files)
|
||||
.then((r) => {
|
||||
this.emitOnChange(r);
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log(ex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public render() {
|
||||
const isNone = this.props.value == null;
|
||||
const image = isNone ? null : this.resolveImage(this.props.value);
|
||||
|
@ -152,7 +153,10 @@ export interface ImageChooserProps {
|
|||
onChoose?: (value: ImageDescription) => void;
|
||||
}
|
||||
|
||||
export class ImageChooser extends ContextedComponent<ImageChooserProps, {}> {
|
||||
export class ImageChooser extends ContextedComponent<
|
||||
ImageChooserProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="charticulator__image-chooser">
|
||||
|
@ -204,7 +208,6 @@ export class ImageUploader extends React.Component<
|
|||
this.refInput.focus();
|
||||
}
|
||||
}
|
||||
public componentWillUnmount() {}
|
||||
|
||||
public static ReadFileAsImage(
|
||||
name: string,
|
||||
|
@ -357,7 +360,7 @@ export class ImageUploader extends React.Component<
|
|||
className="el-input"
|
||||
onPaste={this.handlePaste}
|
||||
value=""
|
||||
onChange={() => {}}
|
||||
onChange={noop}
|
||||
type="text"
|
||||
placeholder={this.props.placeholder || "Drop/Paste Image"}
|
||||
/>
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
export * from "./button";
|
||||
export * from "./combo_box";
|
||||
export * from "./image";
|
||||
export * from "./input_color";
|
||||
export * from "./input_expression";
|
||||
export * from "./input_file";
|
||||
export * from "./input_number";
|
||||
export * from "./input_text";
|
||||
export * from "./select";
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
import * as globals from "../../../../globals";
|
||||
|
||||
import {
|
||||
Color,
|
||||
colorFromHTMLColor,
|
||||
ColorGradient,
|
||||
colorToHTMLColorHEX,
|
||||
} from "../../../../../core";
|
||||
import {
|
||||
ColorPicker,
|
||||
GradientPicker,
|
||||
GradientView,
|
||||
} from "../../../../components";
|
||||
import { PopupView } from "../../../../controllers/popup_controller";
|
||||
|
||||
import { InputText } from "./input_text";
|
||||
import { AppStore } from "../../../../stores";
|
||||
import { strings } from "../../../../../strings";
|
||||
import { getAligntment } from "../../../../utils";
|
||||
|
||||
export interface InputColorProps {
|
||||
defaultValue: Color;
|
||||
allowNull?: boolean;
|
||||
onEnter: (value: Color) => boolean;
|
||||
store?: AppStore;
|
||||
}
|
||||
|
||||
export class InputColor extends React.Component<InputColorProps, {}> {
|
||||
public render() {
|
||||
let hex: string = "";
|
||||
if (this.props.defaultValue) {
|
||||
hex = colorToHTMLColorHEX(this.props.defaultValue);
|
||||
}
|
||||
let colorButton: HTMLSpanElement;
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-color">
|
||||
<span
|
||||
className="el-color-display"
|
||||
style={{ backgroundColor: hex == "" ? "transparent" : hex }}
|
||||
ref={(e) => (colorButton = e)}
|
||||
onClick={() => {
|
||||
const {
|
||||
alignX,
|
||||
}: { alignLeft: boolean; alignX: any } = getAligntment(colorButton);
|
||||
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
return (
|
||||
<PopupView context={context}>
|
||||
<ColorPicker
|
||||
store={this.props.store}
|
||||
allowNull={true}
|
||||
onPick={(color) => {
|
||||
if (color == null) {
|
||||
this.props.onEnter(null);
|
||||
context.close();
|
||||
} else {
|
||||
this.props.onEnter(color);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
);
|
||||
},
|
||||
{ anchor: colorButton, alignX }
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<InputText
|
||||
defaultValue={hex}
|
||||
placeholder={this.props.allowNull ? strings.core.none : ""}
|
||||
onEnter={(newValue) => {
|
||||
newValue = newValue.trim();
|
||||
if (newValue == "") {
|
||||
if (this.props.allowNull) {
|
||||
return this.props.onEnter(null);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const color = colorFromHTMLColor(newValue);
|
||||
if (!color) {
|
||||
return false;
|
||||
}
|
||||
return this.props.onEnter(color);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface InputColorGradientProps {
|
||||
defaultValue: ColorGradient;
|
||||
onEnter: (value: ColorGradient) => boolean;
|
||||
}
|
||||
|
||||
export class InputColorGradient extends React.Component<
|
||||
InputColorGradientProps,
|
||||
{}
|
||||
> {
|
||||
public render() {
|
||||
let colorButton: HTMLSpanElement;
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-color-gradient">
|
||||
<span
|
||||
className="el-color-gradient-display"
|
||||
ref={(e) => (colorButton = e)}
|
||||
onClick={() => {
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
return (
|
||||
<PopupView context={context}>
|
||||
<GradientPicker
|
||||
defaultValue={this.props.defaultValue}
|
||||
onPick={(gradient) => {
|
||||
this.props.onEnter(gradient);
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
);
|
||||
},
|
||||
{ anchor: colorButton }
|
||||
);
|
||||
}}
|
||||
>
|
||||
<GradientView gradient={this.props.defaultValue} />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
import { InputText } from "./input_text";
|
||||
import { applyDateFormat } from "../../../../../core";
|
||||
import { parseDate } from "../../../../../core/dataset/datetime";
|
||||
|
||||
export interface InputDateProps {
|
||||
defaultValue?: number | Date;
|
||||
placeholder?: string;
|
||||
onEnter?: (value: number) => boolean;
|
||||
showCalendar?: boolean;
|
||||
calendarRange?: [number, number];
|
||||
interval?: "second" | "minute" | "hour" | "day" | "month" | "year";
|
||||
dateDisplayFormat?: string;
|
||||
}
|
||||
|
||||
export class InputDate extends React.Component<InputDateProps, {}> {
|
||||
private textInput: InputText;
|
||||
|
||||
private formatDate(value: number | Date) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
if (typeof value === "number") {
|
||||
return applyDateFormat(
|
||||
new Date(value),
|
||||
this.props.dateDisplayFormat || "%Y-%m-%dT%H:%M:%S"
|
||||
);
|
||||
}
|
||||
if (typeof Date === "object" && value instanceof Date) {
|
||||
return applyDateFormat(
|
||||
value,
|
||||
this.props.dateDisplayFormat || "%Y-%m-%dT%H:%M:%S"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-number">
|
||||
<div className="charticulator__widget-control-input-number-input">
|
||||
{this.props.showCalendar ? (
|
||||
"datapicker" // TODO add component
|
||||
) : (
|
||||
<InputText
|
||||
ref={(e) => (this.textInput = e)}
|
||||
placeholder={this.props.placeholder}
|
||||
defaultValue={this.formatDate(this.props.defaultValue)}
|
||||
onEnter={(str) => {
|
||||
const date = parseDate(str);
|
||||
this.props.onEnter(date);
|
||||
return date != null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
import {
|
||||
Expression,
|
||||
replaceNewLineBySymbol,
|
||||
replaceSymbolByTab,
|
||||
replaceSymbolByNewLine,
|
||||
replaceTabBySymbol,
|
||||
} from "../../../../../core";
|
||||
import { classNames } from "../../../../utils";
|
||||
|
||||
export interface InputExpressionProps {
|
||||
validate?: (value: string) => Expression.VerifyUserExpressionReport;
|
||||
defaultValue?: string;
|
||||
placeholder?: string;
|
||||
onEnter?: (value: string) => boolean;
|
||||
onCancel?: () => void;
|
||||
textExpression?: boolean;
|
||||
allowNull?: boolean;
|
||||
}
|
||||
|
||||
export interface InputExpressionState {
|
||||
errorMessage?: string;
|
||||
errorIndicator: boolean;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export class InputExpression extends React.Component<
|
||||
InputExpressionProps,
|
||||
InputExpressionState
|
||||
> {
|
||||
protected refInput: HTMLInputElement;
|
||||
public state: InputExpressionState = {
|
||||
errorMessage: null,
|
||||
errorIndicator: false,
|
||||
value: this.props.defaultValue || "",
|
||||
};
|
||||
|
||||
public componentWillReceiveProps(newProps: InputExpressionProps) {
|
||||
this.setState({
|
||||
errorMessage: null,
|
||||
errorIndicator: false,
|
||||
value: newProps.defaultValue || "",
|
||||
});
|
||||
}
|
||||
|
||||
protected doEnter() {
|
||||
if (this.props.allowNull && this.refInput.value.trim() == "") {
|
||||
this.setState({
|
||||
value: "",
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
this.props.onEnter(null);
|
||||
} else {
|
||||
const result = this.props.validate(
|
||||
replaceTabBySymbol(replaceNewLineBySymbol(this.refInput.value))
|
||||
);
|
||||
if (result.pass) {
|
||||
this.setState({
|
||||
value: result.formatted,
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
this.props.onEnter(result.formatted);
|
||||
} else {
|
||||
this.setState({
|
||||
errorIndicator: true,
|
||||
errorMessage: result.error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
protected doCancel() {
|
||||
this.setState({
|
||||
value: this.props.defaultValue || "",
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
if (this.props.onCancel) {
|
||||
this.props.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-expression">
|
||||
<input
|
||||
className={classNames(
|
||||
"charticulator__widget-control-input-expression-input",
|
||||
["is-error", this.state.errorIndicator]
|
||||
)}
|
||||
type="text"
|
||||
ref={(e) => (this.refInput = e)}
|
||||
value={replaceSymbolByTab(replaceSymbolByNewLine(this.state.value))}
|
||||
placeholder={this.props.placeholder}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key == "Enter") {
|
||||
this.doEnter();
|
||||
}
|
||||
if (e.key == "Escape") {
|
||||
this.doCancel();
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
this.refInput.select();
|
||||
}}
|
||||
onBlur={() => {
|
||||
this.doEnter();
|
||||
}}
|
||||
onChange={() => {
|
||||
// Check for parse errors while input
|
||||
const newValue = this.refInput.value;
|
||||
if (this.props.allowNull && newValue.trim() == "") {
|
||||
this.setState({
|
||||
value: newValue,
|
||||
errorIndicator: false,
|
||||
});
|
||||
} else {
|
||||
const result = this.props.validate(
|
||||
replaceTabBySymbol(replaceNewLineBySymbol(this.refInput.value))
|
||||
);
|
||||
this.setState({
|
||||
value: this.refInput.value,
|
||||
errorIndicator: !result.pass,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{this.state.errorMessage != null ? (
|
||||
<span className="charticulator__widget-control-input-expression-error">
|
||||
{this.state.errorMessage}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import { Button } from "./button";
|
||||
|
||||
export interface InputFileProps {
|
||||
fileName?: string;
|
||||
accept: string[];
|
||||
outputType: "data-url" | "text" | "array-buffer";
|
||||
onOpenFile: (fileName: string, data: any) => void;
|
||||
}
|
||||
|
||||
export class InputFile extends React.Component<
|
||||
InputFileProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public inputElement: HTMLInputElement;
|
||||
|
||||
constructor(props: InputFileProps) {
|
||||
super(props);
|
||||
this.doOpenFile = this.doOpenFile.bind(this);
|
||||
}
|
||||
|
||||
private doOpenFile() {
|
||||
this.inputElement.value = null;
|
||||
this.inputElement.click();
|
||||
}
|
||||
|
||||
private onFileSelected() {
|
||||
if (this.inputElement.files.length == 1) {
|
||||
const file = this.inputElement.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this.props.onOpenFile(file.name, reader.result);
|
||||
};
|
||||
switch (this.props.outputType) {
|
||||
case "data-url":
|
||||
{
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
break;
|
||||
case "text":
|
||||
{
|
||||
reader.readAsText(file);
|
||||
}
|
||||
break;
|
||||
case "array-buffer":
|
||||
{
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-file">
|
||||
{this.props.fileName ? <span className="el-filename" /> : null}
|
||||
<Button
|
||||
icon={"general/open"}
|
||||
active={false}
|
||||
onClick={this.doOpenFile}
|
||||
/>
|
||||
<input
|
||||
style={{ display: "none" }}
|
||||
ref={(e) => (this.inputElement = e)}
|
||||
type="file"
|
||||
accept={this.props.accept.join(",")}
|
||||
onChange={this.onFileSelected}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
import {
|
||||
Expression,
|
||||
replaceNewLineBySymbol,
|
||||
replaceSymbolByTab,
|
||||
replaceSymbolByNewLine,
|
||||
replaceTabBySymbol,
|
||||
} from "../../../../../core";
|
||||
import { classNames } from "../../../../utils";
|
||||
|
||||
export interface InputFormatProps {
|
||||
validate?: (value: string) => Expression.VerifyUserExpressionReport;
|
||||
defaultValue?: string;
|
||||
placeholder?: string;
|
||||
onEnter?: (value: string) => boolean;
|
||||
onCancel?: () => void;
|
||||
textExpression?: boolean;
|
||||
allowNull?: boolean;
|
||||
}
|
||||
|
||||
export interface InputFormatState {
|
||||
errorMessage?: string;
|
||||
errorIndicator: boolean;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export class InputFormat extends React.Component<
|
||||
InputFormatProps,
|
||||
InputFormatState
|
||||
> {
|
||||
protected refInput: HTMLInputElement;
|
||||
public state: InputFormatState = {
|
||||
errorMessage: null,
|
||||
errorIndicator: false,
|
||||
value: this.props.defaultValue || "",
|
||||
};
|
||||
|
||||
public componentWillReceiveProps(newProps: InputFormatProps) {
|
||||
this.setState({
|
||||
errorMessage: null,
|
||||
errorIndicator: false,
|
||||
value: newProps.defaultValue || "",
|
||||
});
|
||||
}
|
||||
|
||||
protected doEnter() {
|
||||
if (this.props.allowNull && this.refInput.value.trim() == "") {
|
||||
this.setState({
|
||||
value: "",
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
this.props.onEnter(null);
|
||||
} else {
|
||||
const result = this.props.validate(
|
||||
replaceTabBySymbol(replaceNewLineBySymbol(this.refInput.value))
|
||||
);
|
||||
if (result.pass) {
|
||||
this.setState({
|
||||
value: result.formatted,
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
this.props.onEnter(result.formatted);
|
||||
} else {
|
||||
this.setState({
|
||||
errorIndicator: true,
|
||||
errorMessage: result.error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
protected doCancel() {
|
||||
this.setState({
|
||||
value: this.props.defaultValue || "",
|
||||
errorIndicator: false,
|
||||
errorMessage: null,
|
||||
});
|
||||
if (this.props.onCancel) {
|
||||
this.props.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-expression">
|
||||
<input
|
||||
className={classNames(
|
||||
"charticulator__widget-control-input-expression-input",
|
||||
["is-error", this.state.errorIndicator]
|
||||
)}
|
||||
type="text"
|
||||
ref={(e) => (this.refInput = e)}
|
||||
value={replaceSymbolByTab(replaceSymbolByNewLine(this.state.value))}
|
||||
placeholder={this.props.placeholder}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key == "Enter") {
|
||||
this.doEnter();
|
||||
}
|
||||
if (e.key == "Escape") {
|
||||
this.doCancel();
|
||||
}
|
||||
}}
|
||||
onFocus={() => {
|
||||
this.refInput.select();
|
||||
}}
|
||||
onBlur={() => {
|
||||
this.doEnter();
|
||||
}}
|
||||
onChange={() => {
|
||||
// Check for parse errors while input
|
||||
const newValue = this.refInput.value;
|
||||
if (this.props.allowNull && newValue.trim() == "") {
|
||||
this.setState({
|
||||
value: newValue,
|
||||
errorIndicator: false,
|
||||
});
|
||||
} else {
|
||||
const result = Expression.verifyUserExpression(
|
||||
replaceTabBySymbol(replaceNewLineBySymbol(newValue)),
|
||||
{
|
||||
textExpression: this.props.textExpression,
|
||||
}
|
||||
);
|
||||
this.setState({
|
||||
value: this.refInput.value,
|
||||
errorIndicator: !result.pass,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{this.state.errorMessage != null ? (
|
||||
<span className="charticulator__widget-control-input-expression-error">
|
||||
{this.state.errorMessage}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { ColorGradient } from "../../../../../core";
|
||||
import * as React from "react";
|
||||
import * as globals from "../../../../globals";
|
||||
import { PopupView } from "../../../../controllers";
|
||||
import { GradientPicker, GradientView } from "../../../../components";
|
||||
|
||||
export interface InputColorGradientProps {
|
||||
defaultValue: ColorGradient;
|
||||
onEnter: (value: ColorGradient) => boolean;
|
||||
}
|
||||
|
||||
export class InputColorGradient extends React.Component<
|
||||
InputColorGradientProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
let colorButton: HTMLSpanElement;
|
||||
return (
|
||||
<span className="charticulator__widget-control-input-color-gradient">
|
||||
<span
|
||||
className="el-color-gradient-display"
|
||||
ref={(e) => (colorButton = e)}
|
||||
onClick={() => {
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
return (
|
||||
<PopupView context={context}>
|
||||
<GradientPicker
|
||||
defaultValue={this.props.defaultValue}
|
||||
onPick={(gradient) => {
|
||||
this.props.onEnter(gradient);
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
);
|
||||
},
|
||||
{ anchor: colorButton }
|
||||
);
|
||||
}}
|
||||
>
|
||||
<GradientView gradient={this.props.defaultValue} />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
import { prettyNumber } from "../../../../../core";
|
||||
|
@ -28,7 +27,10 @@ export interface InputNumberProps {
|
|||
updownStyle?: "normal" | "font";
|
||||
}
|
||||
|
||||
export class InputNumber extends React.Component<InputNumberProps, {}> {
|
||||
export class InputNumber extends React.Component<
|
||||
InputNumberProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
private textInput: InputText;
|
||||
|
||||
private formatNumber(value: number) {
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
|
@ -13,7 +10,10 @@ export interface InputTextProps {
|
|||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
export class InputText extends React.Component<InputTextProps, {}> {
|
||||
export class InputText extends React.Component<
|
||||
InputTextProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public inputElement: HTMLInputElement;
|
||||
|
||||
public componentWillUpdate(newProps: InputTextProps) {
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
import { Expression, Prototypes, Specification } from "../../../../core";
|
||||
import { strings } from "../../../../strings";
|
||||
import { Actions } from "../../../actions";
|
||||
import { DataFieldSelector } from "../../dataset/data_field_selector";
|
||||
import { Button, InputExpression, Select, CheckBox } from "./controls";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
|
||||
export interface FilterEditorProps {
|
||||
manager: Prototypes.Controls.WidgetManager & CharticulatorPropertyAccessors;
|
||||
options: Prototypes.Controls.FilterEditorOptions;
|
||||
value: Specification.Types.Filter;
|
||||
}
|
||||
export interface FilterEditorState {
|
||||
type: string;
|
||||
currentValue: Specification.Types.Filter;
|
||||
}
|
||||
export class FilterEditor extends React.Component<
|
||||
FilterEditorProps,
|
||||
FilterEditorState
|
||||
> {
|
||||
public state: FilterEditorState = this.getDefaultState(this.props.value);
|
||||
public getDefaultState(value: Specification.Types.Filter): FilterEditorState {
|
||||
let filterType = "none";
|
||||
if (value) {
|
||||
if (value.expression) {
|
||||
filterType = "expression";
|
||||
}
|
||||
if (value.categories) {
|
||||
filterType = "categories";
|
||||
}
|
||||
}
|
||||
return {
|
||||
type: filterType,
|
||||
currentValue: value,
|
||||
};
|
||||
}
|
||||
public emitUpdateFilter(newValue: Specification.Types.Filter) {
|
||||
if (this.props.options.target.property) {
|
||||
this.props.manager.emitSetProperty(
|
||||
this.props.options.target.property,
|
||||
newValue
|
||||
);
|
||||
}
|
||||
if (this.props.options.target.plotSegment) {
|
||||
this.props.manager.store.dispatcher.dispatch(
|
||||
new Actions.SetPlotSegmentFilter(
|
||||
this.props.options.target.plotSegment,
|
||||
newValue
|
||||
)
|
||||
);
|
||||
}
|
||||
this.setState(this.getDefaultState(newValue));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
public render() {
|
||||
const { manager, options } = this.props;
|
||||
const value = this.state.currentValue;
|
||||
let typedControls: any[] = [];
|
||||
switch (this.state.type) {
|
||||
case "expression":
|
||||
{
|
||||
typedControls = [
|
||||
manager.row(
|
||||
"Expression",
|
||||
<InputExpression
|
||||
validate={(newValue) =>
|
||||
manager.store.verifyUserExpressionWithTable(
|
||||
newValue,
|
||||
options.table,
|
||||
{ expectedTypes: ["boolean"] }
|
||||
)
|
||||
}
|
||||
defaultValue={this.state.currentValue.expression}
|
||||
onEnter={(newValue) => {
|
||||
this.emitUpdateFilter({
|
||||
expression: newValue,
|
||||
});
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
),
|
||||
];
|
||||
}
|
||||
break;
|
||||
case "categories":
|
||||
{
|
||||
const keysSorted: string[] = [];
|
||||
if (value && value.categories) {
|
||||
for (const k in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(k)) {
|
||||
keysSorted.push(k);
|
||||
}
|
||||
}
|
||||
keysSorted.sort((a, b) => (a < b ? -1 : 1));
|
||||
}
|
||||
typedControls = [
|
||||
manager.row(
|
||||
"Column",
|
||||
<div className="charticulator__filter-editor-column-selector">
|
||||
<DataFieldSelector
|
||||
defaultValue={{
|
||||
table: options.table,
|
||||
expression: this.state.currentValue.categories.expression,
|
||||
}}
|
||||
table={options.table}
|
||||
datasetStore={this.props.manager.store}
|
||||
kinds={[Specification.DataKind.Categorical]}
|
||||
onChange={(field) => {
|
||||
// Enumerate all values of this field
|
||||
if (field.expression) {
|
||||
const parsed = Expression.parse(field.expression);
|
||||
const table = this.props.manager.store.chartManager.dataflow.getTable(
|
||||
field.table
|
||||
);
|
||||
const exprValues: { [value: string]: boolean } = {};
|
||||
for (let i = 0; i < table.rows.length; i++) {
|
||||
const rowContext = table.getRowContext(i);
|
||||
exprValues[parsed.getStringValue(rowContext)] = true;
|
||||
}
|
||||
this.emitUpdateFilter({
|
||||
categories: {
|
||||
expression: field.expression,
|
||||
values: exprValues,
|
||||
},
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
keysSorted.length > 0
|
||||
? manager.row(
|
||||
"Values",
|
||||
<div className="charticulator__filter-editor-values-selector">
|
||||
<div className="el-buttons">
|
||||
<Button
|
||||
text="Select All"
|
||||
onClick={() => {
|
||||
for (const key in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(key)) {
|
||||
value.categories.values[key] = true;
|
||||
}
|
||||
}
|
||||
this.emitUpdateFilter({
|
||||
categories: {
|
||||
expression: value.categories.expression,
|
||||
values: value.categories.values,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>{" "}
|
||||
<Button
|
||||
text="Clear"
|
||||
onClick={() => {
|
||||
for (const key in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(key)) {
|
||||
value.categories.values[key] = false;
|
||||
}
|
||||
}
|
||||
this.emitUpdateFilter({
|
||||
categories: {
|
||||
expression: value.categories.expression,
|
||||
values: value.categories.values,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="el-list">
|
||||
{keysSorted.map((key) => (
|
||||
<div key={key}>
|
||||
<CheckBox
|
||||
value={value.categories.values[key]}
|
||||
text={key}
|
||||
fillWidth={true}
|
||||
onChange={(newValue) => {
|
||||
value.categories.values[key] = newValue;
|
||||
this.emitUpdateFilter({
|
||||
categories: {
|
||||
expression: value.categories.expression,
|
||||
values: value.categories.values,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: null,
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<div className="charticulator__filter-editor">
|
||||
<div className="attribute-editor">
|
||||
<div className="header">{strings.filter.editFilter}</div>
|
||||
{manager.vertical(
|
||||
manager.row(
|
||||
strings.filter.filterType,
|
||||
<Select
|
||||
onChange={(newValue) => {
|
||||
if (this.state.type != newValue) {
|
||||
if (newValue == "none") {
|
||||
this.emitUpdateFilter(null);
|
||||
} else {
|
||||
this.setState({
|
||||
type: newValue,
|
||||
currentValue: {
|
||||
expression: "",
|
||||
categories: {
|
||||
expression: "",
|
||||
values: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
value={this.state.type}
|
||||
options={["none", "categories", "expression"]}
|
||||
labels={["None", "Categories", "Expression"]}
|
||||
showText={true}
|
||||
/>
|
||||
),
|
||||
...typedControls
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable max-lines-per-function */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
|
@ -17,7 +16,7 @@ import { ColorPicker } from "../../../components/fluentui_color_picker";
|
|||
import { ContextedComponent } from "../../../context_component";
|
||||
import { isKindAcceptable, type2DerivedColumns } from "../../dataset/common";
|
||||
import { ScaleEditor } from "../scale_editor";
|
||||
import { CharticulatorPropertyAccessors, DropZoneView } from "./manager";
|
||||
import { CharticulatorPropertyAccessors, DropZoneView } from "./types";
|
||||
import { AppStore } from "../../../stores";
|
||||
import { ScaleValueSelector } from "../scale_value_selector";
|
||||
import { FunctionCall } from "../../../../core/expression";
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-lines-per-function */
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
|
@ -13,7 +12,7 @@ import {
|
|||
FluentButton,
|
||||
} from "./controls/fluentui_customized_components";
|
||||
import { FluentUIFilterEditor } from "./fluentui_filter_editor";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
import { CharticulatorPropertyAccessors } from "./types";
|
||||
|
||||
export const FilterPanel: React.FC<{
|
||||
text: string;
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
labelRender,
|
||||
} from "./controls/fluentui_customized_components";
|
||||
import { FluentInputExpression } from "./controls/fluentui_input_expression";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
import { CharticulatorPropertyAccessors } from "./types";
|
||||
|
||||
export interface FilterEditorProps {
|
||||
manager: Prototypes.Controls.WidgetManager & CharticulatorPropertyAccessors;
|
||||
|
@ -101,8 +101,9 @@ export class FluentUIFilterEditor extends React.Component<
|
|||
const keysSorted: string[] = [];
|
||||
if (value && value.categories) {
|
||||
for (const k in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(k)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(value.categories.values, k)
|
||||
) {
|
||||
keysSorted.push(k);
|
||||
}
|
||||
}
|
||||
|
@ -152,8 +153,12 @@ export class FluentUIFilterEditor extends React.Component<
|
|||
text={strings.filter.selectAll}
|
||||
onClick={() => {
|
||||
for (const key in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(key)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
value.categories.values,
|
||||
key
|
||||
)
|
||||
) {
|
||||
value.categories.values[key] = true;
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +174,12 @@ export class FluentUIFilterEditor extends React.Component<
|
|||
text={strings.filter.clear}
|
||||
onClick={() => {
|
||||
for (const key in value.categories.values) {
|
||||
// eslint-disable-next-line
|
||||
if (value.categories.values.hasOwnProperty(key)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
value.categories.values,
|
||||
key
|
||||
)
|
||||
) {
|
||||
value.categories.values[key] = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import {
|
|||
} from "../../../utils/index";
|
||||
import { DataFieldSelectorValue } from "../../dataset/data_field_selector";
|
||||
import { ReorderListView } from "../object_list_editor";
|
||||
import { InputColorGradient, FluentComboBoxFontFamily } from "./controls";
|
||||
import { FluentComboBoxFontFamily } from "./controls";
|
||||
import { GroupByEditor } from "./groupby_editor";
|
||||
import {
|
||||
ChartTemplate,
|
||||
|
@ -69,7 +69,7 @@ import {
|
|||
TooltipHost,
|
||||
} from "@fluentui/react";
|
||||
import { FluentMappingEditor } from "./fluent_mapping_editor";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
import { CharticulatorPropertyAccessors } from "./types";
|
||||
import { FluentInputColor } from "./controls/fluentui_input_color";
|
||||
import { FluentInputExpression } from "./controls/fluentui_input_expression";
|
||||
|
||||
|
@ -116,6 +116,8 @@ import { FluentUIGradientPicker } from "../../../components/fluent_ui_gradient_p
|
|||
import { OrderMode } from "../../../../core/specification/types";
|
||||
import { CustomCollapsiblePanel } from "./controls/custom_collapsible_panel";
|
||||
import { FluentUIReorderStringsValue } from "./controls/fluentui_reorder_string_value";
|
||||
import { InputColorGradient } from "./controls/input_gradient";
|
||||
import { dropdownStyles, onRenderOption, onRenderTitle } from "./styles";
|
||||
|
||||
export type OnEditMappingHandler = (
|
||||
attribute: string,
|
||||
|
@ -513,46 +515,6 @@ export class FluentUIWidgetManager
|
|||
) {
|
||||
const theme = getTheme();
|
||||
if (options.type == "dropdown") {
|
||||
const iconStyles: CSSProperties = { marginRight: "8px" };
|
||||
|
||||
const onRenderOption = (option: IDropdownOption): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
{option.data && option.data.icon && (
|
||||
<FluentDropdown>
|
||||
<Icon
|
||||
style={iconStyles}
|
||||
iconName={option.data.icon}
|
||||
aria-hidden="true"
|
||||
title={option.data.icon}
|
||||
/>
|
||||
</FluentDropdown>
|
||||
)}
|
||||
<span>{option.text}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const onRenderTitle = (options: IDropdownOption[]): JSX.Element => {
|
||||
const option = options[0];
|
||||
|
||||
return (
|
||||
<FluentDropdownWrapper>
|
||||
{option.data && option.data.icon && (
|
||||
<FluentDropdown>
|
||||
<Icon
|
||||
style={iconStyles}
|
||||
iconName={option.data.icon}
|
||||
aria-hidden="true"
|
||||
title={option.data.icon}
|
||||
/>
|
||||
</FluentDropdown>
|
||||
)}
|
||||
<span>{option.text}</span>
|
||||
</FluentDropdownWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
key={`${this.getKeyFromProperty(property)}-${options.label}-${
|
||||
|
@ -586,16 +548,7 @@ export class FluentUIWidgetManager
|
|||
}}
|
||||
styles={{
|
||||
...defaultStyle,
|
||||
title: {
|
||||
...defultComponentsHeight,
|
||||
borderWidth: options.hideBorder ? "0px" : null,
|
||||
},
|
||||
dropdownItemsWrapper: {
|
||||
minWidth: 90,
|
||||
},
|
||||
callout: {
|
||||
marginTop: options.shiftCallout ? options.shiftCallout : null,
|
||||
},
|
||||
...dropdownStyles(options),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -1297,7 +1250,6 @@ export class FluentUIWidgetManager
|
|||
}
|
||||
|
||||
// Layout elements
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
public sectionHeader(
|
||||
title: string,
|
||||
widget?: JSX.Element,
|
||||
|
@ -1623,11 +1575,7 @@ export class FluentUIWidgetManager
|
|||
);
|
||||
}
|
||||
|
||||
public table(
|
||||
rows: JSX.Element[][],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
options: Prototypes.Controls.TableOptions
|
||||
): JSX.Element {
|
||||
public table(rows: JSX.Element[][]): JSX.Element {
|
||||
return (
|
||||
<table className="charticulator__widget-table">
|
||||
<tbody>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Prototypes, Specification } from "../../../../core";
|
|||
import { strings } from "../../../../strings";
|
||||
import { Actions } from "../../../actions";
|
||||
import { DataFieldSelector } from "../../dataset/data_field_selector";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
import { CharticulatorPropertyAccessors } from "./types";
|
||||
|
||||
export interface GroupByEditorProps {
|
||||
manager: Prototypes.Controls.WidgetManager & CharticulatorPropertyAccessors;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,696 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import {
|
||||
EventEmitter,
|
||||
EventSubscription,
|
||||
Expression,
|
||||
getById,
|
||||
Prototypes,
|
||||
Specification,
|
||||
} from "../../../../core";
|
||||
import { DragData } from "../../../actions";
|
||||
import { ColorPicker, SVGImageIcon } from "../../../components";
|
||||
import { ContextedComponent } from "../../../context_component";
|
||||
import { getAlignment, PopupAlignment, PopupView } from "../../../controllers";
|
||||
import * as globals from "../../../globals";
|
||||
import * as R from "../../../resources";
|
||||
import { isKindAcceptable } from "../../dataset/common";
|
||||
import { DataFieldSelector } from "../../dataset/data_field_selector";
|
||||
import { ScaleEditor } from "../scale_editor";
|
||||
import { Button, InputExpression } from "./controls";
|
||||
import { CharticulatorPropertyAccessors, DropZoneView } from "./manager";
|
||||
import { ValueEditor } from "./value_editor";
|
||||
import { AppStore } from "../../../stores";
|
||||
import { ScaleValueSelector } from "../scale_value_selector";
|
||||
import { FunctionCall } from "../../../../core/expression";
|
||||
import { getAligntment } from "../../../utils";
|
||||
import { MappingType } from "../../../../core/specification";
|
||||
import { ObjectClass } from "../../../../core/prototypes";
|
||||
import { strings } from "../../../../strings";
|
||||
|
||||
export interface MappingEditorProps {
|
||||
parent: Prototypes.Controls.WidgetManager & CharticulatorPropertyAccessors;
|
||||
attribute: string;
|
||||
type: Specification.AttributeType;
|
||||
options: Prototypes.Controls.MappingEditorOptions;
|
||||
store?: AppStore;
|
||||
}
|
||||
|
||||
export interface MappingEditorState {
|
||||
showNoneAsValue: boolean;
|
||||
}
|
||||
|
||||
export class MappingEditor extends React.Component<
|
||||
MappingEditorProps,
|
||||
MappingEditorState
|
||||
> {
|
||||
public mappingButton: Element;
|
||||
public noneLabel: HTMLSpanElement;
|
||||
public scaleMappingDisplay: HTMLSpanElement;
|
||||
|
||||
public updateEvents = new EventEmitter();
|
||||
|
||||
public state: MappingEditorState = {
|
||||
showNoneAsValue: false,
|
||||
};
|
||||
|
||||
private beginDataFieldSelection(anchor: Element = this.mappingButton) {
|
||||
const parent = this.props.parent;
|
||||
const attribute = this.props.attribute;
|
||||
const options = this.props.options;
|
||||
const mapping = parent.getAttributeMapping(attribute);
|
||||
|
||||
const {
|
||||
alignLeft,
|
||||
alignX,
|
||||
}: { alignLeft: boolean; alignX: PopupAlignment } = getAlignment(anchor);
|
||||
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
return (
|
||||
<PopupView context={context}>
|
||||
<DataMappAndScaleEditor
|
||||
plotSegment={parentOfType(
|
||||
(this.props.parent as any).objectClass.parent,
|
||||
"plot-segment"
|
||||
)}
|
||||
attribute={attribute}
|
||||
parent={this}
|
||||
defaultMapping={mapping}
|
||||
options={options}
|
||||
alignLeft={alignLeft}
|
||||
onClose={() => context.close()}
|
||||
/>
|
||||
</PopupView>
|
||||
);
|
||||
},
|
||||
{ anchor, alignX }
|
||||
);
|
||||
}
|
||||
|
||||
private beginDataFieldValueSelection(anchor: Element = this.mappingButton) {
|
||||
const parent = this.props.parent;
|
||||
const attribute = this.props.attribute;
|
||||
const mapping = parent.getAttributeMapping(attribute);
|
||||
|
||||
const {
|
||||
alignX,
|
||||
}: { alignLeft: boolean; alignX: PopupAlignment } = getAlignment(anchor);
|
||||
|
||||
globals.popupController.popupAt(
|
||||
(context) => {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
if (scaleMapping.scale) {
|
||||
const scaleObject = getById(
|
||||
this.props.store.chart.scales,
|
||||
scaleMapping.scale
|
||||
);
|
||||
|
||||
return (
|
||||
<PopupView context={context}>
|
||||
<ScaleValueSelector
|
||||
scale={scaleObject}
|
||||
scaleMapping={mapping as any}
|
||||
store={this.props.store}
|
||||
onSelect={(index) => {
|
||||
const paresedExpression = Expression.parse(
|
||||
scaleMapping.expression
|
||||
) as FunctionCall;
|
||||
// change the second param of get function
|
||||
(paresedExpression.args[1] as any).value = index;
|
||||
scaleMapping.expression = paresedExpression.toString();
|
||||
this.props.parent.onEditMappingHandler(
|
||||
this.props.attribute,
|
||||
scaleMapping
|
||||
);
|
||||
context.close();
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
);
|
||||
}
|
||||
},
|
||||
{ anchor, alignX }
|
||||
);
|
||||
}
|
||||
|
||||
private initiateValueEditor() {
|
||||
switch (this.props.type) {
|
||||
case "number":
|
||||
case "font-family":
|
||||
case "text":
|
||||
{
|
||||
this.setState({
|
||||
showNoneAsValue: true,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "color":
|
||||
{
|
||||
if (this.mappingButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { alignX }: { alignLeft: boolean; alignX: any } = getAligntment(
|
||||
this.mappingButton
|
||||
);
|
||||
globals.popupController.popupAt(
|
||||
(context) => (
|
||||
<PopupView context={context}>
|
||||
<ColorPicker
|
||||
store={this.props.store}
|
||||
defaultValue={null}
|
||||
allowNull={true}
|
||||
onPick={(color) => {
|
||||
if (color == null) {
|
||||
this.clearMapping();
|
||||
context.close();
|
||||
} else {
|
||||
this.setValueMapping(color);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
),
|
||||
{ anchor: this.mappingButton, alignX }
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private setValueMapping(value: Specification.AttributeValue) {
|
||||
this.props.parent.onEditMappingHandler(this.props.attribute, {
|
||||
type: MappingType.value,
|
||||
value,
|
||||
} as Specification.ValueMapping);
|
||||
}
|
||||
|
||||
public clearMapping() {
|
||||
this.props.parent.onEditMappingHandler(this.props.attribute, null);
|
||||
this.setState({
|
||||
showNoneAsValue: false,
|
||||
});
|
||||
}
|
||||
|
||||
public mapData(
|
||||
data: DragData.DataExpression,
|
||||
hints: Prototypes.DataMappingHints
|
||||
) {
|
||||
this.props.parent.onMapDataHandler(this.props.attribute, data, hints);
|
||||
}
|
||||
|
||||
public componentDidUpdate() {
|
||||
this.updateEvents.emit("update");
|
||||
}
|
||||
|
||||
public getTableOrDefault() {
|
||||
if (this.props.options.table) {
|
||||
return this.props.options.table;
|
||||
} else {
|
||||
return this.props.parent.store.getTables()[0].name;
|
||||
}
|
||||
}
|
||||
|
||||
private renderValueEditor(value: Specification.AttributeValue) {
|
||||
let placeholderText = this.props.options.defaultAuto
|
||||
? strings.core.auto
|
||||
: strings.core.none;
|
||||
if (this.props.options.defaultValue != null) {
|
||||
placeholderText = this.props.options.defaultValue.toString();
|
||||
}
|
||||
return (
|
||||
<ValueEditor
|
||||
value={value}
|
||||
type={this.props.type}
|
||||
placeholder={placeholderText}
|
||||
onClear={() => this.clearMapping()}
|
||||
onEmitValue={(value) => this.setValueMapping(value)}
|
||||
onEmitMapping={(mapping) =>
|
||||
this.props.parent.onEditMappingHandler(this.props.attribute, mapping)
|
||||
}
|
||||
onBeginDataFieldSelection={(ref) => this.beginDataFieldSelection(ref)}
|
||||
getTable={() => this.getTableOrDefault()}
|
||||
hints={this.props.options.hints}
|
||||
numberOptions={this.props.options.numberOptions}
|
||||
anchorReference={this.mappingButton}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
private renderCurrentAttributeMapping() {
|
||||
const parent = this.props.parent;
|
||||
const attribute = this.props.attribute;
|
||||
const options = this.props.options;
|
||||
const mapping = parent.getAttributeMapping(attribute);
|
||||
if (!mapping) {
|
||||
if (options.defaultValue != undefined) {
|
||||
return this.renderValueEditor(options.defaultValue);
|
||||
} else {
|
||||
let alwaysShowNoneAsValue = false;
|
||||
if (
|
||||
this.props.type == "number" ||
|
||||
this.props.type == "enum" ||
|
||||
this.props.type == "image"
|
||||
) {
|
||||
alwaysShowNoneAsValue = true;
|
||||
}
|
||||
if (this.state.showNoneAsValue || alwaysShowNoneAsValue) {
|
||||
return this.renderValueEditor(null);
|
||||
} else {
|
||||
if (options.defaultAuto) {
|
||||
return (
|
||||
<span
|
||||
className="el-clickable-label"
|
||||
ref={(e) => (this.noneLabel = e)}
|
||||
onClick={() => {
|
||||
if (!mapping || (mapping as any).valueIndex == undefined) {
|
||||
this.initiateValueEditor();
|
||||
}
|
||||
}}
|
||||
>
|
||||
(auto)
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span
|
||||
className="el-clickable-label"
|
||||
ref={(e) => (this.noneLabel = e)}
|
||||
onClick={() => {
|
||||
if (!mapping || (mapping as any).valueIndex == undefined) {
|
||||
this.initiateValueEditor();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{strings.core.none}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (mapping.type) {
|
||||
case MappingType.value: {
|
||||
const valueMapping = mapping as Specification.ValueMapping;
|
||||
return this.renderValueEditor(valueMapping.value);
|
||||
}
|
||||
case MappingType.text: {
|
||||
const textMapping = mapping as Specification.TextMapping;
|
||||
return (
|
||||
<InputExpression
|
||||
defaultValue={textMapping.textExpression}
|
||||
textExpression={true}
|
||||
validate={(value) =>
|
||||
parent.store.verifyUserExpressionWithTable(
|
||||
value,
|
||||
textMapping.table,
|
||||
{ textExpression: true, expectedTypes: ["string"] }
|
||||
)
|
||||
}
|
||||
allowNull={true}
|
||||
onEnter={(newValue) => {
|
||||
if (newValue == null || newValue.trim() == "") {
|
||||
this.clearMapping();
|
||||
} else {
|
||||
if (
|
||||
Expression.parseTextExpression(newValue).isTrivialString()
|
||||
) {
|
||||
this.props.parent.onEditMappingHandler(
|
||||
this.props.attribute,
|
||||
{
|
||||
type: MappingType.value,
|
||||
value: newValue,
|
||||
} as Specification.ValueMapping
|
||||
);
|
||||
} else {
|
||||
this.props.parent.onEditMappingHandler(
|
||||
this.props.attribute,
|
||||
{
|
||||
type: MappingType.text,
|
||||
table: textMapping.table,
|
||||
textExpression: newValue,
|
||||
} as Specification.TextMapping
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case MappingType.scale: {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
if (scaleMapping.scale) {
|
||||
let scaleIcon = <span>f</span>;
|
||||
if (this.props.type == "color") {
|
||||
scaleIcon = <SVGImageIcon url={R.getSVGIcon("scale/color")} />;
|
||||
}
|
||||
return (
|
||||
<span
|
||||
className="el-mapping-scale"
|
||||
ref={(e) => (this.scaleMappingDisplay = e)}
|
||||
onClick={() => {
|
||||
if (!scaleMapping || scaleMapping.valueIndex == undefined) {
|
||||
const {
|
||||
alignLeft,
|
||||
alignX,
|
||||
}: {
|
||||
alignLeft: boolean;
|
||||
alignX: PopupAlignment;
|
||||
} = getAlignment(this.scaleMappingDisplay);
|
||||
|
||||
globals.popupController.popupAt(
|
||||
(context) => (
|
||||
<PopupView context={context}>
|
||||
<DataMappAndScaleEditor
|
||||
plotSegment={parentOfType(
|
||||
(this.props.parent as any).objectClass.parent,
|
||||
"plot-segment"
|
||||
)}
|
||||
attribute={this.props.attribute}
|
||||
parent={this}
|
||||
defaultMapping={mapping}
|
||||
options={options}
|
||||
alignLeft={alignLeft}
|
||||
onClose={() => context.close()}
|
||||
/>
|
||||
</PopupView>
|
||||
),
|
||||
{ anchor: this.scaleMappingDisplay, alignX }
|
||||
);
|
||||
} else {
|
||||
this.beginDataFieldValueSelection();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span className="el-mapping-scale-scale is-left">
|
||||
{scaleIcon}
|
||||
</span>
|
||||
<svg width={6} height={20}>
|
||||
<path d="M3.2514,10A17.37314,17.37314,0,0,1,6,0H0V20H6A17.37342,17.37342,0,0,1,3.2514,10Z" />
|
||||
</svg>
|
||||
<span className="el-mapping-scale-column">
|
||||
{scaleMapping.expression}
|
||||
</span>
|
||||
<svg width={6} height={20}>
|
||||
<path d="M2.7486,10A17.37314,17.37314,0,0,0,0,0H6V20H0A17.37342,17.37342,0,0,0,2.7486,10Z" />
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span className="el-mapping-scale">
|
||||
<span className="el-mapping-scale-scale is-left">=</span>
|
||||
<svg width={6} height={20}>
|
||||
<path d="M3.2514,10A17.37314,17.37314,0,0,1,6,0H0V20H6A17.37342,17.37342,0,0,1,3.2514,10Z" />
|
||||
</svg>
|
||||
<span className="el-mapping-scale-column">
|
||||
{scaleMapping.expression}
|
||||
</span>
|
||||
<svg width={6} height={20}>
|
||||
<path d="M2.7486,10A17.37314,17.37314,0,0,0,0,0H6V20H0A17.37342,17.37342,0,0,0,2.7486,10Z" />
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return <span>(...)</span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
public render() {
|
||||
const parent = this.props.parent;
|
||||
const attribute = this.props.attribute;
|
||||
const options = this.props.options;
|
||||
const currentMapping = parent.getAttributeMapping(attribute);
|
||||
// If there is a mapping, also not having default or using auto
|
||||
let shouldShowEraser =
|
||||
currentMapping != null &&
|
||||
(currentMapping.type != MappingType.value ||
|
||||
!options.defaultValue ||
|
||||
options.defaultAuto);
|
||||
shouldShowEraser = shouldShowEraser || this.state.showNoneAsValue;
|
||||
const shouldShowBindData = parent.onMapDataHandler != null;
|
||||
const isDataMapping =
|
||||
currentMapping != null && currentMapping.type == MappingType.scale;
|
||||
shouldShowEraser = isDataMapping;
|
||||
const valueIndex = currentMapping && (currentMapping as any).valueIndex;
|
||||
|
||||
if (this.props.options.openMapping) {
|
||||
setTimeout(() => {
|
||||
if (valueIndex == undefined) {
|
||||
this.beginDataFieldSelection();
|
||||
} else {
|
||||
this.beginDataFieldValueSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={(e) => (this.mappingButton = ReactDOM.findDOMNode(e) as Element)}
|
||||
className="charticulator__widget-control-mapping-editor"
|
||||
>
|
||||
<DropZoneView
|
||||
filter={(data) => {
|
||||
if (!shouldShowBindData) {
|
||||
return false;
|
||||
}
|
||||
if (data instanceof DragData.DataExpression) {
|
||||
return isKindAcceptable(data.metadata.kind, options.acceptKinds);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
onDrop={(data: DragData.DataExpression, point, modifiers) => {
|
||||
if (!options.hints) {
|
||||
options.hints = {};
|
||||
}
|
||||
options.hints.newScale = modifiers.shiftKey;
|
||||
options.hints.scaleID = data.scaleID;
|
||||
|
||||
const parsedExpression = Expression.parse(
|
||||
data.expression
|
||||
) as FunctionCall;
|
||||
|
||||
if (data.allowSelectValue && parsedExpression.name !== "get") {
|
||||
data.expression = `get(${data.expression}, 0)`;
|
||||
}
|
||||
// because original mapping allowed it
|
||||
if (parsedExpression.name === "get") {
|
||||
data.allowSelectValue = true;
|
||||
}
|
||||
this.mapData(data, {
|
||||
...options.hints,
|
||||
allowSelectValue: data.allowSelectValue,
|
||||
});
|
||||
}}
|
||||
className="charticulator__widget-control-mapping-editor"
|
||||
>
|
||||
{parent.horizontal(
|
||||
[1, 0],
|
||||
this.renderCurrentAttributeMapping(),
|
||||
<span>
|
||||
{shouldShowEraser ? (
|
||||
<Button
|
||||
icon="general/eraser"
|
||||
active={false}
|
||||
title="Remove"
|
||||
onClick={() => {
|
||||
if (parent.getAttributeMapping(attribute)) {
|
||||
this.clearMapping();
|
||||
}
|
||||
this.setState({
|
||||
showNoneAsValue: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{valueIndex == undefined && shouldShowBindData ? (
|
||||
<Button
|
||||
icon={"general/bind-data"}
|
||||
title="Bind data"
|
||||
// ref={(e) =>
|
||||
// (this.mappingButton = ReactDOM.findDOMNode(e) as Element)
|
||||
// }
|
||||
onClick={() => {
|
||||
this.beginDataFieldSelection();
|
||||
}}
|
||||
active={isDataMapping}
|
||||
/>
|
||||
) : null}
|
||||
{valueIndex != undefined ? (
|
||||
<Button
|
||||
icon={"general/bind-data"}
|
||||
title="Bind data value"
|
||||
// ref={(e) =>
|
||||
// (this.mappingButton = ReactDOM.findDOMNode(e) as Element)
|
||||
// }
|
||||
onClick={() => {
|
||||
this.beginDataFieldValueSelection();
|
||||
}}
|
||||
active={isDataMapping}
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
)}
|
||||
</DropZoneView>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataMappAndScaleEditorProps {
|
||||
plotSegment: ObjectClass;
|
||||
attribute: string;
|
||||
defaultMapping: Specification.Mapping;
|
||||
options: Prototypes.Controls.MappingEditorOptions;
|
||||
parent: MappingEditor;
|
||||
onClose: () => void;
|
||||
alignLeft?: boolean;
|
||||
}
|
||||
export interface DataMappAndScaleEditorState {
|
||||
currentMapping: Specification.Mapping;
|
||||
}
|
||||
|
||||
export class DataMappAndScaleEditor extends ContextedComponent<
|
||||
DataMappAndScaleEditorProps,
|
||||
DataMappAndScaleEditorState
|
||||
> {
|
||||
public state = {
|
||||
currentMapping: this.props.defaultMapping,
|
||||
};
|
||||
|
||||
private tokens: EventSubscription[];
|
||||
|
||||
public componentDidMount() {
|
||||
this.tokens = [
|
||||
this.props.parent.updateEvents.addListener("update", () => {
|
||||
this.setState({
|
||||
currentMapping: this.props.parent.props.parent.getAttributeMapping(
|
||||
this.props.attribute
|
||||
),
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
for (const t of this.tokens) {
|
||||
t.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public renderScaleEditor() {
|
||||
const mapping = this.state.currentMapping;
|
||||
if (mapping && mapping.type == MappingType.scale) {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
if (scaleMapping.scale) {
|
||||
const scaleObject = getById(
|
||||
this.store.chart.scales,
|
||||
scaleMapping.scale
|
||||
);
|
||||
if (scaleObject) {
|
||||
return (
|
||||
<ScaleEditor
|
||||
plotSegment={this.props.plotSegment}
|
||||
scale={scaleObject}
|
||||
scaleMapping={scaleMapping}
|
||||
store={this.store}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public renderDataPicker() {
|
||||
const options = this.props.options;
|
||||
let currentExpression: string = null;
|
||||
const mapping = this.state.currentMapping;
|
||||
|
||||
if (mapping != null && mapping.type == MappingType.scale) {
|
||||
currentExpression = (mapping as Specification.ScaleMapping).expression;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DataFieldSelector
|
||||
table={mapping ? (mapping as any).table : options.table}
|
||||
datasetStore={this.store}
|
||||
kinds={options.acceptKinds}
|
||||
useAggregation={true}
|
||||
defaultValue={
|
||||
currentExpression
|
||||
? { table: options.table, expression: currentExpression }
|
||||
: null
|
||||
}
|
||||
nullDescription={strings.core.none}
|
||||
nullNotHighlightable={true}
|
||||
onChange={(value) => {
|
||||
if (value != null) {
|
||||
this.props.parent.mapData(
|
||||
new DragData.DataExpression(
|
||||
this.store.getTable(value.table),
|
||||
value.expression,
|
||||
value.type,
|
||||
value.metadata,
|
||||
value.rawExpression
|
||||
),
|
||||
options.hints
|
||||
);
|
||||
} else {
|
||||
this.props.parent.clearMapping();
|
||||
this.props.onClose();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const scaleElement = this.renderScaleEditor();
|
||||
if (scaleElement) {
|
||||
return (
|
||||
<div className="charticulator__data-mapping-and-scale-editor">
|
||||
<div
|
||||
className={
|
||||
this.props.alignLeft ? "el-scale-editor-left" : "el-scale-editor"
|
||||
}
|
||||
>
|
||||
{scaleElement}
|
||||
</div>
|
||||
<div className="el-data-picker">{this.renderDataPicker()}</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="charticulator__data-mapping-and-scale-editor">
|
||||
<div className="el-data-picker">{this.renderDataPicker()}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parentOfType(p: ObjectClass, typeSought: string) {
|
||||
while (p) {
|
||||
if (Prototypes.isType(p.object.classID, typeSought)) {
|
||||
return p;
|
||||
}
|
||||
p = p.parent;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import { Prototypes, Specification } from "../../../../core";
|
||||
import { CharticulatorPropertyAccessors } from "./manager";
|
||||
import { CharticulatorPropertyAccessors } from "./types";
|
||||
|
||||
interface EventListener {
|
||||
update(
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { IDropdownOption, IDropdownStyles } from "@fluentui/react";
|
||||
import {
|
||||
defultComponentsHeight,
|
||||
FluentDropdown,
|
||||
FluentDropdownWrapper,
|
||||
} from "./controls/fluentui_customized_components";
|
||||
import { Prototypes } from "../../../../core";
|
||||
import { Icon } from "@fluentui/react/lib/Icon";
|
||||
import * as React from "react";
|
||||
import { CSSProperties } from "react";
|
||||
|
||||
export const dropdownStyles = (
|
||||
options: Prototypes.Controls.InputSelectOptions
|
||||
): Partial<IDropdownStyles> => {
|
||||
return {
|
||||
title: {
|
||||
...defultComponentsHeight,
|
||||
borderWidth: options.hideBorder ? "0px" : null,
|
||||
},
|
||||
dropdownItemsWrapper: {
|
||||
minWidth: 90,
|
||||
},
|
||||
callout: {
|
||||
marginTop: options.shiftCallout ? options.shiftCallout : null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const iconStyles: CSSProperties = { marginRight: "8px" };
|
||||
|
||||
export const onRenderOption = (option: IDropdownOption): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
{option.data && option.data.icon && (
|
||||
<FluentDropdown>
|
||||
<Icon
|
||||
style={iconStyles}
|
||||
iconName={option.data.icon}
|
||||
aria-hidden="true"
|
||||
title={option.data.icon}
|
||||
/>
|
||||
</FluentDropdown>
|
||||
)}
|
||||
<span>{option.text}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const onRenderTitle = (options: IDropdownOption[]): JSX.Element => {
|
||||
const option = options[0];
|
||||
|
||||
return (
|
||||
<FluentDropdownWrapper>
|
||||
{option.data && option.data.icon && (
|
||||
<FluentDropdown>
|
||||
<Icon
|
||||
style={iconStyles}
|
||||
iconName={option.data.icon}
|
||||
aria-hidden="true"
|
||||
title={option.data.icon}
|
||||
/>
|
||||
</FluentDropdown>
|
||||
)}
|
||||
<span>{option.text}</span>
|
||||
</FluentDropdownWrapper>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,238 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import * as globals from "../../../globals";
|
||||
|
||||
import {
|
||||
EventSubscription,
|
||||
Point,
|
||||
Prototypes,
|
||||
Specification,
|
||||
} from "../../../../core";
|
||||
import { DragData } from "../../../actions";
|
||||
import { ButtonRaised } from "../../../components";
|
||||
import {
|
||||
DragContext,
|
||||
DragModifiers,
|
||||
Droppable,
|
||||
} from "../../../controllers/drag_controller";
|
||||
|
||||
import { AppStore } from "../../../stores";
|
||||
import { classNames } from "../../../utils/index";
|
||||
import { ReorderListView } from "../object_list_editor";
|
||||
import { Button } from "./controls";
|
||||
import { getSortFunctionByData } from "../../../../container";
|
||||
|
||||
export type OnEditMappingHandler = (
|
||||
attribute: string,
|
||||
mapping: Specification.Mapping
|
||||
) => void;
|
||||
export type OnMapDataHandler = (
|
||||
attribute: string,
|
||||
data: DragData.DataExpression,
|
||||
hints: Prototypes.DataMappingHints
|
||||
) => void;
|
||||
export type OnSetPropertyHandler = (
|
||||
property: string,
|
||||
field: string,
|
||||
value: Specification.AttributeValue
|
||||
) => void;
|
||||
|
||||
export interface CharticulatorPropertyAccessors {
|
||||
emitSetProperty?: (
|
||||
property: Prototypes.Controls.Property,
|
||||
value: Specification.AttributeValue
|
||||
) => void;
|
||||
store: AppStore;
|
||||
|
||||
getAttributeMapping?: (attribute: string) => Specification.Mapping;
|
||||
onEditMappingHandler?: OnEditMappingHandler;
|
||||
onMapDataHandler?: OnMapDataHandler;
|
||||
}
|
||||
|
||||
export interface DropZoneViewProps {
|
||||
/** Determine whether the data is acceptable */
|
||||
filter: (x: any) => boolean;
|
||||
/** The user dropped the thing */
|
||||
onDrop: (data: any, point: Point, modifiers: DragModifiers) => void;
|
||||
/** className of the root div element */
|
||||
className: string;
|
||||
onClick?: () => void;
|
||||
/** Display this instead when dragging (normally we show what's in this view) */
|
||||
draggingHint?: () => JSX.Element;
|
||||
}
|
||||
|
||||
export interface DropZoneViewState {
|
||||
isInSession: boolean;
|
||||
isDraggingOver: boolean;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export class DropZoneView
|
||||
extends React.Component<DropZoneViewProps, DropZoneViewState>
|
||||
implements Droppable {
|
||||
public dropContainer: HTMLDivElement;
|
||||
public tokens: EventSubscription[];
|
||||
|
||||
constructor(props: DropZoneViewProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isInSession: false,
|
||||
isDraggingOver: false,
|
||||
data: null,
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
globals.dragController.registerDroppable(this, this.dropContainer);
|
||||
this.tokens = [
|
||||
globals.dragController.addListener("sessionstart", () => {
|
||||
const session = globals.dragController.getSession();
|
||||
if (this.props.filter(session.data)) {
|
||||
this.setState({
|
||||
isInSession: true,
|
||||
});
|
||||
}
|
||||
}),
|
||||
globals.dragController.addListener("sessionend", () => {
|
||||
this.setState({
|
||||
isInSession: false,
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
globals.dragController.unregisterDroppable(this);
|
||||
this.tokens.forEach((x) => x.remove());
|
||||
}
|
||||
|
||||
public onDragEnter(ctx: DragContext) {
|
||||
const data = ctx.data;
|
||||
const judge = this.props.filter(data);
|
||||
if (judge) {
|
||||
this.setState({
|
||||
isDraggingOver: true,
|
||||
data,
|
||||
});
|
||||
ctx.onLeave(() => {
|
||||
this.setState({
|
||||
isDraggingOver: false,
|
||||
data: null,
|
||||
});
|
||||
});
|
||||
ctx.onDrop((point: Point, modifiers: DragModifiers) => {
|
||||
this.props.onDrop(data, point, modifiers);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
this.props.className,
|
||||
["is-in-session", this.state.isInSession],
|
||||
["is-dragging-over", this.state.isDraggingOver]
|
||||
)}
|
||||
onClick={this.props.onClick}
|
||||
ref={(e) => (this.dropContainer = e)}
|
||||
>
|
||||
{this.props.draggingHint == null
|
||||
? this.props.children
|
||||
: this.state.isInSession
|
||||
? this.props.draggingHint()
|
||||
: this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReorderStringsValue extends React.Component<
|
||||
{
|
||||
items: string[];
|
||||
onConfirm: (items: string[], customOrder: boolean) => void;
|
||||
allowReset?: boolean;
|
||||
onReset?: () => string[];
|
||||
},
|
||||
{
|
||||
items: string[];
|
||||
customOrder: boolean;
|
||||
}
|
||||
> {
|
||||
public state: { items: string[]; customOrder: boolean } = {
|
||||
items: this.props.items.slice(),
|
||||
customOrder: false,
|
||||
};
|
||||
|
||||
public render() {
|
||||
const items = this.state.items.slice();
|
||||
return (
|
||||
<div className="charticulator__widget-popup-reorder-widget">
|
||||
<div className="el-row el-list-view">
|
||||
<ReorderListView
|
||||
enabled={true}
|
||||
onReorder={(a, b) => {
|
||||
ReorderListView.ReorderArray(items, a, b);
|
||||
this.setState({ items, customOrder: true });
|
||||
}}
|
||||
>
|
||||
{items.map((x) => (
|
||||
<div key={x} className="el-item">
|
||||
{x}
|
||||
</div>
|
||||
))}
|
||||
</ReorderListView>
|
||||
</div>
|
||||
<div className="el-row">
|
||||
<Button
|
||||
icon={"Sort"}
|
||||
text="Reverse"
|
||||
onClick={() => {
|
||||
this.setState({ items: this.state.items.reverse() });
|
||||
}}
|
||||
/>{" "}
|
||||
<Button
|
||||
icon={"general/sort"}
|
||||
text="Sort"
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
items: this.state.items.sort(
|
||||
getSortFunctionByData(this.state.items)
|
||||
),
|
||||
customOrder: false,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{this.props.allowReset && (
|
||||
<>
|
||||
{" "}
|
||||
<Button
|
||||
icon={"general/clear"}
|
||||
text="Reset"
|
||||
onClick={() => {
|
||||
if (this.props.onReset) {
|
||||
const items = this.props.onReset();
|
||||
this.setState({ items });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="el-row">
|
||||
<ButtonRaised
|
||||
text="OK"
|
||||
onClick={() => {
|
||||
this.props.onConfirm(this.state.items, this.state.customOrder);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { strings } from "../../../../strings";
|
||||
import {
|
||||
Color,
|
||||
colorFromHTMLColor,
|
||||
colorToHTMLColorHEX,
|
||||
Expression,
|
||||
Specification,
|
||||
} from "../../../../core";
|
||||
import { DataMappingHints } from "../../../../core/prototypes";
|
||||
import { InputNumberOptions } from "../../../../core/prototypes/controls";
|
||||
import { MappingType } from "../../../../core/specification";
|
||||
import { ColorPicker } from "../../../components";
|
||||
import { ContextedComponent } from "../../../context_component";
|
||||
import { PopupView } from "../../../controllers";
|
||||
import * as globals from "../../../globals";
|
||||
import {
|
||||
Button,
|
||||
ComboBox,
|
||||
ComboBoxFontFamily,
|
||||
InputExpression,
|
||||
InputImage,
|
||||
InputNumber,
|
||||
InputText,
|
||||
} from "./controls";
|
||||
import { getAligntment } from "../../../utils";
|
||||
|
||||
export interface ValueEditorProps {
|
||||
value: Specification.AttributeValue;
|
||||
type: Specification.AttributeType;
|
||||
|
||||
/** When value is null, show defaultValue in editor */
|
||||
defaultValue?: Specification.AttributeValue;
|
||||
/** When value is null, show placeholder text */
|
||||
placeholder?: string;
|
||||
|
||||
onEmitValue?: (value: Specification.AttributeValue) => void;
|
||||
onClear?: () => void;
|
||||
|
||||
/** In some cases the value editor can emit data mapping */
|
||||
onEmitMapping?: (mapping: Specification.Mapping) => void;
|
||||
onBeginDataFieldSelection?: (anchor: Element) => void;
|
||||
/** The table to use for data mapping */
|
||||
getTable?: () => string;
|
||||
|
||||
hints?: DataMappingHints;
|
||||
numberOptions?: InputNumberOptions;
|
||||
anchorReference?: Element;
|
||||
}
|
||||
|
||||
export class ValueEditor extends ContextedComponent<ValueEditorProps, {}> {
|
||||
public emitClearValue() {
|
||||
this.props.onClear();
|
||||
}
|
||||
|
||||
public emitSetValue(value: Specification.AttributeValue) {
|
||||
this.props.onEmitValue(value);
|
||||
}
|
||||
|
||||
public emitMapping(mapping: Specification.Mapping) {
|
||||
this.props.onEmitMapping(mapping);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
public render() {
|
||||
const value = this.props.value;
|
||||
|
||||
let placeholderText = this.props.placeholder || strings.core.none;
|
||||
if (this.props.defaultValue != null) {
|
||||
placeholderText = this.props.defaultValue.toString();
|
||||
}
|
||||
|
||||
switch (this.props.type) {
|
||||
case Specification.AttributeType.Number: {
|
||||
const number = value as number;
|
||||
let numberOptions = this.props.numberOptions;
|
||||
if (!numberOptions) {
|
||||
numberOptions = {};
|
||||
}
|
||||
return (
|
||||
<InputNumber
|
||||
defaultValue={number}
|
||||
placeholder={placeholderText}
|
||||
{...numberOptions}
|
||||
onEnter={(newValue) => {
|
||||
if (newValue == null) {
|
||||
this.emitClearValue();
|
||||
return true;
|
||||
}
|
||||
if (newValue == newValue) {
|
||||
this.emitSetValue(newValue);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Specification.AttributeType.Color: {
|
||||
const color = value as Color;
|
||||
const hex = colorToHTMLColorHEX(color);
|
||||
let colorItem: Element;
|
||||
return (
|
||||
<span className="el-color-value">
|
||||
<span
|
||||
className="el-color-item"
|
||||
ref={(e) => (colorItem = e)}
|
||||
style={{ backgroundColor: hex }}
|
||||
onClick={() => {
|
||||
const {
|
||||
alignX,
|
||||
}: { alignLeft: boolean; alignX: any } = getAligntment(
|
||||
colorItem
|
||||
);
|
||||
globals.popupController.popupAt(
|
||||
(context) => (
|
||||
<PopupView context={context}>
|
||||
<ColorPicker
|
||||
store={this.store}
|
||||
defaultValue={color}
|
||||
allowNull={true}
|
||||
onPick={(color) => {
|
||||
if (color == null) {
|
||||
this.emitClearValue();
|
||||
context.close();
|
||||
} else {
|
||||
this.emitSetValue(color);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PopupView>
|
||||
),
|
||||
{ anchor: colorItem, alignX: alignX }
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<InputText
|
||||
defaultValue={hex}
|
||||
onEnter={(newValue) => {
|
||||
newValue = newValue.trim();
|
||||
if (newValue == "") {
|
||||
this.emitClearValue();
|
||||
} else {
|
||||
const newColor = colorFromHTMLColor(newValue);
|
||||
if (newColor) {
|
||||
this.emitSetValue(newColor);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
case Specification.AttributeType.FontFamily:
|
||||
return (
|
||||
<ComboBoxFontFamily
|
||||
defaultValue={value as string}
|
||||
onEnter={(value) => {
|
||||
this.emitSetValue(value);
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case Specification.AttributeType.Text: {
|
||||
const str = value as string;
|
||||
if (this.props.onEmitMapping) {
|
||||
return (
|
||||
<InputExpression
|
||||
textExpression={true}
|
||||
validate={(value) =>
|
||||
this.context.store.verifyUserExpressionWithTable(
|
||||
value,
|
||||
this.props.getTable(),
|
||||
{ textExpression: true, expectedTypes: ["string"] }
|
||||
)
|
||||
}
|
||||
defaultValue={new Expression.TextExpression([
|
||||
{ string: str },
|
||||
]).toString()}
|
||||
placeholder={placeholderText}
|
||||
allowNull={true}
|
||||
onEnter={(newValue) => {
|
||||
if (newValue == null || newValue.trim() == "") {
|
||||
this.emitClearValue();
|
||||
} else {
|
||||
if (
|
||||
Expression.parseTextExpression(newValue).isTrivialString()
|
||||
) {
|
||||
this.emitMapping({
|
||||
type: MappingType.value,
|
||||
value: newValue,
|
||||
} as Specification.ValueMapping);
|
||||
} else {
|
||||
this.emitMapping({
|
||||
type: MappingType.text,
|
||||
table: this.props.getTable(),
|
||||
textExpression: newValue,
|
||||
} as Specification.TextMapping);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<InputText
|
||||
defaultValue={str}
|
||||
placeholder={placeholderText}
|
||||
onEnter={(newValue) => {
|
||||
if (newValue == null) {
|
||||
this.emitClearValue();
|
||||
} else {
|
||||
this.emitSetValue(newValue);
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
case Specification.AttributeType.Enum: {
|
||||
const str = value as string;
|
||||
const strings = this.props.hints.rangeEnum;
|
||||
return (
|
||||
<ComboBox
|
||||
defaultValue={str}
|
||||
onEnter={(newValue) => {
|
||||
if (newValue == null) {
|
||||
this.emitClearValue();
|
||||
} else {
|
||||
this.emitSetValue(newValue);
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
options={strings || []}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Specification.AttributeType.Boolean: {
|
||||
const boolean = value as boolean;
|
||||
let ref: Element;
|
||||
if (this.props.onEmitMapping) {
|
||||
return (
|
||||
<Button
|
||||
active={false}
|
||||
text="Conditioned by..."
|
||||
ref={(e) => (ref = ReactDOM.findDOMNode(e) as Element)}
|
||||
onClick={() => {
|
||||
this.props.onBeginDataFieldSelection(
|
||||
this.props.anchorReference || ref
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Button
|
||||
active={false}
|
||||
icon={boolean ? "checkbox/checked" : "checkbox/empty"}
|
||||
ref={(e) => (ref = ReactDOM.findDOMNode(e) as Element)}
|
||||
onClick={() => {
|
||||
this.emitSetValue(!boolean);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
case Specification.AttributeType.Image: {
|
||||
const str = value as Specification.Types.Image;
|
||||
return (
|
||||
<InputImage
|
||||
value={str}
|
||||
onChange={(newValue) => {
|
||||
if (newValue == null) {
|
||||
this.emitClearValue();
|
||||
} else {
|
||||
this.emitSetValue(newValue as Specification.Types.Image);
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <span>(not implemented)</span>;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable @typescript-eslint/ban-types */
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
@ -630,7 +629,10 @@ export interface ObjectButtonProps {
|
|||
compact?: boolean;
|
||||
}
|
||||
|
||||
export class ObjectButton extends ContextedComponent<ObjectButtonProps, {}> {
|
||||
export class ObjectButton extends ContextedComponent<
|
||||
ObjectButtonProps,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public token: EventSubscription;
|
||||
|
||||
public getIsActive() {
|
||||
|
@ -838,7 +840,7 @@ export class ScaffoldButton extends ContextedComponent<
|
|||
type: string;
|
||||
icon: string;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
@ -861,7 +863,7 @@ export class LinkButton extends ContextedComponent<
|
|||
{
|
||||
label: boolean;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public container: HTMLSpanElement;
|
||||
|
||||
|
@ -889,7 +891,10 @@ export class LinkButton extends ContextedComponent<
|
|||
}
|
||||
}
|
||||
|
||||
export class LegendButton extends ContextedComponent<{}, {}> {
|
||||
export class LegendButton extends ContextedComponent<
|
||||
Record<string, unknown>,
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public container: HTMLSpanElement;
|
||||
|
||||
public render() {
|
||||
|
@ -921,7 +926,7 @@ export class CheckboxButton extends React.Component<
|
|||
text?: string;
|
||||
onChange?: (v: boolean) => void;
|
||||
},
|
||||
{}
|
||||
Record<string, unknown>
|
||||
> {
|
||||
public render() {
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
/* eslint-disable no-prototype-builtins */
|
||||
|
||||
import {
|
||||
Dataset,
|
||||
|
@ -72,7 +71,9 @@ export class ChartTemplate {
|
|||
|
||||
/** Assign an expression to a data mapping slot */
|
||||
public assignColumn(tableName: string, columnName: string, column: string) {
|
||||
if (!this.columnAssignment.hasOwnProperty(tableName)) {
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(this.columnAssignment, tableName)
|
||||
) {
|
||||
this.columnAssignment[tableName] = {};
|
||||
}
|
||||
this.columnAssignment[tableName][columnName] = column;
|
||||
|
@ -321,8 +322,7 @@ export class ChartTemplate {
|
|||
|
||||
// Replace data-mapping expressions with assigned columns
|
||||
const mappings = item.object.mappings;
|
||||
// eslint-disable-next-line
|
||||
for (const [attr, mapping] of forEachMapping(mappings)) {
|
||||
for (const [, mapping] of forEachMapping(mappings)) {
|
||||
if (mapping.type == MappingType.scale) {
|
||||
const scaleMapping = mapping as Specification.ScaleMapping;
|
||||
scaleMapping.expression = this.transformExpression(
|
||||
|
|
|
@ -163,15 +163,13 @@ export class ChartContainerComponent extends React.Component<
|
|||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
protected handleGlyphMouseEnter: GlyphEventHandler = (data, modifiers) => {
|
||||
protected handleGlyphMouseEnter: GlyphEventHandler = (data) => {
|
||||
if (this.props.onMouseEnterGlyph) {
|
||||
this.props.onMouseEnterGlyph(data);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
protected handleGlyphMouseLeave: GlyphEventHandler = (data, modifiers) => {
|
||||
protected handleGlyphMouseLeave: GlyphEventHandler = (data) => {
|
||||
if (this.props.onMouseLeaveGlyph) {
|
||||
this.props.onMouseLeaveGlyph(data);
|
||||
}
|
||||
|
|
|
@ -406,8 +406,7 @@ export function getDefaultColorGeneratorResetFunction() {
|
|||
return defaultColorGeneratorResetFunction;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export function getDefaultColorPaletteByValue(value: string, count: number) {
|
||||
export function getDefaultColorPaletteByValue(value: string) {
|
||||
return defaultColorGeneratorFunction?.(value);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,7 @@ export function deepClone<T>(obj: T): T {
|
|||
export function shallowClone<T>(obj: T): T {
|
||||
const r = <T>{};
|
||||
for (const key in obj) {
|
||||
// eslint-disable-next-line
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
r[key] = obj[key];
|
||||
}
|
||||
}
|
||||
|
@ -213,10 +212,8 @@ export function fillDefaults<T extends Record<string, unknown>>(
|
|||
obj = <T>{};
|
||||
}
|
||||
for (const key in defaults) {
|
||||
// eslint-disable-next-line
|
||||
if (defaults.hasOwnProperty(key)) {
|
||||
// eslint-disable-next-line
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(defaults, key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
obj[key] = defaults[key];
|
||||
}
|
||||
}
|
||||
|
@ -411,8 +408,7 @@ export class KeyNameMap<KeyType, ValueType> {
|
|||
/** Determine if the map has an entry */
|
||||
public has(key: KeyType, name: string) {
|
||||
if (this.mapping.has(key)) {
|
||||
// eslint-disable-next-line
|
||||
return this.mapping.get(key).hasOwnProperty(name);
|
||||
return Object.prototype.hasOwnProperty.call(this.mapping.get(key), name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -421,8 +417,7 @@ export class KeyNameMap<KeyType, ValueType> {
|
|||
public get(key: KeyType, name: string) {
|
||||
if (this.mapping.has(key)) {
|
||||
const m = this.mapping.get(key);
|
||||
// eslint-disable-next-line
|
||||
if (m.hasOwnProperty(name)) {
|
||||
if (Object.prototype.hasOwnProperty.call(m, name)) {
|
||||
return m[name];
|
||||
}
|
||||
return null;
|
||||
|
@ -435,8 +430,7 @@ export class KeyNameMap<KeyType, ValueType> {
|
|||
) {
|
||||
this.mapping.forEach((v, key) => {
|
||||
for (const p in v) {
|
||||
// eslint-disable-next-line
|
||||
if (v.hasOwnProperty(p)) {
|
||||
if (Object.prototype.hasOwnProperty.call(v, p)) {
|
||||
callback(v[p], key, p);
|
||||
}
|
||||
}
|
||||
|
@ -574,7 +568,7 @@ export function getSortFunctionByData(values: string[]) {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
if (values.length > 0){
|
||||
if (values.length > 0) {
|
||||
const testResult = values
|
||||
.map((val) => testToRange(val))
|
||||
.reduceRight((a, b) => a && b);
|
||||
|
@ -586,8 +580,8 @@ export function getSortFunctionByData(values: string[]) {
|
|||
return +aNum < +bNum
|
||||
? 1
|
||||
: +a.split("-").pop() < +b.split("-").pop()
|
||||
? 1
|
||||
: -1;
|
||||
? 1
|
||||
: -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ export class ShadowContext implements Context {
|
|||
public shadows: { [name: string]: ValueType } = {}
|
||||
) {}
|
||||
public getVariable(name: string): ValueType {
|
||||
// eslint-disable-next-line
|
||||
if (this.shadows.hasOwnProperty(name)) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.shadows, name)) {
|
||||
return this.shadows[name];
|
||||
}
|
||||
return this.upstream.getVariable(name);
|
||||
|
@ -43,8 +42,7 @@ export type PatternReplacer = (expr: Expression) => Expression | void;
|
|||
export function variableReplacer(map: { [name: string]: string }) {
|
||||
return (expr: Expression) => {
|
||||
if (expr instanceof Variable) {
|
||||
// eslint-disable-next-line
|
||||
if (map.hasOwnProperty(expr.name)) {
|
||||
if (Object.prototype.hasOwnProperty.call(map, expr.name)) {
|
||||
return new Variable(map[expr.name]);
|
||||
}
|
||||
}
|
||||
|
@ -244,8 +242,7 @@ export class FunctionCall extends Expression {
|
|||
this.args = args;
|
||||
let v = <any>functions;
|
||||
for (const part of parts) {
|
||||
// eslint-disable-next-line
|
||||
if (v.hasOwnProperty(part)) {
|
||||
if (Object.prototype.hasOwnProperty.call(v, part)) {
|
||||
v = v[part];
|
||||
} else {
|
||||
v = undefined;
|
||||
|
|
|
@ -111,7 +111,6 @@ export class ChartRenderer {
|
|||
*/
|
||||
// eslint-disable-next-line
|
||||
private renderChart(
|
||||
// eslint-disable-next-line
|
||||
dataset: Dataset.Dataset,
|
||||
chart: Specification.Chart,
|
||||
chartState: Specification.ChartState
|
||||
|
|
|
@ -29,8 +29,12 @@ export class DataflowTableGroupedContext implements Expression.Context {
|
|||
}
|
||||
|
||||
public getVariable(name: string) {
|
||||
// eslint-disable-next-line
|
||||
if (this.table.rows[this.indices[0]]?.hasOwnProperty(name)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
this.table.rows[this.indices[0]],
|
||||
name
|
||||
)
|
||||
) {
|
||||
return this.indices.map((i) => this.table.rows[i][name]);
|
||||
}
|
||||
return this.table.getVariable(name);
|
||||
|
|
|
@ -10,8 +10,9 @@ export class CompiledFilter {
|
|||
const map = filter.categories.values;
|
||||
this.filter = (context) => {
|
||||
const val = expr.getStringValue(context);
|
||||
// eslint-disable-next-line
|
||||
return map.hasOwnProperty(val) && map[val] == true;
|
||||
return (
|
||||
Object.prototype.hasOwnProperty.call(map, val) && map[val] == true
|
||||
);
|
||||
};
|
||||
} else if (filter.expression) {
|
||||
const expr = cache.parse(filter.expression);
|
||||
|
|
|
@ -7,7 +7,7 @@ import * as Graphics from "../../graphics";
|
|||
import { LegendClass, LegendProperties } from "./legend";
|
||||
import { Controls } from "..";
|
||||
import { strings } from "../../../strings";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/manager";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
|
||||
|
||||
export interface CategoricalLegendItem {
|
||||
type: "number" | "color" | "boolean";
|
||||
|
@ -40,8 +40,7 @@ export class CategoricalLegendClass extends LegendClass {
|
|||
const items: CategoricalLegendItem[] = [];
|
||||
for (const key in mapping) {
|
||||
if (
|
||||
// eslint-disable-next-line
|
||||
mapping.hasOwnProperty(key) &&
|
||||
Object.prototype.hasOwnProperty.call(mapping, key) &&
|
||||
!key.startsWith(ReservedMappingKeyNamePrefix)
|
||||
) {
|
||||
switch (scaleObject.classID) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { LegendProperties, LegendState } from "./legend";
|
|||
import { CategoricalLegendClass } from "./categorical_legend";
|
||||
import { strings } from "../../../strings";
|
||||
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/manager";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
|
||||
|
||||
export type LegendSourceType = "columnNames" | "columnValues";
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
defaultFont,
|
||||
defaultFontSizeLegend,
|
||||
} from "../../../app/stores/defaults";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/manager";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
|
||||
import { Prototypes } from "../../../container";
|
||||
import { strings } from "../../../strings";
|
||||
import { Color, indexOf, rgbToHex } from "../../common";
|
||||
|
|
|
@ -139,8 +139,9 @@ export abstract class ObjectClass<
|
|||
};
|
||||
obj.properties = deepClone(this.defaultProperties);
|
||||
for (const attr in this.defaultMappingValues) {
|
||||
// eslint-disable-next-line
|
||||
if (this.defaultMappingValues.hasOwnProperty(attr)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(this.defaultMappingValues, attr)
|
||||
) {
|
||||
const value = deepClone(this.defaultMappingValues[attr]);
|
||||
obj.mappings[attr] = <Specification.ValueMapping>{
|
||||
type: MappingType.value,
|
||||
|
|
|
@ -53,7 +53,7 @@ import {
|
|||
import { DataflowManager, DataflowTable } from "../dataflow";
|
||||
import * as Expression from "../../expression";
|
||||
import { CompiledGroupBy } from "../group_by";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/manager";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
|
||||
import { type2DerivedColumns } from "../../../app/views/dataset/common";
|
||||
import React = require("react");
|
||||
|
||||
|
|
|
@ -75,8 +75,7 @@ export abstract class StaticMapService {
|
|||
function buildQueryParameters(options: { [name: string]: string }) {
|
||||
const r: string[] = [];
|
||||
for (const p in options) {
|
||||
// eslint-disable-next-line
|
||||
if (options.hasOwnProperty(p)) {
|
||||
if (Object.prototype.hasOwnProperty.call(options, p)) {
|
||||
r.push(encodeURIComponent(p) + "=" + encodeURIComponent(options[p]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { Controls } from "../common";
|
||||
import { deepClone } from "../../common";
|
||||
import { Dataset, Expression } from "../../index";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/manager";
|
||||
import { CharticulatorPropertyAccessors } from "../../../app/views/panels/widgets/types";
|
||||
|
||||
export function getTableColumns(
|
||||
manager: Controls.WidgetManager & CharticulatorPropertyAccessors
|
||||
|
|
|
@ -42,8 +42,7 @@ function reuseMapping<T>(
|
|||
}
|
||||
// Assign remaining keys from the domain
|
||||
domain.forEach((v, d) => {
|
||||
// eslint-disable-next-line
|
||||
if (!result.hasOwnProperty(d)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, d)) {
|
||||
if (available.length > 0) {
|
||||
result[d] = available[0];
|
||||
available.splice(0, 1);
|
||||
|
@ -146,8 +145,7 @@ export class CategoricalScaleNumber extends ScaleClass<
|
|||
const props = this.object.properties;
|
||||
const keys: string[] = [];
|
||||
for (const key in props.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (props.mapping.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(props.mapping, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +272,7 @@ export class CategoricalScaleColor extends ScaleClass<
|
|||
});
|
||||
} else if (getDefaultColorPaletteGenerator()) {
|
||||
s.domain.forEach((v, d) => {
|
||||
props.mapping[d] = getDefaultColorPaletteByValue(d, s.length);
|
||||
props.mapping[d] = getDefaultColorPaletteByValue(d);
|
||||
});
|
||||
} else {
|
||||
colorList = getDefaultColorPalette(s.length);
|
||||
|
@ -291,8 +289,7 @@ export class CategoricalScaleColor extends ScaleClass<
|
|||
const props = this.object.properties;
|
||||
const keys: string[] = [];
|
||||
for (const key in props.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (props.mapping.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(props.mapping, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
@ -433,8 +430,7 @@ export class CategoricalScaleEnum extends ScaleClass<
|
|||
const props = this.object.properties;
|
||||
const keys: string[] = [];
|
||||
for (const key in props.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (props.mapping.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(props.mapping, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
@ -520,8 +516,7 @@ export class CategoricalScaleBoolean extends ScaleClass<
|
|||
const mappingALL: { [name: string]: boolean } = {};
|
||||
const mappingNONE: { [name: string]: boolean } = {};
|
||||
for (const key in props.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (props.mapping.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(props.mapping, key)) {
|
||||
items.push(
|
||||
manager.inputBoolean(
|
||||
{ property: "mapping", field: key },
|
||||
|
@ -619,8 +614,7 @@ export class CategoricalScaleImage extends ScaleClass<
|
|||
const props = this.object.properties;
|
||||
const keys: string[] = [];
|
||||
for (const key in props.mapping) {
|
||||
// eslint-disable-next-line
|
||||
if (props.mapping.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(props.mapping, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -225,8 +225,7 @@ export class ChartConstraintSolver {
|
|||
);
|
||||
}
|
||||
if (!info.stateExclude) {
|
||||
// eslint-disable-next-line
|
||||
if (object.mappings.hasOwnProperty(attr)) {
|
||||
if (Object.prototype.hasOwnProperty.call(object.mappings, attr)) {
|
||||
// If the attribute is mapped, apply the mapping, and do not compute gradient
|
||||
const mapping = object.mappings[attr];
|
||||
this.addMapping(
|
||||
|
@ -743,8 +742,7 @@ export class GlyphConstraintAnalyzer extends ConstraintSolver {
|
|||
continue;
|
||||
}
|
||||
this.addAttribute(glyphState.attributes, attr, glyph._id);
|
||||
// eslint-disable-next-line
|
||||
if (glyph.mappings.hasOwnProperty(attr)) {
|
||||
if (Object.prototype.hasOwnProperty.call(glyph.mappings, attr)) {
|
||||
this.addMapping(
|
||||
glyphState.attributes,
|
||||
attr,
|
||||
|
@ -764,8 +762,7 @@ export class GlyphConstraintAnalyzer extends ConstraintSolver {
|
|||
continue;
|
||||
}
|
||||
this.addAttribute(markState.attributes, attr, mark._id);
|
||||
// eslint-disable-next-line
|
||||
if (mark.mappings.hasOwnProperty(attr)) {
|
||||
if (Object.prototype.hasOwnProperty.call(mark.mappings, attr)) {
|
||||
this.addMapping(
|
||||
markState.attributes,
|
||||
attr,
|
||||
|
|
|
@ -1,31 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
import * as React from "react";
|
||||
import { ColorPicker, GradientPicker } from "../../app/components";
|
||||
|
||||
export class ColorPickerTestView extends React.Component<{}, {}> {
|
||||
public render() {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
background: "#eee",
|
||||
border: "10px solid #aaa",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
<ColorPicker
|
||||
defaultValue={{ r: 117, g: 232, b: 75 }}
|
||||
allowNull={true}
|
||||
onPick={(value) => {
|
||||
console.log(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import { GradientPicker } from "../../app/components";
|
||||
|
||||
export class GradientPickerTestView extends React.Component<{}, {}> {
|
||||
public render() {
|
||||
|
@ -47,6 +23,5 @@ export class GradientPickerTestView extends React.Component<{}, {}> {
|
|||
}
|
||||
|
||||
export function register(f: any) {
|
||||
f("ColorPicker", ColorPickerTestView);
|
||||
f("GradientPicker", GradientPickerTestView);
|
||||
}
|
||||
|
|
|
@ -102,8 +102,7 @@ export class CharticulatorWorker
|
|||
src: Specification.AttributeMap
|
||||
) => {
|
||||
for (const key in src) {
|
||||
// eslint-disable-next-line
|
||||
if (src.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(src, key)) {
|
||||
dest[key] = src[key] as any;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче