Родитель
e7785e5fb3
Коммит
44690a9bc4
|
@ -65,7 +65,7 @@ export class TreeLegend extends React.Component<ITreeLegendProps> {
|
|||
<circle
|
||||
r="26"
|
||||
className={classNames.node}
|
||||
style={this.props.nodeDetail.errorColor}
|
||||
style={{ fill: this.props.nodeDetail.errorColor }}
|
||||
/>
|
||||
<g
|
||||
style={this.props.nodeDetail.maskDown}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { SVGToolTip } from "@responsible-ai/core-ui";
|
||||
import { getRandomId, SVGToolTip } from "@responsible-ai/core-ui";
|
||||
import { HierarchyPointNode } from "d3-hierarchy";
|
||||
import { IProcessedStyleSet } from "office-ui-fabric-react";
|
||||
import { getTheme, IProcessedStyleSet } from "office-ui-fabric-react";
|
||||
import React from "react";
|
||||
|
||||
import { isColorDark } from "../../ColorPalette";
|
||||
|
@ -32,6 +32,8 @@ export class TreeViewNode extends React.Component<ITreeViewNodeProps> {
|
|||
public render(): React.ReactNode {
|
||||
const { node } = this.props;
|
||||
const classNames = treeViewRendererStyles();
|
||||
const gradientFillId = getRandomId();
|
||||
const theme = getTheme();
|
||||
return (
|
||||
<>
|
||||
<g
|
||||
|
@ -40,10 +42,47 @@ export class TreeViewNode extends React.Component<ITreeViewNodeProps> {
|
|||
pointerEvents="all"
|
||||
ref={this.ref}
|
||||
>
|
||||
<linearGradient id={gradientFillId} x1="0.5" y1="1" x2="0.5" y2="0">
|
||||
<stop
|
||||
offset="0%"
|
||||
stopOpacity="1"
|
||||
stopColor={this.props.node.data.errorColor}
|
||||
/>
|
||||
<stop
|
||||
offset={`${
|
||||
(this.props.node.data.error /
|
||||
this.props.node.data.rootErrorSize) *
|
||||
100
|
||||
}%`}
|
||||
stopOpacity="1"
|
||||
stopColor={this.props.node.data.errorColor}
|
||||
/>
|
||||
<stop
|
||||
offset={`${
|
||||
(this.props.node.data.error /
|
||||
this.props.node.data.rootErrorSize) *
|
||||
100
|
||||
}%`}
|
||||
stopOpacity="1"
|
||||
stopColor={theme.semanticColors.bodyBackgroundChecked}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopOpacity="1"
|
||||
stopColor={theme.semanticColors.bodyBackgroundChecked}
|
||||
/>
|
||||
</linearGradient>
|
||||
<circle
|
||||
r={node.data.r}
|
||||
className={classNames.node}
|
||||
style={node.data.nodeState.errorStyle}
|
||||
style={{
|
||||
color: "black",
|
||||
stroke: this.props.node.data.nodeState.onSelectedPath
|
||||
? theme.semanticColors.link
|
||||
: this.props.node.data.errorColor,
|
||||
strokeWidth: this.props.node.data.nodeState.onSelectedPath ? 3 : 2
|
||||
}}
|
||||
fill={`url(#${gradientFillId})`}
|
||||
/>
|
||||
{node.data.nodeState.onSelectedPath && (
|
||||
<circle
|
||||
|
@ -55,19 +94,12 @@ export class TreeViewNode extends React.Component<ITreeViewNodeProps> {
|
|||
}
|
||||
/>
|
||||
)}
|
||||
<g
|
||||
style={node.data.fillstyleDown}
|
||||
mask="url(#Mask)"
|
||||
className={classNames.nopointer}
|
||||
>
|
||||
<circle r="26" style={node.data.fillstyleUp} />
|
||||
</g>
|
||||
<text
|
||||
textAnchor="middle"
|
||||
className={this.getNodeClassName(
|
||||
classNames,
|
||||
node.data.filterProps.errorCoverage,
|
||||
node.data.errorColor.fill
|
||||
node.data.errorColor
|
||||
)}
|
||||
>
|
||||
{node.data.error}/{node.data.size}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { Property } from "csstype";
|
||||
import {
|
||||
IStyle,
|
||||
mergeStyleSets,
|
||||
|
@ -14,7 +15,6 @@ import { ColorPalette } from "../../ColorPalette";
|
|||
export interface ITreeViewRendererStyles {
|
||||
clickedNodeDashed: IStyle;
|
||||
clickedNodeFull: IStyle;
|
||||
detailLines: IStyle;
|
||||
filledNodeText: IStyle;
|
||||
legend: IStyle;
|
||||
linkLabel: IStyle;
|
||||
|
@ -26,9 +26,10 @@ export interface ITreeViewRendererStyles {
|
|||
treeDescription: IStyle;
|
||||
}
|
||||
|
||||
export const treeViewRendererStyles: () => IProcessedStyleSet<
|
||||
ITreeViewRendererStyles
|
||||
> = () => {
|
||||
export const treeViewRendererStyles = (props?: {
|
||||
onSelectedPath?: boolean;
|
||||
fill?: Property.Color;
|
||||
}): IProcessedStyleSet<ITreeViewRendererStyles> => {
|
||||
const theme = getTheme();
|
||||
const nodeTextStyle = {
|
||||
fontSize: "10px",
|
||||
|
@ -48,13 +49,6 @@ export const treeViewRendererStyles: () => IProcessedStyleSet<
|
|||
stroke: "#0078D4",
|
||||
strokeWidth: 2
|
||||
},
|
||||
detailLines: {
|
||||
fill: theme.semanticColors.inputBackground,
|
||||
fontWeight: "bold",
|
||||
pointerEvents: "none",
|
||||
textAnchor: "start",
|
||||
transform: "translate(0px, 10px)"
|
||||
},
|
||||
filledNodeText: mergeStyles([
|
||||
nodeTextStyle,
|
||||
{
|
||||
|
@ -71,12 +65,13 @@ export const treeViewRendererStyles: () => IProcessedStyleSet<
|
|||
},
|
||||
node: {
|
||||
":hover": {
|
||||
strokeWidth: "3px"
|
||||
stroke: `${theme.semanticColors.link} !important`,
|
||||
strokeWidth: "3px !important"
|
||||
},
|
||||
cursor: "pointer",
|
||||
opacity: "1",
|
||||
stroke: "#089acc",
|
||||
strokeWidth: "0px"
|
||||
|
||||
stroke: props?.onSelectedPath ? theme.semanticColors.link : props?.fill,
|
||||
strokeWidth: props?.onSelectedPath ? 3 : 2
|
||||
},
|
||||
nodeText: mergeStyles([
|
||||
nodeTextStyle,
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
getRandomId
|
||||
} from "@responsible-ai/core-ui";
|
||||
import { localization } from "@responsible-ai/localization";
|
||||
import { Property } from "csstype";
|
||||
import { max as d3max } from "d3-array";
|
||||
import {
|
||||
stratify as d3stratify,
|
||||
|
@ -216,17 +217,8 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
// The code below generates the circular nodes in the tree view.
|
||||
const nodeData: Array<HierarchyPointNode<ITreeNode>> = rootDescendants.map(
|
||||
(d: HierarchyPointNode<ITreeNode>): HierarchyPointNode<ITreeNode> => {
|
||||
let selectedStyle: Record<string, number | string | undefined> = {
|
||||
fill: d.data.errorColor.fill
|
||||
};
|
||||
|
||||
if (d.data.nodeState.onSelectedPath) {
|
||||
selectedStyle = { fill: d.data.errorColor.fill, strokeWidth: 3 };
|
||||
}
|
||||
|
||||
// Update node state based on new user actions
|
||||
d.data.nodeState = {
|
||||
errorStyle: selectedStyle,
|
||||
isSelectedLeaf: d.data.nodeState.isSelectedLeaf,
|
||||
onSelectedPath: d.data.nodeState.onSelectedPath,
|
||||
style: {
|
||||
|
@ -402,7 +394,7 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
const minColor = ColorPalette.MinColor;
|
||||
const maxColor = ColorPalette.MaxColor;
|
||||
|
||||
const colorgrad = d3scaleLinear<string>()
|
||||
const colorgrad = d3scaleLinear<Property.Color>()
|
||||
.domain([min, max])
|
||||
.interpolate(d3interpolateHcl)
|
||||
.range([minColor, maxColor]);
|
||||
|
@ -416,12 +408,10 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
const calcMaskShift = globalErrorPerc * 52;
|
||||
const filterProps = this.calculateFilterProps(node, rootErrorSize);
|
||||
|
||||
let heatmapStyle: { fill: string | undefined } = {
|
||||
fill: errorAvgColor
|
||||
};
|
||||
let heatmapStyle: Property.Color = errorAvgColor;
|
||||
|
||||
if (node.error / node.size > rootLocalError * errorRatioThreshold) {
|
||||
heatmapStyle = { fill: colorgrad(localErrorPerc) };
|
||||
heatmapStyle = colorgrad(localErrorPerc) || errorAvgColor;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -429,13 +419,6 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
condition: node.condition,
|
||||
error: node.error,
|
||||
errorColor: heatmapStyle,
|
||||
fillstyleDown: {
|
||||
transform: `translate(0px, -${calcMaskShift}px)`
|
||||
},
|
||||
fillstyleUp: {
|
||||
fill: ColorPalette.FillStyle,
|
||||
transform: `translate(0px, ${calcMaskShift}px)`
|
||||
},
|
||||
filterProps,
|
||||
id: node.id,
|
||||
maskShift: calcMaskShift,
|
||||
|
@ -443,7 +426,6 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
nodeIndex: node.nodeIndex,
|
||||
nodeName: node.nodeName,
|
||||
nodeState: {
|
||||
errorStyle: undefined,
|
||||
isSelectedLeaf: false,
|
||||
onSelectedPath: false,
|
||||
style: undefined
|
||||
|
@ -452,6 +434,7 @@ export class TreeViewRenderer extends React.PureComponent<
|
|||
parentNodeName: node.parentNodeName,
|
||||
pathFromRoot: node.pathFromRoot,
|
||||
r: 28,
|
||||
rootErrorSize,
|
||||
size: node.size,
|
||||
sourceRowKeyHash: node.sourceRowKeyHash,
|
||||
success: node.success
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { Property } from "csstype";
|
||||
import { HierarchyPointNode } from "d3-hierarchy";
|
||||
|
||||
import { FilterProps } from "./FilterProps";
|
||||
|
@ -24,7 +25,7 @@ export interface INodeDetail {
|
|||
instanceInfo: string;
|
||||
errorInfo: string;
|
||||
successInfo: string;
|
||||
errorColor: IErrorColorStyle;
|
||||
errorColor: Property.Color;
|
||||
maskDown: ITransform;
|
||||
maskUp: ITransform;
|
||||
}
|
||||
|
@ -46,6 +47,7 @@ export interface ITreeNode {
|
|||
arg: number;
|
||||
condition: string;
|
||||
error: number;
|
||||
rootErrorSize: number;
|
||||
id: string;
|
||||
method: string;
|
||||
nodeIndex: number;
|
||||
|
@ -56,9 +58,7 @@ export interface ITreeNode {
|
|||
size: number;
|
||||
sourceRowKeyHash: string;
|
||||
success: number;
|
||||
errorColor: IErrorColorStyle;
|
||||
fillstyleUp: IFillStyleUp;
|
||||
fillstyleDown: ITransform;
|
||||
errorColor: Property.Color;
|
||||
filterProps: FilterProps;
|
||||
maskShift: number;
|
||||
r: number;
|
||||
|
@ -72,7 +72,6 @@ export interface IFillStyleUp {
|
|||
|
||||
// Contains node state that changes with UI clicks
|
||||
export interface INodeState {
|
||||
errorStyle: Record<string, number | string | undefined> | undefined;
|
||||
onSelectedPath: boolean;
|
||||
isSelectedLeaf: boolean;
|
||||
style: ITransform | undefined;
|
||||
|
@ -81,9 +80,7 @@ export interface INodeState {
|
|||
export function createInitialTreeViewState(): ITreeViewRendererState {
|
||||
return {
|
||||
nodeDetail: {
|
||||
errorColor: {
|
||||
fill: "#eaeaea"
|
||||
},
|
||||
errorColor: "#eaeaea",
|
||||
errorInfo: "0 Errors",
|
||||
globalError: "0",
|
||||
instanceInfo: "0 Instances",
|
||||
|
|
Загрузка…
Ссылка в новой задаче