diff --git a/capabilities.json b/capabilities.json index 3c51a96..8b07b4c 100644 --- a/capabilities.json +++ b/capabilities.json @@ -679,6 +679,280 @@ "multiplier_specification": { "displayName": "Apply Multiplier to Specification Limits", "type" : { "bool" : true } + }, + "plot_label_show_99": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_95": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_68": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_main": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_target": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_alt_target": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_show_specification": { + "displayName": "Show Value on Plot", + "type": { "bool": true } + }, + "plot_label_position_99": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Outside", "value" : "outside" }, + { "displayName" : "Inside", "value" : "inside" }, + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_95": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Outside", "value" : "outside" }, + { "displayName" : "Inside", "value" : "inside" }, + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_68": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Outside", "value" : "outside" }, + { "displayName" : "Inside", "value" : "inside" }, + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_main": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_target": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_alt_target": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_position_specification": { + "displayName": "Position of Value on Line(s)", + "type": { + "enumeration" : [ + { "displayName" : "Outside", "value" : "outside" }, + { "displayName" : "Inside", "value" : "inside" }, + { "displayName" : "Above", "value" : "above" }, + { "displayName" : "Below", "value" : "below" }, + { "displayName" : "Beside", "value" : "beside" } + ] + } + }, + "plot_label_vpad_99": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_95": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_68": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_main": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_target": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_alt_target": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_vpad_specification": { + "displayName": "Value Vertical Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_99": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_95": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_68": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_main": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_target": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_alt_target": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_hpad_specification": { + "displayName": "Value Horizontal Padding", + "type": { "numeric": true } + }, + "plot_label_font_99": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_95": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_68": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_main": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_target": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_alt_target": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_font_specification": { + "displayName": "Value Font", + "type": { "formatting": { "fontFamily": true } } + }, + "plot_label_size_99": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_95": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_68": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_main": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_target": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_alt_target": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_size_specification": { + "displayName": "Value Font Size", + "type": { "formatting": { "fontSize": true } } + }, + "plot_label_colour_99":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_95":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_68":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_main":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_target":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_alt_target":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_colour_specification":{ + "displayName": "Value Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "plot_label_prefix_99": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_95": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_68": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_main": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_target": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_alt_target": { + "displayName": "Value Prefix", + "type": { "text": true } + }, + "plot_label_prefix_specification": { + "displayName": "Value Prefix", + "type": { "text": true } } } }, diff --git a/pbiviz.json b/pbiviz.json index 2b2c31c..7cc6167 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -4,7 +4,7 @@ "displayName":"SPC Charts", "guid":"PBISPC", "visualClassName":"Visual", - "version":"1.4.4.17", + "version":"1.4.4.18", "description":"A PowerBI custom visual for SPC charts", "supportUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-SPC", "gitHubUrl":"https://github.com/AUS-DOH-Safety-and-Quality/PowerBI-SPC" diff --git a/src/D3 Plotting Functions/drawLineLabels.ts b/src/D3 Plotting Functions/drawLineLabels.ts new file mode 100644 index 0000000..8171fe0 --- /dev/null +++ b/src/D3 Plotting Functions/drawLineLabels.ts @@ -0,0 +1,71 @@ +import type { svgBaseType, Visual } from "../visual"; +import { lineNameMap } from "../Functions/getAesthetic"; +import { valueFormatter } from "../Functions"; +import * as d3 from "./D3 Modules"; + +const positionOffsetMap: Record = { + "above": -1, + "below": 1, + "beside": -1 +} + +const outsideMap: Record = { + "ll99" : "below", + "ll95" : "below", + "ll68" : "below", + "ul68" : "above", + "ul95" : "above", + "ul99" : "above", + "speclimits_lower" : "below", + "speclimits_upper" : "above" +} + +const insideMap: Record = { + "ll99" : "above", + "ll95" : "above", + "ll68" : "above", + "ul68" : "below", + "ul95" : "below", + "ul99" : "below", + "speclimits_lower" : "above", + "speclimits_upper" : "below" +} + +export default function drawLineLabels(selection: svgBaseType, visualObj: Visual) { + const lineSettings = visualObj.viewModel.inputSettings.settings.lines; + const formatValue = valueFormatter(visualObj.viewModel.inputSettings.settings, visualObj.viewModel.inputSettings.derivedSettings); + selection + .select(".linesgroup") + .selectAll("text") + .data(visualObj.viewModel.groupedLines) + .join("text") + .text(d => { + return lineSettings[`plot_label_show_${lineNameMap[d[0]]}`] + ? lineSettings[`plot_label_prefix_${lineNameMap[d[0]]}`] + formatValue(d[1][d[1].length - 1].line_value, "value") + : ""; + }) + .attr("x", d => visualObj.viewModel.plotProperties.xScale(d[1][d[1].length - 1].x)) + .attr("y", d => visualObj.viewModel.plotProperties.yScale(d[1][d[1].length - 1].line_value)) + .attr("fill", d => lineSettings[`plot_label_colour_${lineNameMap[d[0]]}`]) + .attr("font-size", d => `${lineSettings[`plot_label_size_${lineNameMap[d[0]]}`]}px`) + .attr("font-family", d => lineSettings[`plot_label_font_${lineNameMap[d[0]]}`]) + .attr("text-anchor", d => lineSettings[`plot_label_position_${lineNameMap[d[0]]}`] === "beside" ? "start" : "end") + .attr("dx", d => { + const offset = (lineSettings[`plot_label_position_${lineNameMap[d[0]]}`] === "beside" ? 1 : -1) * lineSettings[`plot_label_hpad_${lineNameMap[d[0]]}`]; + return `${offset}px`; + }) + .attr("dy", function(d) { + const bounds = (d3.select(this).node() as SVGGraphicsElement).getBoundingClientRect() as DOMRect; + let position: string = lineSettings[`plot_label_position_${lineNameMap[d[0]]}`]; + let vpadding: number = lineSettings[`plot_label_vpad_${lineNameMap[d[0]]}`]; + if (["outside", "inside"].includes(position)) { + position = position === "outside" ? outsideMap[d[0]] : insideMap[d[0]]; + } + const heightMap: Record = { + "above": -lineSettings[`width_${lineNameMap[d[0]]}`], + "below": lineSettings[`plot_label_size_${lineNameMap[d[0]]}`], + "beside": bounds.height / 4 + } + return `${positionOffsetMap[position] * vpadding + heightMap[position]}px`; + }); +} diff --git a/src/D3 Plotting Functions/drawLabels.ts b/src/D3 Plotting Functions/drawValueLabels.ts similarity index 100% rename from src/D3 Plotting Functions/drawLabels.ts rename to src/D3 Plotting Functions/drawValueLabels.ts diff --git a/src/D3 Plotting Functions/drawXAxis.ts b/src/D3 Plotting Functions/drawXAxis.ts index 600e210..3c03833 100644 --- a/src/D3 Plotting Functions/drawXAxis.ts +++ b/src/D3 Plotting Functions/drawXAxis.ts @@ -1,9 +1,8 @@ import * as d3 from "./D3 Modules"; -import { abs } from "../Functions"; import type { axisProperties } from "../Classes"; import type { svgBaseType, Visual } from "../visual"; -export default function drawXAxis(selection: svgBaseType, visualObj: Visual, refresh?: boolean) { +export default function drawXAxis(selection: svgBaseType, visualObj: Visual) { const xAxisProperties: axisProperties = visualObj.viewModel.plotProperties.xAxis; const xAxis: d3.Axis = d3.axisBottom(visualObj.viewModel.plotProperties.xScale); @@ -51,27 +50,6 @@ export default function drawXAxis(selection: svgBaseType, visualObj: Visual, ref return; } const xAxisCoordinates: DOMRect = xAxisNode.getBoundingClientRect() as DOMRect; - - // Update padding and re-draw axis if large tick values rendered outside of plot - const tickBelowPadding: number = xAxisCoordinates.bottom - xAxisHeight; - const tickLeftofPadding: number = xAxisCoordinates.left - xAxisProperties.start_padding; - - if ((tickBelowPadding > 0 || tickLeftofPadding < 0)) { - if (!refresh) { - if (tickBelowPadding > 0) { - visualObj.viewModel.plotProperties.yAxis.start_padding += abs(tickBelowPadding); - } - if (tickLeftofPadding < 0) { - visualObj.viewModel.plotProperties.xAxis.start_padding += abs(tickLeftofPadding) - } - visualObj.viewModel.plotProperties.initialiseScale(visualObj.viewModel.svgWidth, - visualObj.viewModel.svgHeight - ); - selection.call(drawXAxis, visualObj, true); - return; - } - } - const bottomMidpoint: number = plotHeight - ((plotHeight - xAxisCoordinates.bottom) / 2); selection.select(".xaxislabel") diff --git a/src/D3 Plotting Functions/drawYAxis.ts b/src/D3 Plotting Functions/drawYAxis.ts index c30af52..369a66b 100644 --- a/src/D3 Plotting Functions/drawYAxis.ts +++ b/src/D3 Plotting Functions/drawYAxis.ts @@ -1,10 +1,9 @@ import * as d3 from "./D3 Modules"; -import { abs, isNullOrUndefined } from "../Functions"; -import drawXAxis from "./drawXAxis"; +import { isNullOrUndefined } from "../Functions"; import type { axisProperties } from "../Classes"; import type { svgBaseType, Visual } from "../visual"; -export default function drawYAxis(selection: svgBaseType, visualObj: Visual, refresh?: boolean) { +export default function drawYAxis(selection: svgBaseType, visualObj: Visual) { const yAxisProperties: axisProperties = visualObj.viewModel.plotProperties.yAxis; const yAxis: d3.Axis = d3.axisLeft(visualObj.viewModel.plotProperties.yScale); const yaxis_sig_figs: number = visualObj.viewModel.inputSettings.settings.y_axis.ylimit_sig_figs; @@ -50,19 +49,6 @@ export default function drawYAxis(selection: svgBaseType, visualObj: Visual, ref return; } const yAxisCoordinates: DOMRect = yAxisNode.getBoundingClientRect() as DOMRect; - - const settingsPadding: number = visualObj.viewModel.inputSettings.settings.canvas.left_padding - const tickLeftofPadding: number = yAxisCoordinates.left - settingsPadding; - if (tickLeftofPadding < 0) { - if (!refresh) { - visualObj.viewModel.plotProperties.xAxis.start_padding += abs(tickLeftofPadding) - visualObj.viewModel.plotProperties.initialiseScale(visualObj.viewModel.svgWidth, - visualObj.viewModel.svgHeight); - selection.call(drawYAxis, visualObj, true).call(drawXAxis, visualObj, true); - return; - } - } - const leftMidpoint: number = yAxisCoordinates.x * 0.7; const y: number = visualObj.viewModel.svgHeight / 2; diff --git a/src/D3 Plotting Functions/index.ts b/src/D3 Plotting Functions/index.ts index 9e8dcb3..de9bd1a 100644 --- a/src/D3 Plotting Functions/index.ts +++ b/src/D3 Plotting Functions/index.ts @@ -9,4 +9,5 @@ export { default as drawErrors } from "./drawErrors" export { default as initialiseSVG } from "./initialiseSVG" export { default as drawSummaryTable } from "./drawSummaryTable" export { default as drawDownloadButton } from "./drawDownloadButton" -export { default as drawLabels } from "./drawLabels" +export { default as drawValueLabels } from "./drawValueLabels" +export { default as drawLineLabels } from "./drawLineLabels" diff --git a/src/Functions/getAesthetic.ts b/src/Functions/getAesthetic.ts index 0cd1102..13a7980 100644 --- a/src/Functions/getAesthetic.ts +++ b/src/Functions/getAesthetic.ts @@ -19,3 +19,5 @@ export default function getAesthetic(type: string, group: string, aesthetic: str const settingName: string = aesthetic + "_" + mapName; return inputSettings[group][settingName]; } + +export { lineNameMap } diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index 94bd8cd..5f936c9 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -1,4 +1,4 @@ -import { textOptions, lineOptions, iconOptions, colourOptions, borderOptions } from "./validSettingValues"; +import { textOptions, lineOptions, iconOptions, colourOptions, borderOptions, labelOptions } from "./validSettingValues"; const defaultSettings = { canvas: { @@ -123,7 +123,63 @@ const defaultSettings = { specification_upper: { default: null }, specification_lower: { default: null }, multiplier_alt_target: { default: false }, - multiplier_specification: { default: false } + multiplier_specification: { default: false }, + plot_label_show_99: { default: false }, + plot_label_show_95: { default: false }, + plot_label_show_68: { default: false }, + plot_label_show_main: { default: false }, + plot_label_show_target: { default: false }, + plot_label_show_alt_target: { default: false }, + plot_label_show_specification: { default: false }, + plot_label_position_99: labelOptions.limits, + plot_label_position_95: labelOptions.limits, + plot_label_position_68: labelOptions.limits, + plot_label_position_main: labelOptions.standard, + plot_label_position_target: labelOptions.standard, + plot_label_position_alt_target: labelOptions.standard, + plot_label_position_specification: labelOptions.limits, + plot_label_vpad_99: { default: 0 }, + plot_label_vpad_95: { default: 0 }, + plot_label_vpad_68: { default: 0 }, + plot_label_vpad_main: { default: 0 }, + plot_label_vpad_target: { default: 0 }, + plot_label_vpad_alt_target: { default: 0 }, + plot_label_vpad_specification: { default: 0 }, + plot_label_hpad_99: { default: 10 }, + plot_label_hpad_95: { default: 10 }, + plot_label_hpad_68: { default: 10 }, + plot_label_hpad_main: { default: 10 }, + plot_label_hpad_target: { default: 10 }, + plot_label_hpad_alt_target: { default: 10 }, + plot_label_hpad_specification: { default: 10 }, + plot_label_font_99: textOptions.font, + plot_label_font_95: textOptions.font, + plot_label_font_68: textOptions.font, + plot_label_font_main: textOptions.font, + plot_label_font_target: textOptions.font, + plot_label_font_alt_target: textOptions.font, + plot_label_font_specification: textOptions.font, + plot_label_size_99: textOptions.size, + plot_label_size_95: textOptions.size, + plot_label_size_68: textOptions.size, + plot_label_size_main: textOptions.size, + plot_label_size_target: textOptions.size, + plot_label_size_alt_target: textOptions.size, + plot_label_size_specification: textOptions.size, + plot_label_colour_99: colourOptions.standard, + plot_label_colour_95: colourOptions.standard, + plot_label_colour_68: colourOptions.standard, + plot_label_colour_main: colourOptions.standard, + plot_label_colour_target: colourOptions.standard, + plot_label_colour_alt_target: colourOptions.standard, + plot_label_colour_specification: colourOptions.standard, + plot_label_prefix_99: { default: "" }, + plot_label_prefix_95: { default: "" }, + plot_label_prefix_68: { default: "" }, + plot_label_prefix_main: { default: "" }, + plot_label_prefix_target: { default: "" }, + plot_label_prefix_alt_target: { default: "" }, + plot_label_prefix_specification: { default: "" } }, x_axis: { xlimit_colour: colourOptions.standard, @@ -248,13 +304,13 @@ export const settingsPaneGroupings: Partial