* 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:
Ramil Minyukov 2022-01-11 17:28:03 +03:00 коммит произвёл GitHub
Родитель 5bba2ba68b
Коммит aa57f387c3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
89 изменённых файлов: 915 добавлений и 4426 удалений

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

@ -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> {

6
src/app/utils/noop.ts Normal 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;
}
}