From 9d8698fac848fc49f564fec5f3cf42f1be45d433 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 7 Aug 2024 15:06:43 +0300 Subject: [PATCH] Expand table formatting settings --- capabilities.json | 127 +++++++++++++-- src/D3 Plotting Functions/drawSummaryTable.ts | 149 +++++++++++------- src/defaultSettings.ts | 35 +++- src/validSettingValues.ts | 17 +- 4 files changed, 254 insertions(+), 74 deletions(-) diff --git a/capabilities.json b/capabilities.json index 3164935..d98c3d6 100644 --- a/capabilities.json +++ b/capabilities.json @@ -760,6 +760,63 @@ "displayName": "Show Summary Table", "type" : { "bool" : true } }, + "table_opacity": { + "displayName": "Opacity", + "type": { "numeric": true } + }, + "table_opacity_unselected": { + "displayName": "Opacity if Unselected", + "type": { "numeric": true } + }, + "table_text_overflow": { + "displayName": "Text Overflow Handling", + "type": { + "enumeration" : [ + { "displayName" : "Ellipsis", "value" : "ellipsis" }, + { "displayName" : "Truncate", "value" : "clip" }, + { "displayName" : "None", "value" : "none" } + ] + } + }, + "table_outer_border_colour":{ + "displayName": "Outer Border Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "table_outer_border_width": { + "displayName": "Outer Border Width", + "type": { "numeric": true } + }, + "table_outer_border_style": { + "displayName": "Outer Border Style", + "type": { + "enumeration" : [ + { "displayName" : "Solid", "value" : "solid" }, + { "displayName" : "Dashed", "value" : "dashed" }, + { "displayName" : "Dotted", "value" : "dotted" }, + { "displayName" : "Double", "value" : "double" }, + { "displayName" : "Groove", "value" : "groove" }, + { "displayName" : "Ridge", "value" : "ridge" }, + { "displayName" : "Inset", "value" : "inset" }, + { "displayName" : "Outset", "value" : "outset" } + ] + } + }, + "table_outer_border_top": { + "displayName": "Outer Border Top", + "type": { "bool": true } + }, + "table_outer_border_bottom": { + "displayName": "Outer Border Bottom", + "type": { "bool": true } + }, + "table_outer_border_left": { + "displayName": "Outer Border Left", + "type": { "bool": true } + }, + "table_outer_border_right": { + "displayName": "Outer Border Right", + "type": { "bool": true } + }, "table_header_font": { "displayName": "Header Font", "type": { "formatting": { "fontFamily": true } } @@ -800,6 +857,41 @@ "displayName": "Text Alignment", "type": { "formatting": { "alignment": true } } }, + "table_header_text_padding": { + "displayName": "Padding Around Text", + "type": { "numeric": true } + }, + "table_header_border_colour":{ + "displayName": "Header Border Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "table_header_border_width": { + "displayName": "Header Border Width", + "type": { "numeric": true } + }, + "table_header_border_style": { + "displayName": "Header Border Style", + "type": { + "enumeration" : [ + { "displayName" : "Solid", "value" : "solid" }, + { "displayName" : "Dashed", "value" : "dashed" }, + { "displayName" : "Dotted", "value" : "dotted" }, + { "displayName" : "Double", "value" : "double" }, + { "displayName" : "Groove", "value" : "groove" }, + { "displayName" : "Ridge", "value" : "ridge" }, + { "displayName" : "Inset", "value" : "inset" }, + { "displayName" : "Outset", "value" : "outset" } + ] + } + }, + "table_header_border_bottom": { + "displayName": "Bottom Border", + "type": { "bool": true } + }, + "table_header_border_inner": { + "displayName": "Inner Borders", + "type": { "bool": true } + }, "table_body_font": { "displayName": "Body Font", "type": { "formatting": { "fontFamily": true } } @@ -840,23 +932,40 @@ "displayName": "Text Alignment", "type": { "formatting": { "alignment": true } } }, - "table_opacity": { - "displayName": "Opacity", + "table_body_text_padding": { + "displayName": "Padding Around Text", "type": { "numeric": true } }, - "table_opacity_unselected": { - "displayName": "Opacity if Unselected", + "table_body_border_colour":{ + "displayName": "Body Border Colour", + "type": { "fill": { "solid": { "color": true } } } + }, + "table_body_border_width": { + "displayName": "Body Border Width", "type": { "numeric": true } }, - "table_text_overflow": { - "displayName": "Text Overflow Handling", + "table_body_border_style": { + "displayName": "Body Border Style", "type": { "enumeration" : [ - { "displayName" : "Ellipsis", "value" : "ellipsis" }, - { "displayName" : "Truncate", "value" : "clip" }, - { "displayName" : "None", "value" : "none" } + { "displayName" : "Solid", "value" : "solid" }, + { "displayName" : "Dashed", "value" : "dashed" }, + { "displayName" : "Dotted", "value" : "dotted" }, + { "displayName" : "Double", "value" : "double" }, + { "displayName" : "Groove", "value" : "groove" }, + { "displayName" : "Ridge", "value" : "ridge" }, + { "displayName" : "Inset", "value" : "inset" }, + { "displayName" : "Outset", "value" : "outset" } ] } + }, + "table_body_border_top_bottom": { + "displayName": "Top/Bottom Borders", + "type": { "bool": true } + }, + "table_body_border_left_right": { + "displayName": "Left/Right Borders", + "type": { "bool": true } } } }, diff --git a/src/D3 Plotting Functions/drawSummaryTable.ts b/src/D3 Plotting Functions/drawSummaryTable.ts index e756211..baa0935 100644 --- a/src/D3 Plotting Functions/drawSummaryTable.ts +++ b/src/D3 Plotting Functions/drawSummaryTable.ts @@ -3,8 +3,11 @@ import type { divBaseType, Visual } from "../visual"; import initialiseIconSVG from "./initialiseIconSVG"; import * as nhsIcons from "./NHS Icons" import * as d3 from "./D3 Modules"; +import type { defaultSettingsType } from "../Classes"; +import { isNullOrUndefined } from "../Functions"; -// ESLint errors due to number of lines in function, but would reduce readability to separate further +// ESLint errors due to number of lines in function +// Will refactor in future /* eslint-disable */ export default function drawSummaryTable(selection: divBaseType, visualObj: Visual) { selection.style("height", "100%").style("width", "100%"); @@ -36,16 +39,54 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu const maxWidth: number = visualObj.viewModel.svgWidth / cols.length; const tableSettings = visualObj.viewModel.inputSettings.settings.summary_table; + selection.select(".table-group") + .style("border-width", `${tableSettings.table_outer_border_width}px`) + .style("border-style", tableSettings.table_outer_border_style) + .style("border-color", tableSettings.table_outer_border_colour); + + ["top", "right", "bottom", "left"].forEach((side) => { + if (!tableSettings[`table_outer_border_${side}`]) { + selection.select(".table-group").style(`border-${side}`, "none"); + } + }); + const tableHeaders = selection.select(".table-header") .selectAll("th") .data(cols) - .join("th") - .style("border", "1px black solid") - .style("padding", "1px") + .join("th"); + + tableHeaders.selectAll("text") + .data(d => [d.label]) + .join("text") + .text(d => d) + .style("font-size", `${tableSettings.table_header_size}px`) + .style("font-family", tableSettings.table_header_font) + .style("color", tableSettings.table_header_colour) + + tableHeaders.style("padding", `${tableSettings.table_header_text_padding}px`) .style("background-color", tableSettings.table_header_bg_colour) .style("font-weight", tableSettings.table_header_font_weight) .style("text-transform", tableSettings.table_header_text_transform) .style("text-align", tableSettings.table_header_text_align) + .style("border-width", `${tableSettings.table_header_border_width}px`) + .style("border-style", tableSettings.table_header_border_style) + .style("border-color", tableSettings.table_header_border_colour) + // Top border of header controlled by outer border + .style("border-top", "inherit"); + + selection.selectAll("first-child") + .style("border-left", "inherit"); + selection.selectAll("th:last-child") + .style("border-right", "inherit"); + + if (!tableSettings.table_header_border_bottom) { + tableHeaders.style("border-bottom", "none"); + } + + if (!tableSettings.table_header_border_inner) { + tableHeaders.style("border-left", "none") + .style("border-right", "none"); + } if (tableSettings.table_text_overflow !== "none") { tableHeaders.style("overflow", "hidden") @@ -56,14 +97,6 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu .style("max-width", "none") } - tableHeaders.selectAll("text") - .data(d => [d.label]) - .join("text") - .text(d => d) - .style("font-size", `${tableSettings.table_header_size}px`) - .style("font-family", tableSettings.table_header_font) - .style("color", tableSettings.table_header_colour) - const tableRows = selection.select(".table-body") .selectAll('tr') .data(plotPoints) @@ -77,34 +110,6 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu }); event.stopPropagation(); } - }) - .style("background-color", (d) => { - const groupBGColour: string = d.aesthetics?.["table_body_bg_colour"] ?? tableSettings.table_body_bg_colour; - return groupBGColour; - }) - .style("font-weight", (d) => { - const groupWeight: string = d.aesthetics?.["table_body_font_weight"] ?? tableSettings.table_body_font_weight; - return groupWeight; - }) - .style("text-transform", (d) => { - const groupTransform: string = d.aesthetics?.["table_body_text_transform"] ?? tableSettings.table_body_text_transform; - return groupTransform; - }) - .style("text-align", (d) => { - const groupAlign: string = d.aesthetics?.["table_body_text_align"] ?? tableSettings.table_body_text_align; - return groupAlign; - }) - .style("font-size", (d) => { - const groupSize: number = d.aesthetics?.["table_body_size"] ?? tableSettings.table_body_size; - return `${groupSize}px`; - }) - .style("font-family", (d) => { - const groupFont: string = d.aesthetics?.["table_body_font"] ?? tableSettings.table_body_font; - return groupFont; - }) - .style("color", (d) => { - const groupColour: string = d.aesthetics?.["table_body_colour"] ?? tableSettings.table_body_colour; - return groupColour; }); if (tableSettings.table_text_overflow !== "none") { @@ -122,19 +127,6 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu })) .join('td'); - tableSelect.on("mouseover", (event) => { - d3.select(event.target).select(function(){ - return this.closest("td"); - }).style("background-color", "lightgray"); - }) - .on("mouseout", (event) => { - d3.select(event.target).select(function(){ - return this.closest("td"); - }).style("background-color", "inherit"); - }) - .style("border", "1px black solid") - .style("padding", "1px") - const nhsIconSettings = visualObj.viewModel.inputSettings.settings.nhs_icons; const draw_icons: boolean = nhsIconSettings.show_variation_icons || nhsIconSettings.show_assurance_icons; const showGrouped: boolean = visualObj.viewModel.showGrouped; @@ -144,9 +136,12 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu const icon_x: number = (thisSelDims.width * 0.8) / 0.08 / 2 - 189; const icon_y: number = (thisSelDims.height * 0.8) / 0.08 / 2 - 189; - tableSelect.each(function(d) { + tableSelect.each(function(d, idx) { + const currNode = d3.select(this); + const parentNode = d3.select(currNode.property("parentNode")); + const rowData = parentNode.datum() as plotData; if (showGrouped && draw_icons && (d.column === "variation" || d.column === "assurance")) { - d3.select(this) + currNode .append("svg") .attr("width", thisSelDims.width * 0.8) .attr("height", thisSelDims.height * 0.8) @@ -162,9 +157,49 @@ export default function drawSummaryTable(selection: divBaseType, visualObj: Visu ? d.value.toFixed(visualObj.viewModel.inputSettings.settings.spc.sig_figs) : d.value; - d3.select(this).text(value).classed("cell-text", true); + currNode.text(value).classed("cell-text", true); + } + const tableAesthetics: defaultSettingsType["summary_table"] + = rowData.aesthetics?.["table_body_bg_colour"] + ? rowData.aesthetics as any + : tableSettings; + + currNode.style("background-color", tableAesthetics.table_body_bg_colour) + .style("font-weight", tableAesthetics.table_body_font_weight) + .style("text-transform", tableAesthetics.table_body_text_transform) + .style("text-align", tableAesthetics.table_body_text_align) + .style("font-size", `${tableAesthetics.table_body_size}px`) + .style("font-family", tableAesthetics.table_body_font) + .style("color", tableAesthetics.table_body_colour) + .style("border-width", `${tableAesthetics.table_body_border_width}px`) + .style("border-style", tableAesthetics.table_body_border_style) + .style("border-color", tableAesthetics.table_body_border_colour) + .style("padding", `${tableAesthetics.table_body_text_padding}px`); + + if (idx === 0) { + currNode.style("border-left", "inherit"); + } else if (idx === cols.length - 1) { + currNode.style("border-right", "inherit"); + } + + if (isNullOrUndefined(parentNode.property("nextElementSibling"))) { + currNode.style("border-bottom", "inherit"); } - }) + if (isNullOrUndefined(parentNode.property("previousElementSibling"))) { + currNode.style("border-top", "inherit"); + } + + currNode.on("mouseover", (event) => { + d3.select(event.target).select(function(){ + return this.closest("td"); + }).style("background-color", "lightgray"); + }) + .on("mouseout", (event) => { + d3.select(event.target).select(function(){ + return this.closest("td"); + }).style("background-color", "inherit"); + }); + }); selection.on('click', () => { visualObj.selectionManager.clear(); diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index 46c05e8..ad944df 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -1,4 +1,4 @@ -import { textOptions, lineOptions, iconOptions, colourOptions } from "./validSettingValues"; +import { textOptions, lineOptions, iconOptions, colourOptions, borderOptions } from "./validSettingValues"; const defaultSettings = { canvas: { @@ -156,23 +156,42 @@ const defaultSettings = { }, summary_table: { show_table: { default: false }, + table_text_overflow: textOptions.text_overflow, + table_opacity: { default: 1, valid: { numberRange: { min: 0, max: 1 } } }, + table_opacity_unselected: { default: 0.2, valid: { numberRange: { min: 0, max: 1 } } }, + table_outer_border_style: borderOptions.style, + table_outer_border_width: borderOptions.width, + table_outer_border_colour: borderOptions.colour, + table_outer_border_top: { default: true }, + table_outer_border_bottom: { default: true }, + table_outer_border_left: { default: true }, + table_outer_border_right: { default: true }, table_header_font: textOptions.font, table_header_font_weight: textOptions.weight, table_header_text_transform: textOptions.text_transform, table_header_text_align: textOptions.text_align, + table_header_text_padding: {default: 1, valid: { numberRange: { min: 0, max: 100 }}}, table_header_size: textOptions.size, table_header_colour: colourOptions.standard, table_header_bg_colour: { default: "#D3D3D3" }, + table_header_border_style: borderOptions.style, + table_header_border_width: borderOptions.width, + table_header_border_colour: borderOptions.colour, + table_header_border_bottom: { default: true }, + table_header_border_inner: { default: true }, table_body_font: textOptions.font, table_body_font_weight: textOptions.weight, table_body_text_transform: textOptions.text_transform, table_body_text_align: textOptions.text_align, + table_body_text_padding: {default: 1, valid: { numberRange: { min: 0, max: 100 }}}, table_body_size: textOptions.size, table_body_colour: colourOptions.standard, table_body_bg_colour: { default: "#FFFFFF" }, - table_text_overflow: textOptions.text_overflow, - table_opacity: { default: 1, valid: { numberRange: { min: 0, max: 1 } } }, - table_opacity_unselected: { default: 0.2, valid: { numberRange: { min: 0, max: 1 } } } + table_body_border_style: borderOptions.style, + table_body_border_width: borderOptions.width, + table_body_border_colour: borderOptions.colour, + table_body_border_top_bottom: { default: true }, + table_body_border_left_right: { default: true } }, download_options: { show_button: { default: false } @@ -216,9 +235,11 @@ export const settingsPaneGroupings = { "Label": ["ylimit_label", "ylimit_label_font", "ylimit_label_size", "ylimit_label_colour"] }, summary_table: { - "General": ["show_table", "table_text_overflow", "table_opacity", "table_opacity_unselected"], - "Header": ["table_header_font", "table_header_size", "table_header_text_align", "table_header_font_weight", "table_header_text_transform", "table_header_colour", "table_header_bg_colour"], - "Body": ["table_body_font", "table_body_size", "table_body_text_align", "table_body_font_weight", "table_body_text_transform", "table_body_colour", "table_body_bg_colour"] + "General": ["show_table", "table_text_overflow", "table_opacity", "table_opacity_unselected", "table_outer_border_style", "table_outer_border_width", "table_outer_border_colour", "table_outer_border_top", "table_outer_border_bottom", "table_outer_border_left", "table_outer_border_right"], + + "Header": ["table_header_font", "table_header_size", "table_header_text_align",, "table_header_font_weight", "table_header_text_transform", "table_header_text_padding", "table_header_colour", "table_header_bg_colour", "table_header_border_style", "table_header_border_width", "table_header_border_colour", "table_header_border_bottom", "table_header_border_inner"], + + "Body": ["table_body_font", "table_body_size", "table_body_text_align", "table_body_font_weight", "table_body_text_transform", "table_body_text_padding", "table_body_colour", "table_body_bg_colour", "table_body_border_style", "table_body_border_width", "table_body_border_colour", "table_body_border_top_bottom", "table_body_border_left_right"] } } diff --git a/src/validSettingValues.ts b/src/validSettingValues.ts index 9f98894..2c9e284 100644 --- a/src/validSettingValues.ts +++ b/src/validSettingValues.ts @@ -82,9 +82,24 @@ const colourOptions = { standard: { default: "#000000" } } +const borderOptions = { + width: { + default: 1, + valid: { numberRange: { min: 0 } } + }, + style: { + default: "solid", + valid: ["solid", "dotted", "dashed", "double", "groove", "ridge", "inset", "outset", "none"] + }, + colour: { + default: "#000000" + } +} + export { textOptions, lineOptions, iconOptions, - colourOptions + colourOptions, + borderOptions }