-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: single value as a highcharts instance WIP
- Loading branch information
1 parent
59f717e
commit 7cae17e
Showing
14 changed files
with
1,157 additions
and
3 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
{ | ||
"rules": { | ||
"max-params": "off" | ||
"max-params": "off", | ||
// TODO: switch back on before merging | ||
"no-unused-vars": "off" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/visualizations/config/generators/highcharts/renderSingleValueSvg/constants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// TODO: remove this, sch thing it should not be needed | ||
export const svgNS = 'http://www.w3.org/2000/svg' | ||
// multiply text width with this factor | ||
// to get very close to actual text width | ||
// nb: dependent on viewbox etc | ||
export const ACTUAL_TEXT_WIDTH_FACTOR = 0.9 | ||
|
||
// multiply value text size with this factor | ||
// to get very close to the actual number height | ||
// as numbers don't go below the baseline like e.g. "j" and "g" | ||
export const ACTUAL_NUMBER_HEIGHT_FACTOR = 0.67 | ||
|
||
// do not allow text width to exceed this threshold | ||
// a threshold >1 does not really make sense but text width vs viewbox is complicated | ||
export const TEXT_WIDTH_CONTAINER_WIDTH_FACTOR = 1.3 | ||
|
||
// do not allow text size to exceed this | ||
export const TEXT_SIZE_CONTAINER_HEIGHT_FACTOR = 0.6 | ||
export const TEXT_SIZE_MAX_THRESHOLD = 400 | ||
|
||
// multiply text size with this factor | ||
// to get an appropriate letter spacing | ||
export const LETTER_SPACING_TEXT_SIZE_FACTOR = (1 / 35) * -1 | ||
export const LETTER_SPACING_MIN_THRESHOLD = -6 | ||
export const LETTER_SPACING_MAX_THRESHOLD = -1 | ||
|
||
// fixed top margin above title/subtitle | ||
export const TOP_MARGIN_FIXED = 16 | ||
|
||
// multiply text size with this factor | ||
// to get an appropriate sub text size | ||
export const SUB_TEXT_SIZE_FACTOR = 0.5 | ||
export const SUB_TEXT_SIZE_MIN_THRESHOLD = 26 | ||
export const SUB_TEXT_SIZE_MAX_THRESHOLD = 40 | ||
|
||
// multiply text size with this factor | ||
// to get an appropriate icon padding | ||
export const ICON_PADDING_FACTOR = 0.3 |
134 changes: 134 additions & 0 deletions
134
src/visualizations/config/generators/highcharts/renderSingleValueSvg/generateDVItem.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import { | ||
defaultFontStyle, | ||
FONT_STYLE_OPTION_BOLD, | ||
FONT_STYLE_OPTION_FONT_SIZE, | ||
FONT_STYLE_OPTION_ITALIC, | ||
FONT_STYLE_OPTION_TEXT_ALIGN, | ||
FONT_STYLE_OPTION_TEXT_COLOR, | ||
FONT_STYLE_VISUALIZATION_SUBTITLE, | ||
FONT_STYLE_VISUALIZATION_TITLE, | ||
mergeFontStyleWithDefault, | ||
} from '../../../../../modules/fontStyle.js' | ||
import { TOP_MARGIN_FIXED } from './constants.js' | ||
import { generateValueSVG } from './generateValueSVG.js' | ||
import { getTextAnchorFromTextAlign } from './getTextAnchorFromTextAlign.js' | ||
import { getXFromTextAlign } from './getXFromTextAlign.js' | ||
|
||
export const generateDVItem = ( | ||
config, | ||
{ | ||
renderer, | ||
width, | ||
height, | ||
valueColor, | ||
noData, | ||
backgroundColor, | ||
titleColor, | ||
fontStyle, | ||
icon, | ||
} | ||
) => { | ||
backgroundColor = 'red' | ||
if (backgroundColor) { | ||
renderer | ||
.rect(0, 0, width, height) | ||
.attr({ fill: backgroundColor, width: '100%', height: '100%' }) | ||
.add() | ||
} | ||
|
||
// TITLE | ||
const titleFontStyle = mergeFontStyleWithDefault( | ||
fontStyle && fontStyle[FONT_STYLE_VISUALIZATION_TITLE], | ||
FONT_STYLE_VISUALIZATION_TITLE | ||
) | ||
|
||
const titleYPosition = | ||
TOP_MARGIN_FIXED + | ||
parseInt(titleFontStyle[FONT_STYLE_OPTION_FONT_SIZE]) + | ||
'px' | ||
|
||
const titleFontSize = `${titleFontStyle[FONT_STYLE_OPTION_FONT_SIZE]}px` | ||
|
||
renderer | ||
.text(config.title) | ||
.attr({ | ||
x: getXFromTextAlign(titleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN]), | ||
y: titleYPosition, | ||
'text-anchor': getTextAnchorFromTextAlign( | ||
titleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN] | ||
), | ||
'font-size': titleFontSize, | ||
'font-weight': titleFontStyle[FONT_STYLE_OPTION_BOLD] | ||
? FONT_STYLE_OPTION_BOLD | ||
: 'normal', | ||
'font-style': titleFontStyle[FONT_STYLE_OPTION_ITALIC] | ||
? FONT_STYLE_OPTION_ITALIC | ||
: 'normal', | ||
'data-test': 'visualization-title', | ||
fill: | ||
titleColor && | ||
titleFontStyle[FONT_STYLE_OPTION_TEXT_COLOR] === | ||
defaultFontStyle[FONT_STYLE_VISUALIZATION_TITLE][ | ||
FONT_STYLE_OPTION_TEXT_COLOR | ||
] | ||
? titleColor | ||
: titleFontStyle[FONT_STYLE_OPTION_TEXT_COLOR], | ||
}) | ||
.add() | ||
|
||
// SUBTITLE | ||
const subtitleFontStyle = mergeFontStyleWithDefault( | ||
fontStyle && fontStyle[FONT_STYLE_VISUALIZATION_SUBTITLE], | ||
FONT_STYLE_VISUALIZATION_SUBTITLE | ||
) | ||
const subtitleFontSize = `${subtitleFontStyle[FONT_STYLE_OPTION_FONT_SIZE]}px` | ||
|
||
if (config.subtitle) { | ||
renderer | ||
.text(config.subtitle) | ||
.attr({ | ||
x: getXFromTextAlign( | ||
subtitleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN] | ||
), | ||
y: titleYPosition, | ||
dy: `${subtitleFontStyle[FONT_STYLE_OPTION_FONT_SIZE] + 10}`, | ||
'text-anchor': getTextAnchorFromTextAlign( | ||
subtitleFontStyle[FONT_STYLE_OPTION_TEXT_ALIGN] | ||
), | ||
'font-size': subtitleFontSize, | ||
'font-weight': subtitleFontStyle[FONT_STYLE_OPTION_BOLD] | ||
? FONT_STYLE_OPTION_BOLD | ||
: 'normal', | ||
'font-style': subtitleFontStyle[FONT_STYLE_OPTION_ITALIC] | ||
? FONT_STYLE_OPTION_ITALIC | ||
: 'normal', | ||
fill: | ||
titleColor && | ||
subtitleFontStyle[FONT_STYLE_OPTION_TEXT_COLOR] === | ||
defaultFontStyle[FONT_STYLE_VISUALIZATION_SUBTITLE][ | ||
FONT_STYLE_OPTION_TEXT_COLOR | ||
] | ||
? titleColor | ||
: subtitleFontStyle[FONT_STYLE_OPTION_TEXT_COLOR], | ||
'data-test': 'visualization-subtitle', | ||
}) | ||
.add() | ||
} | ||
|
||
generateValueSVG({ | ||
renderer, | ||
formattedValue: config.formattedValue, | ||
subText: config.subText, | ||
valueColor, | ||
textColor: titleColor, | ||
noData, | ||
icon, | ||
containerWidth: width, | ||
containerHeight: height, | ||
topMargin: | ||
TOP_MARGIN_FIXED + | ||
((config.title ? parseInt(titleFontSize) : 0) + | ||
(config.subtitle ? parseInt(subtitleFontSize) : 0)) * | ||
2.5, | ||
}) | ||
} |
135 changes: 135 additions & 0 deletions
135
src/visualizations/config/generators/highcharts/renderSingleValueSvg/generateValueSVG.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { colors } from '@dhis2/ui' | ||
import { | ||
LETTER_SPACING_MAX_THRESHOLD, | ||
LETTER_SPACING_MIN_THRESHOLD, | ||
LETTER_SPACING_TEXT_SIZE_FACTOR, | ||
SUB_TEXT_SIZE_FACTOR, | ||
SUB_TEXT_SIZE_MAX_THRESHOLD, | ||
SUB_TEXT_SIZE_MIN_THRESHOLD, | ||
svgNS, | ||
} from './constants.js' | ||
import { | ||
getIconPadding, | ||
getTextHeightForNumbers, | ||
getTextSize, | ||
getTextWidth, | ||
} from './textSize.js' | ||
|
||
export const generateValueSVG = ({ | ||
renderer, | ||
formattedValue, | ||
subText, | ||
valueColor, | ||
textColor, | ||
icon, | ||
noData, | ||
containerWidth, | ||
containerHeight, | ||
topMargin = 0, | ||
}) => { | ||
console.log('show value') | ||
const showIcon = icon && formattedValue !== noData.text | ||
|
||
const textSize = getTextSize( | ||
formattedValue, | ||
containerWidth, | ||
containerHeight, | ||
showIcon | ||
) | ||
|
||
const textWidth = getTextWidth(formattedValue, `${textSize}px Roboto`) | ||
|
||
const iconSize = textSize | ||
|
||
const subTextSize = | ||
textSize * SUB_TEXT_SIZE_FACTOR > SUB_TEXT_SIZE_MAX_THRESHOLD | ||
? SUB_TEXT_SIZE_MAX_THRESHOLD | ||
: textSize * SUB_TEXT_SIZE_FACTOR < SUB_TEXT_SIZE_MIN_THRESHOLD | ||
? SUB_TEXT_SIZE_MIN_THRESHOLD | ||
: textSize * SUB_TEXT_SIZE_FACTOR | ||
|
||
const svgValue = document.createElementNS(svgNS, 'svg') | ||
svgValue.setAttribute('viewBox', `0 0 ${containerWidth} ${containerHeight}`) | ||
svgValue.setAttribute('width', '50%') | ||
svgValue.setAttribute('height', '50%') | ||
svgValue.setAttribute('x', '50%') | ||
svgValue.setAttribute('y', '50%') | ||
svgValue.setAttribute('style', 'overflow: visible') | ||
|
||
let fillColor = colors.grey900 | ||
|
||
if (valueColor) { | ||
fillColor = valueColor | ||
} else if (formattedValue === noData.text) { | ||
fillColor = colors.grey600 | ||
} | ||
|
||
// show icon if configured in maintenance app | ||
if (showIcon) { | ||
// embed icon to allow changing color | ||
// (elements with fill need to use "currentColor" for this to work) | ||
const iconSvgNode = document.createElementNS(svgNS, 'svg') | ||
iconSvgNode.setAttribute('viewBox', '0 0 48 48') | ||
iconSvgNode.setAttribute('width', iconSize) | ||
iconSvgNode.setAttribute('height', iconSize) | ||
iconSvgNode.setAttribute('y', (iconSize / 2 - topMargin / 2) * -1) | ||
iconSvgNode.setAttribute( | ||
'x', | ||
`-${(iconSize + getIconPadding(textSize) + textWidth) / 2}` | ||
) | ||
iconSvgNode.setAttribute('style', `color: ${fillColor}`) | ||
iconSvgNode.setAttribute('data-test', 'visualization-icon') | ||
|
||
const parser = new DOMParser() | ||
const svgIconDocument = parser.parseFromString(icon, 'image/svg+xml') | ||
|
||
Array.from(svgIconDocument.documentElement.children).forEach((node) => | ||
iconSvgNode.appendChild(node) | ||
) | ||
|
||
svgValue.appendChild(iconSvgNode) | ||
} | ||
|
||
const letterSpacing = Math.round(textSize * LETTER_SPACING_TEXT_SIZE_FACTOR) | ||
|
||
const textNode = document.createElementNS(svgNS, 'text') | ||
textNode.setAttribute('font-size', textSize) | ||
textNode.setAttribute('font-weight', '300') | ||
textNode.setAttribute( | ||
'letter-spacing', | ||
letterSpacing < LETTER_SPACING_MIN_THRESHOLD | ||
? LETTER_SPACING_MIN_THRESHOLD | ||
: letterSpacing > LETTER_SPACING_MAX_THRESHOLD | ||
? LETTER_SPACING_MAX_THRESHOLD | ||
: letterSpacing | ||
) | ||
textNode.setAttribute('text-anchor', 'middle') | ||
textNode.setAttribute( | ||
'x', | ||
showIcon ? `${(iconSize + getIconPadding(textSize)) / 2}` : 0 | ||
) | ||
textNode.setAttribute( | ||
'y', | ||
topMargin / 2 + getTextHeightForNumbers(textSize) / 2 | ||
) | ||
textNode.setAttribute('fill', fillColor) | ||
textNode.setAttribute('data-test', 'visualization-primary-value') | ||
|
||
textNode.appendChild(document.createTextNode(formattedValue)) | ||
|
||
svgValue.appendChild(textNode) | ||
|
||
if (subText) { | ||
const subTextNode = document.createElementNS(svgNS, 'text') | ||
subTextNode.setAttribute('text-anchor', 'middle') | ||
subTextNode.setAttribute('font-size', subTextSize) | ||
subTextNode.setAttribute('y', iconSize / 2 + topMargin / 2) | ||
subTextNode.setAttribute('dy', subTextSize * 1.7) | ||
subTextNode.setAttribute('fill', textColor) | ||
subTextNode.appendChild(document.createTextNode(subText)) | ||
|
||
svgValue.appendChild(subTextNode) | ||
} | ||
|
||
return svgValue | ||
} |
17 changes: 17 additions & 0 deletions
17
...lizations/config/generators/highcharts/renderSingleValueSvg/getTextAnchorFromTextAlign.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { | ||
TEXT_ALIGN_LEFT, | ||
TEXT_ALIGN_CENTER, | ||
TEXT_ALIGN_RIGHT, | ||
} from '../../../../../modules/fontStyle.js' | ||
|
||
export const getTextAnchorFromTextAlign = (textAlign) => { | ||
switch (textAlign) { | ||
default: | ||
case TEXT_ALIGN_LEFT: | ||
return 'start' | ||
case TEXT_ALIGN_CENTER: | ||
return 'middle' | ||
case TEXT_ALIGN_RIGHT: | ||
return 'end' | ||
} | ||
} |
Oops, something went wrong.