* version upgrade

* context menu on clear catcher added

* selection quick fix

* matrix selection wip

* updated packages

* position bug fix

* contrast mode label display bug fix

* 3.0.3

* deleted identityIndex from node label

* check

* Delete yarn.lock

* code cleanup in sankeyDIagrams.ts

Co-authored-by: Nikita Grachev <v-grniki@microsoft.com>
This commit is contained in:
MulyukovAidar 2020-12-15 18:22:16 +03:00 коммит произвёл GitHub
Родитель d7db394c69
Коммит 3abaecf013
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 5348 добавлений и 6168 удалений

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

@ -1,3 +1,7 @@
## 3.0.1
* Context menu fix
* Selection fix
## 2.1.0
* Updated APIs
* Sorting added

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

@ -6,29 +6,28 @@
"displayName": "Source",
"displayNameKey": "Visual_Source"
},
{
"name": "SourceLabels",
"kind": "Measure",
"displayName": "Source labels",
"displayNameKey": "Visual_SourceLabels"
},
{
"name": "Destination",
"kind": "Grouping",
"kind": "GroupingOrMeasure",
"displayName": "Destination",
"displayNameKey": "Visual_Destination"
},
{
"name": "DestinationLabels",
"kind": "Measure",
"displayName": "Destination labels",
"displayNameKey": "Visual_DestinationLabels"
},
{
"name": "Weight",
"kind": "Measure",
"displayName": "Weight",
"displayNameKey": "Visual_Weight"
"displayNameKey": "Visual_Weight",
"requiredTypes": [
{
"numeric": true
}
]
},
{
"name": "SourceLabels",
"kind": "Measure",
"displayName": "Source labels",
"displayNameKey": "Visual_SourceLabels"
}
],
"dataViewMappings": [
@ -36,7 +35,7 @@
"conditions": [
{
"Source": {
"min": 0,
"min": 1,
"max": 1
},
"Destination": {
@ -47,10 +46,6 @@
"min": 0,
"max": 1
},
"DestinationLabels": {
"min": 0,
"max": 1
},
"Weight": {
"min": 0,
"max": 1
@ -66,8 +61,8 @@
}
},
{
"for": {
"in": "Destination"
"bind": {
"to": "Destination"
}
}
]
@ -79,11 +74,6 @@
"in": "SourceLabels"
}
},
{
"for": {
"in": "DestinationLabels"
}
},
{
"for": {
"in": "Weight"

9102
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,6 +1,6 @@
{
"name": "powerbi-visuals-sankey",
"version": "2.1.3",
"version": "3.0.3",
"description": "Sankey is a type of flow diagram in which the width of the series is in proportion to the quantity of the flow. Use it to find major contributions to an overall flow.",
"repository": {
"type": "git",
@ -16,7 +16,7 @@
"package": "pbiviz package",
"lint": "tslint -c tslint.json -p tsconfig.json --fix",
"test": "karma start",
"cert": "pbiviz --create-cert"
"cert": "pbiviz --install-cert"
},
"author": {
"name": "Microsoft",
@ -28,53 +28,53 @@
},
"homepage": "https://github.com/Microsoft/powerbi-visuals-sankey#readme",
"devDependencies": {
"@types/d3": "5.7.2",
"@types/jasmine": "3.5.2",
"@types/d3": "6.2.0",
"@types/jasmine": "3.6.2",
"@types/jasmine-jquery": "1.5.33",
"@types/jquery": "3.3.31",
"@types/karma": "3.0.5",
"@types/jquery": "3.5.5",
"@types/karma": "5.0.1",
"@types/lodash-es": "4.17.3",
"coveralls": "3.0.9",
"d3": "5.15.0",
"globalize": "1.4.2",
"coveralls": "3.1.0",
"d3": "^5.16.0",
"globalize": "1.6.0",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine": "3.5.0",
"jasmine-core": "3.5.0",
"jasmine": "3.6.3",
"jasmine-core": "3.6.0",
"jasmine-jquery": "2.1.1",
"jquery": "^3.4.1",
"karma": "^4.4.1",
"jquery": "^3.5.1",
"karma": "^5.2.3",
"karma-chrome-launcher": "3.1.0",
"karma-coverage": "2.0.1",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-jasmine": "3.1.0",
"karma-coverage": "2.0.3",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "4.0.1",
"karma-junit-reporter": "^2.0.1",
"karma-sourcemap-loader": "^0.3.7",
"karma-typescript": "4.1.1",
"karma-sourcemap-loader": "^0.3.8",
"karma-typescript": "5.2.0",
"karma-typescript-preprocessor": "0.4.0",
"karma-webpack": "4.0.2",
"lodash-es": "4.17.15",
"powerbi-models": "1.3.1",
"powerbi-visuals-api": "^2.6.2",
"powerbi-visuals-tools": "^3.1.10",
"powerbi-visuals-utils-colorutils": "^2.2.1",
"powerbi-visuals-utils-dataviewutils": "^2.2.1",
"powerbi-visuals-utils-formattingutils": "^4.5.0",
"powerbi-visuals-utils-interactivityutils": "5.6.0",
"powerbi-visuals-utils-svgutils": "^2.2.1",
"powerbi-visuals-utils-testutils": "^2.2.1",
"powerbi-visuals-utils-tooltiputils": "^2.3.1",
"powerbi-visuals-utils-typeutils": "^2.2.1",
"puppeteer": "^2.1.1",
"style-loader": "1.1.3",
"ts-loader": "6.2.1",
"ts-node": "8.6.2",
"powerbi-models": "1.7.0",
"powerbi-visuals-api": "^3.4.0",
"powerbi-visuals-tools": "^3.1.15",
"powerbi-visuals-utils-colorutils": "^2.3.0",
"powerbi-visuals-utils-dataviewutils": "^2.4.0",
"powerbi-visuals-utils-formattingutils": "^4.7.0",
"powerbi-visuals-utils-interactivityutils": "5.7.0",
"powerbi-visuals-utils-svgutils": "^2.3.0",
"powerbi-visuals-utils-testutils": "^2.3.4",
"powerbi-visuals-utils-tooltiputils": "^2.4.0",
"powerbi-visuals-utils-typeutils": "^2.3.0",
"puppeteer": "^5.5.0",
"style-loader": "2.0.0",
"ts-loader": "8.0.11",
"ts-node": "9.1.1",
"tslint": "^6.0.0",
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.7.5",
"webpack": "4.41.5"
"typescript": "^4.1.2",
"webpack": "5.10.0"
},
"dependencies": {
"core-js": "^3.6.4",
"serialize-javascript": "^2.1.2"
"core-js": "^3.8.1",
"serialize-javascript": "^5.0.1"
}
}

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

@ -1,15 +1,15 @@
{
"visual": {
"name": "SankeyDiagram",
"displayName": "Sankey 2.1.3",
"guid": "SankeyDiagram1446463184954",
"displayName": "Sankey 3.0.3",
"guid": "sankey02300D1BE6F5427989F3DE31CCA9E0F32020",
"visualClassName": "SankeyDiagram",
"version": "2.1.3",
"version": "3.0.3",
"description": "Sankey is a type of flow diagram in which the width of the series is in proportion to the quantity of the flow. Use it to find major contributions to an overall flow.",
"supportUrl": "https://community.powerbi.com",
"gitHubUrl": "https://github.com/Microsoft/powerbi-visuals-sankey"
},
"apiVersion": "2.6.0",
"apiVersion": "3.4.0",
"author": {
"name": "Microsoft",
"email": "pbicvsupport@microsoft.com"

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

@ -136,6 +136,18 @@ export class SankeyDiagramBehavior implements IInteractiveBehavior {
}
private bindClickEventToClearCatcher(): void {
this.behaviorOptions.clearCatcher.on("contextmenu", () => {
const event: MouseEvent = (<MouseEvent>getEvent()) || <MouseEvent>window.event;
if (event) {
this.selectionHandler.handleContextMenu(
null,
{
x: event.clientX,
y: event.clientY
});
event.preventDefault();
}
});
this.behaviorOptions.clearCatcher.on("click", () => {
this.clearSelection();
this.createAnEmptySelectedDataPoints();

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

@ -85,6 +85,7 @@ export interface SankeyDiagramLink extends
TooltipEnabledDataPoint,
SelectableDataPoint {
label: string;
source: SankeyDiagramNode;
destination: SankeyDiagramNode;
weigth: number;

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

@ -52,8 +52,9 @@ import DataViewMetadataColumn = powerbi.DataViewMetadataColumn;
import DataViewValueColumns = powerbi.DataViewValueColumns;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import DataViewMatrix = powerbi.DataViewMatrix;
// powerbi.visuals
import ISelectionIdBuilder = powerbi.visuals.ISelectionIdBuilder;
import ISelectionId = powerbi.visuals.ISelectionId;
// powerbi.extensibility
@ -131,6 +132,7 @@ import {
SankeyDiagramBehaviorOptions,
SankeyDiagramBehavior
} from "./behavior";
import { data } from "jquery";
export class SankeyDiagram implements IVisual {
private static ClassName: string = "sankeyDiagram";
@ -322,32 +324,28 @@ export class SankeyDiagram implements IVisual {
public update(visualUpdateOptions: VisualUpdateOptions): void {
this.visualHost.eventService.renderingStarted(visualUpdateOptions);
try {
let sankeyDiagramDataView: SankeyDiagramDataView,
viewport: IViewport = visualUpdateOptions
&& visualUpdateOptions.viewport
|| SankeyDiagram.DefaultViewport,
dataView: DataView = visualUpdateOptions
&& visualUpdateOptions.dataViews
&& visualUpdateOptions.dataViews[0];
let sankeyDiagramDataView: SankeyDiagramDataView,
viewport: IViewport = visualUpdateOptions
&& visualUpdateOptions.viewport
|| SankeyDiagram.DefaultViewport,
dataView: DataView = visualUpdateOptions
&& visualUpdateOptions.dataViews
&& visualUpdateOptions.dataViews[0];
this.updateViewport(visualUpdateOptions.viewport);
this.updateViewport(visualUpdateOptions.viewport);
sankeyDiagramDataView = this.converter(dataView);
sankeyDiagramDataView = this.converter(dataView);
this.computePositions(sankeyDiagramDataView);
this.computePositions(sankeyDiagramDataView);
this.dataView = sankeyDiagramDataView;
this.dataView = sankeyDiagramDataView;
this.applySelectionStateToData();
this.applySelectionStateToData();
this.render(sankeyDiagramDataView);
this.visualHost.eventService.renderingFinished(visualUpdateOptions);
}
catch (e) {
this.visualHost.eventService.renderingFailed(visualUpdateOptions, e);
console.log(e);
}
this.render(sankeyDiagramDataView);
this.visualHost.eventService.renderingFinished(visualUpdateOptions);
}
private updateViewport(viewport: IViewport): void {
@ -380,6 +378,48 @@ export class SankeyDiagram implements IVisual {
this.main.attr("transform", translate(this.margin.left, this.margin.top));
}
private createNewNode(node: DataViewMatrixNode, settings: SankeyDiagramSettings): SankeyDiagramNode {
let nodeFillColor = this.getColor(
SankeyDiagram.NodesPropertyIdentifier,
this.colorPalette.getColor(<string>node.value).value,
<any>node.objects);
let nodeStrokeColor = this.colorHelper.getHighContrastColor("foreground", nodeFillColor);
let name = <any>node.value;
let textProperties: TextProperties = {
text: name,
fontFamily: this.textProperties.fontFamily,
fontSize: this.textProperties.fontSize
};
let label: SankeyDiagramLabel = {
internalName: name,
name: name,
formattedName: name,//valueFormatterForCategories.format((<string>labelsDictionary[item].toString()).replace(SankeyDiagram.DuplicatedNamePostfix, "")),
width: textMeasurementService.measureSvgTextWidth(textProperties),
height: textMeasurementService.estimateSvgTextHeight(textProperties),
color: settings.labels.fill
};
return {
label: label,
links: [],
inputWeight: 0,
outputWeight: 0,
backwardWeight: 0,
selftLinkWeight: 0,
width: 10, //fix
height: 0,
fillColor: nodeFillColor,
strokeColor: nodeStrokeColor,
tooltipInfo: [],
selectableDataPoints: [],
settings: null,
identity: null,
selected: false
}
}
// tslint:disable-next-line: max-func-body-length
public converter(dataView: DataView): SankeyDiagramDataView {
const settings: SankeyDiagramSettings = this.parseSettings(dataView);
@ -399,16 +439,16 @@ export class SankeyDiagram implements IVisual {
|| !dataView.matrix.rows.root
|| !dataView.matrix.rows.root.children
|| !dataView.matrix.valueSources) {
return {
settings,
nodes: [],
links: [],
columns: []
}
return {
settings,
nodes: [],
links: [],
columns: []
}
}
let nodes: SankeyDiagramNode[],
links: SankeyDiagramLink[],
let nodes: SankeyDiagramNode[] = [],
links: SankeyDiagramLink[] = [],
categories: any[] = [],
sourceCategories: any[] = [],
destinationCategories: any[] = [],
@ -419,82 +459,150 @@ export class SankeyDiagram implements IVisual {
selectionIdBuilder: SelectionIdBuilder = new SelectionIdBuilder(
this.visualHost,
dataView.matrix),
valueSources = dataView.matrix.valueSources,
sourceLabelIndex: number,
destinationLabelIndex: number,
weightIndex: number;
valueSources = dataView.matrix.valueSources;
sourceLabelIndex = valueSources.indexOf(valueSources.filter((column: powerbi.DataViewMetadataColumn) => {
const sourceLabelIndex: number = valueSources.indexOf(valueSources.filter((column: powerbi.DataViewMetadataColumn) => {
return column.roles.SourceLabels;
}).pop());
destinationLabelIndex = valueSources.indexOf(valueSources.filter((source: powerbi.DataViewMetadataColumn) => {
return source.roles.DestinationLabels;
}).pop());
weightIndex = valueSources.indexOf(valueSources.filter((source: powerbi.DataViewMetadataColumn) => {
const weightIndex: number = valueSources.indexOf(valueSources.filter((source: powerbi.DataViewMetadataColumn) => {
return source.roles.Weight;
}).pop());
dataView.matrix.rows.root.children.forEach((source: DataViewMatrixNode) =>{
objects.push(source.objects);
const sourceFieldName = dataView.matrix.rows.levels[0].sources[0].displayName;
const destinationFieldName = dataView.matrix.rows.levels[1].sources[0].displayName;
const valueFieldName = dataView.matrix.valueSources[weightIndex] ? dataView.matrix.valueSources[weightIndex].displayName : null;
const formatOfWeigth = valueFormatter.getFormatStringByColumn(valueSources[weightIndex]);
let weightValues: number[] = [1];
dataView.matrix.rows.root.children.forEach(parent => {
let newSourceNode = this.createNewNode(parent, settings)
newSourceNode.identity = this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.createSelectionId();
nodes.push(newSourceNode);
});
dataView.matrix.rows.root.children.forEach((source: DataViewMatrixNode) =>{
categories.push(source.levelValues[0].value);
source.children.forEach((destination: DataViewMatrixNode) => {
sourceCategories.push(source.levelValues[0].value);
destinationCategories.push(destination.levelValues[0].value);
dataView.matrix.rows.root.children.forEach(parent => {
let foundSource: SankeyDiagramNode = nodes.find(found => found.label.name === parent.value)
// If both source and destination labels are present in DataView, populate appropiate arrays
if (sourceLabelIndex != -1 && destinationLabelIndex != -1)
{
sourceCategoriesLabels.push(destination.values[sourceLabelIndex].value);
destinationCategoriesLabels.push(destination.values[destinationLabelIndex].value);
parent.children.forEach(child => {
let linkLabel = undefined;
let weigth: any = SankeyDiagram.DefaultWeightValue;
let foundDestination: SankeyDiagramNode = nodes.find(found => found.label.name === child.value)
if (!foundDestination) {
foundDestination = this.createNewNode(child, settings);
foundDestination.identity = this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.withMatrixNode(child, dataView.matrix.rows.levels)
.createSelectionId();
nodes.push(foundDestination);
}
if (sourceLabelIndex != -1) {
linkLabel = (child.values[sourceLabelIndex] && child.values[sourceLabelIndex].value) ?
child.values[sourceLabelIndex].value || SankeyDiagram.DefaultWeightValue : SankeyDiagram.MinWeightValue;
}
// If weights are present, populate the weights array
if (weightIndex != -1)
{
weights.push(destination.values[weightIndex].value);
if (weightIndex != -1) {
weigth = (child.values[weightIndex] && child.values[weightIndex].value) ?
child.values[weightIndex].value || SankeyDiagram.DefaultWeightValue : SankeyDiagram.MinWeightValue;
weightValues.push(weigth);
}
objects.push(destination.objects);
categories.push(destination.levelValues[0].value);
let linkFillColor = this.getColor(
SankeyDiagram.LinksPropertyIdentifier,
SankeyDiagram.DefaultColourOfLink,
child.objects);
let linkStrokeColor = this.colorHelper.isHighContrast ? this.colorHelper.getHighContrastColor("foreground", linkFillColor) : linkFillColor;
let tooltipInfo = SankeyDiagram.getTooltipDataForLink(
valuesFormatterForWeigth,
foundSource.label.formattedName,
foundDestination.label.formattedName,
weigth,
sourceFieldName,
destinationFieldName,
valueFieldName
);
let link: SankeyDiagramLink = {
label: linkLabel && linkLabel.toString(),
source: foundSource,
destination: foundDestination,
weigth: weigth,
height: 10,
fillColor: linkFillColor,
strokeColor: linkStrokeColor,
dySource: 0,
dyDestination: 0,
tooltipInfo: tooltipInfo,
identity: this.visualHost.createSelectionIdBuilder()
.withMatrixNode(parent, dataView.matrix.rows.levels)
.withMatrixNode(child, dataView.matrix.rows.levels)
.createSelectionId(),
selected: false,
direction: SankeyLinkDirrections.Forward
}
let selectableDataPoint: SelectableDataPoint = SankeyDiagram.createSelectableDataPoint(<ISelectionId>link.identity);
foundSource.selectableDataPoints.push(selectableDataPoint);
foundDestination.selectableDataPoints.push(selectableDataPoint);
links.push(link);
foundSource.links.push(link);
foundDestination.links.push(link);
SankeyDiagram.updateValueOfNode(foundSource);
SankeyDiagram.updateValueOfNode(foundDestination);
});
});
const valuesFormatterForWeigth = valueFormatter.create({
format: formatOfWeigth,
value: Math.max(
settings.labels.unit !== 0 ? settings.labels.unit : d3.max(weightValues) || SankeyDiagram.MinWeightValue,
SankeyDiagram.MinWeightValue),
});
nodes = this.createNodes(
categories,
sourceCategories,
destinationCategories,
settings,
selectionIdBuilder,
dataView.matrix.rows.levels[0].sources[0],
objects,
sourceCategoriesLabels,
destinationCategoriesLabels);
links = this.createLinks(
nodes,
sourceCategories,
destinationCategories,
settings,
selectionIdBuilder,
weights,
objects,
dataView.matrix.rows.levels[0].sources[0].displayName,
dataView.matrix.rows.levels[1].sources[0].displayName,
dataView.matrix.valueSources[weightIndex] ? dataView.matrix.valueSources[weightIndex].displayName : null,
dataView.matrix.valueSources
);
let cycles: SankeyDiagramCycleDictionary = this.checkCycles(nodes);
if (settings.cyclesLinks.drawCycles === CyclesDrawType.Duplicate) {
links = this.processCyclesForwardLinks(cycles, nodes, links, settings);
}
nodes.forEach((node: SankeyDiagramNode) => {
node.tooltipInfo = SankeyDiagram.getTooltipForNode(
valuesFormatterForWeigth,
node.label.formattedName,
node.inputWeight
? node.inputWeight
: node.outputWeight,
this.localizationManager,
node.inputWeight > 0 && node.outputWeight > 0 ? `${sourceFieldName}-${destinationFieldName}` : node.outputWeight > 0
? sourceFieldName
: destinationFieldName,
valueFieldName
);
});
let sankeyDiagramDataView = {
nodes,
links,
@ -511,10 +619,8 @@ export class SankeyDiagram implements IVisual {
}
});
}
this.checkNodePositionSettings(nodes, settings);
this.restoreNodePositions(nodes, settings);
return sankeyDiagramDataView;
}
@ -699,219 +805,8 @@ export class SankeyDiagram implements IVisual {
return simpleCycles;
}
private createNodes(
categories: any[],
sourceCategories: any[],
destinationCategories: any[],
settings: SankeyDiagramSettings,
selectionIdBuilder: SelectionIdBuilder,
source: DataViewMetadataColumn,
objects: DataViewObjects[],
sourceCategoriesLabels?: any[],
destinationCategoriesLabels?: any[]): SankeyDiagramNode[] {
let nodes: SankeyDiagramNode[] = [],
valueFormatterForCategories: IValueFormatter,
nodeFillColor: string,
nodeStrokeColor: string,
selectionId: ISelectionId;
valueFormatterForCategories = valueFormatter.create({
format: valueFormatter.getFormatStringByColumn(source),
value: sourceCategories[0],
value2: destinationCategories[destinationCategories.length - 1]
});
// check self connected links
for (let index: number = 0; index < destinationCategories.length; index++) {
if (sourceCategoriesLabels[index] === undefined) {
sourceCategoriesLabels[index] = sourceCategories[index];
}
if (destinationCategoriesLabels[index] === undefined) {
destinationCategoriesLabels[index] = destinationCategories[index];
}
}
let labelsDictionary: Object = {};
sourceCategories.forEach((item: any, index: number) => {
labelsDictionary[item] = sourceCategoriesLabels[index] || "";
});
destinationCategories.forEach((item: any, index: number) => {
labelsDictionary[item] = destinationCategoriesLabels[index] || "";
});
categories.forEach((item: any, index: number) => {
let formattedValue: string = valueFormatterForCategories.format((<string>labelsDictionary[item].toString()).replace(SankeyDiagram.DuplicatedNamePostfix, "")),
label: SankeyDiagramLabel,
selectableDataPoint: SelectableDataPoint,
textProperties: TextProperties = {
text: formattedValue,
fontFamily: this.textProperties.fontFamily,
fontSize: this.textProperties.fontSize
};
label = {
internalName: item,
name: item,
formattedName: valueFormatterForCategories.format((<string>labelsDictionary[item].toString()).replace(SankeyDiagram.DuplicatedNamePostfix, "")),
width: textMeasurementService.measureSvgTextWidth(textProperties),
height: textMeasurementService.estimateSvgTextHeight(textProperties),
color: settings.labels.fill
};
nodeFillColor = this.getColor(
SankeyDiagram.NodesPropertyIdentifier,
this.colorPalette.getColor(item).value,
objects[index]);
nodeStrokeColor = this.colorHelper.getHighContrastColor("foreground", nodeFillColor);
selectionId = selectionIdBuilder.createSelectionId(index);
if (nodes.filter((node: SankeyDiagramNode) => {
return node.label.name === item;
}).length === 0)
nodes.push({
label: label,
links: [],
inputWeight: 0,
outputWeight: 0,
backwardWeight: 0,
selftLinkWeight: 0,
width: this.nodeWidth,
height: 0,
fillColor: nodeFillColor,
strokeColor: nodeStrokeColor,
tooltipInfo: [],
selectableDataPoints: [],
settings: null,
identity: selectionId,
selected: false
});
});
return nodes;
}
// tslint:disable-next-line: max-func-body-length
private createLinks(
nodes: SankeyDiagramNode[],
sourceCategories: any[],
destinationCategories: any[],
settings: SankeyDiagramSettings,
selectionIdBuilder: SelectionIdBuilder,
weights: any[],
objects: DataViewObjects[],
sourceFieldName: string,
destinationFieldName: string,
valueFieldName: string,
valueSources: any
): SankeyDiagramLink[] {
let links: SankeyDiagramLink[] = [],
weightValues: number[] = [],
dataPoints: SankeyDiagramDataPoint[] = [],
valuesFormatterForWeigth: IValueFormatter,
formatOfWeigth: string = SankeyDiagram.DefaultFormatOfWeigth;
formatOfWeigth = valueFormatter.getFormatStringByColumn(valueSources);
dataPoints = sourceCategories.map((item: any, index: number) => {
return {
source: item,
destination: destinationCategories[index],
weigth: weights[index]
? weights[index] || SankeyDiagram.DefaultWeightValue
: SankeyDiagram.MinWeightValue
};
});
valuesFormatterForWeigth = valueFormatter.create({
format: formatOfWeigth,
value: Math.max(
settings.labels.unit !== 0 ? settings.labels.unit : d3.max(weightValues) || SankeyDiagram.MinWeightValue,
SankeyDiagram.MinWeightValue),
});
dataPoints.forEach((dataPoint: SankeyDiagramDataPoint, index: number) => {
let sourceNode: SankeyDiagramNode,
destinationNode: SankeyDiagramNode,
link: SankeyDiagramLink,
linkFillColor: string,
linkStrokeColor: string,
selectionId: ISelectionId;
nodes.forEach((node: SankeyDiagramNode) => {
if (node.label.internalName === dataPoint.source) {
sourceNode = node;
}
if (node.label.internalName === dataPoint.destination) {
destinationNode = node;
}
});
linkFillColor = this.getColor(
SankeyDiagram.LinksPropertyIdentifier,
SankeyDiagram.DefaultColourOfLink,
objects[index]);
linkStrokeColor = this.colorHelper.isHighContrast ? this.colorHelper.getHighContrastColor("foreground", linkFillColor) : linkFillColor;
selectionId = selectionIdBuilder.createSelectionId(index);
link = {
source: sourceNode,
destination: destinationNode,
weigth: dataPoint.weigth,
height: dataPoint.weigth,
fillColor: linkFillColor,
strokeColor: linkStrokeColor,
dySource: 0,
dyDestination: 0,
tooltipInfo: SankeyDiagram.getTooltipDataForLink(
valuesFormatterForWeigth,
sourceNode.label.formattedName,
destinationNode.label.formattedName,
dataPoint.weigth,
sourceFieldName,
destinationFieldName,
valueFieldName
),
identity: selectionId,
selected: false,
direction: SankeyLinkDirrections.Forward
};
let selectableDataPoint: SelectableDataPoint = SankeyDiagram.createSelectableDataPoint(selectionId);
sourceNode.selectableDataPoints.push(selectableDataPoint);
destinationNode.selectableDataPoints.push(selectableDataPoint);
links.push(link);
sourceNode.links.push(link);
destinationNode.links.push(link);
SankeyDiagram.updateValueOfNode(sourceNode);
SankeyDiagram.updateValueOfNode(destinationNode);
});
nodes.forEach((nodes: SankeyDiagramNode) => {
nodes.tooltipInfo = SankeyDiagram.getTooltipForNode(
valuesFormatterForWeigth,
nodes.label.formattedName,
nodes.inputWeight
? nodes.inputWeight
: nodes.outputWeight,
this.localizationManager,
nodes.inputWeight > 0 && nodes.outputWeight > 0 ? `${sourceFieldName}-${destinationFieldName}` : nodes.outputWeight > 0
? sourceFieldName
: destinationFieldName,
valueFieldName
);
});
return links;
}
private static createSelectableDataPoint(
selectionId: ISelectionId,
isSelected: boolean = false): SelectableDataPoint {
@ -1048,6 +943,8 @@ export class SankeyDiagram implements IVisual {
private parseSettings(dataView: DataView): SankeyDiagramSettings {
let settings: SankeyDiagramSettings = SankeyDiagramSettings.parse<SankeyDiagramSettings>(dataView);
// settings.valueSourcesQuery = dataView.matrix.valueSources && dataView.matrix.valueSources[0].queryName;
// detect sorting chosen
const foundSortedColumn = dataView.metadata.columns.find(col => col.sort !== undefined);
if (foundSortedColumn) {
@ -1057,7 +954,6 @@ export class SankeyDiagram implements IVisual {
// change settings from high contrast mode
settings.labels.fill = this.colorHelper.getHighContrastColor("foreground", settings.labels.fill);
settings.linkLabels.fill = this.colorHelper.getHighContrastColor("foreground", settings.linkLabels.fill);
// node positions
try {
settings._nodePositions = <SankeyDiagramNodePositionSetting[]>JSON.parse(settings.nodeComplexSettings.nodePositions);
@ -2025,7 +1921,7 @@ export class SankeyDiagram implements IVisual {
})
.style("font-size", this.dataView.settings.linkLabels.fontSize)
.style("fill", this.dataView.settings.linkLabels.fill)
.text((link: SankeyDiagramLink) =>
.text((link: SankeyDiagramLink) => (link.label && (link.label.length > 0)) ? link.label :
`${link.source.label.name || ""}-${link.destination.label.name || ""}:${(link.tooltipInfo[2] || { value: "" }).value}`
);
}
@ -2401,7 +2297,7 @@ export class SankeyDiagram implements IVisual {
this.enumerateLinks(instanceEnumeration);
}
if(options.objectName === SankeyDiagram.NodesPropertyIdentifier.objectName) {
if (options.objectName === SankeyDiagram.NodesPropertyIdentifier.objectName) {
this.enumerateNodeCategories(instanceEnumeration);
}
@ -2425,11 +2321,11 @@ export class SankeyDiagram implements IVisual {
return !node.label.name.endsWith(SankeyDiagram.DuplicatedNamePostfix);
}).forEach((node: SankeyDiagramNode) => {
const identity: ISelectionId = <ISelectionId>node.identity,
displayName: string = node.label.formattedName;
displayName: string = node.label.formattedName;
this.addAnInstanceToEnumeration(instanceEnumeration, {
displayName,
objectName: SankeyDiagram.NodesPropertyIdentifier.objectName,
selector: identity.getSelector(),
selector: ColorHelper.normalizeSelector(identity.getSelector(), false),
properties: {
fill: { solid: { color: node.fillColor } }
}

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

@ -30,13 +30,15 @@ import ISelectionId = powerbi.visuals.ISelectionId;
import DataViewCategoryColumn = powerbi.DataViewCategoryColumn;
import DataViewMatrix = powerbi.DataViewMatrix;
import DataViewMatrixNode = powerbi.DataViewMatrixNode;
import ISelectionIdBuilder = powerbi.visuals.ISelectionIdBuilder;
// powerbi.extensibility.visual
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
export class SelectionIdBuilder {
private visualHost: IVisualHost;
private matrix: DataViewMatrix;
private matrixDataArray: DataViewMatrixNode[] = [];
private builder: ISelectionIdBuilder;
constructor(
IVisualHost: IVisualHost,
@ -44,61 +46,22 @@ export class SelectionIdBuilder {
this.visualHost = IVisualHost;
this.matrix = matrix
this.getMatrixArray(this.matrix.rows.root);
this.matrixDataArray.shift(); // delete matrix root node
}
// walks the matrix deep first, adds each node to matrixDataArray
private getMatrixArray(node: DataViewMatrixNode) {
this.matrixDataArray.push(node);
if (node.children) {
node.children.forEach(child => this.getMatrixArray(child))
}
}
public createSelectionId(index: number): ISelectionId {
let counter: number = 0,
selectionId:ISelectionId;
this.matrix.rows.root.children.forEach((source: DataViewMatrixNode) => {
if (counter == index){
const categoryColumn: DataViewCategoryColumn = {
source: {
displayName: null,
// tslint:disable-next-line: insecure-random
queryName: `${Math.random()}-${+(new Date())}`
},
values: null,
identity: [source.identity]
};
selectionId = this.visualHost.createSelectionIdBuilder()
.withCategory(categoryColumn,0)
.createSelectionId();
}
counter+=1
});
this.matrix.rows.root.children.forEach((source: powerbi.DataViewMatrixNode) =>{
source.children.forEach((destination: powerbi.DataViewMatrixNode) => {
if (counter == index){
const categoryColumn1: DataViewCategoryColumn = {
source: {
displayName: null,
// tslint:disable-next-line: insecure-random
queryName: `${Math.random()}-${+(new Date())}`
},
values: null,
identity: [source.identity]
};
const categoryColumn2: DataViewCategoryColumn = {
source: {
displayName: null,
// tslint:disable-next-line: insecure-random
queryName: `${Math.random()}-${+(new Date())}`
},
values: null,
identity: [destination.identity]
};
selectionId = this.visualHost.createSelectionIdBuilder()
.withCategory(categoryColumn1,0)
.withCategory(categoryColumn2, 0)
.createSelectionId();
}
counter += 1
});
});
return selectionId;
debugger;
return this.visualHost.createSelectionIdBuilder()
.withMatrixNode(this.matrixDataArray[index], this.matrix.rows.levels)
.createSelectionId();
}
}

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

@ -34,6 +34,9 @@ import {
import DataViewObjectsParser = dataViewObjectsParser.DataViewObjectsParser;
import powerbi from "powerbi-visuals-api";
import DataViewMetadataColumn = powerbi.DataViewMetadataColumn;
export enum CyclesDrawType {
Duplicate,
Backward
@ -95,4 +98,5 @@ export class SankeyDiagramSettings extends DataViewObjectsParser {
public cyclesLinks: SankeyNodeCycles = new SankeyNodeCycles();
public _viewportSize: ViewportSize = {};
public sort: string = "";
public valueSourcesQuery: string = "";
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу