Skip to content

Commit

Permalink
chore: positioning WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikThePendric committed Sep 17, 2024
1 parent ffc03df commit 86beba0
Show file tree
Hide file tree
Showing 18 changed files with 368 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { VIS_TYPE_SINGLE_VALUE } from '../../../../../modules/visTypes.js'
import { getSingleValueCustomSVGOptions } from './singleValue/index.js'
import { renderSingleValueSVG } from './singleValue/renderer/renderSingleValueSVG.js'

export function renderCustomSVG() {
const renderer = this.renderer
const options = this.userOptions.customSVGOptions
const { visualizationType } = this.userOptions.customSVGOptions

switch (options.visualizationType) {
switch (visualizationType) {
case VIS_TYPE_SINGLE_VALUE:
console.log('now render SV viz', this)
renderSingleValueSVG.call(this)
break
default:
break
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LEGEND_DISPLAY_STYLE_FILL } from '../../../../../../modules/legends.js'
import { LEGEND_DISPLAY_STYLE_FILL } from '../../../../../../../modules/legends.js'
import { getSingleValueLegendColor } from './getSingleValueLegendColor.js'

export function getSingleValueBackgroundColor(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { renderValue } from '../../../../../../modules/renderValue.js'
import { VALUE_TYPE_TEXT } from '../../../../../../modules/valueTypes.js'
import { renderValue } from '../../../../../../../modules/renderValue.js'
import { VALUE_TYPE_TEXT } from '../../../../../../../modules/valueTypes.js'

export const INDICATOR_FACTOR_100 = 100

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getColorByValueFromLegendSet } from '../../../../../../modules/legends.js'
import { getColorByValueFromLegendSet } from '../../../../../../../modules/legends.js'

export function getSingleValueLegendColor(legendOptions, legendSets, value) {
const legendSet = legendOptions && legendSets[0]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { colors } from '@dhis2/ui'
import { LEGEND_DISPLAY_STYLE_TEXT } from '../../../../../../modules/legends.js'
import { LEGEND_DISPLAY_STYLE_TEXT } from '../../../../../../../modules/legends.js'
import { getSingleValueLegendColor } from './getSingleValueLegendColor.js'
import { shouldUseContrastColor } from './shouldUseContrastColor.js'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { getSingleValueCustomSVGOptions } from './getSingleValueCustomSVGOptions.js'
export { getSingleValueBackgroundColor } from './getSingleValueBackgroundColor.js'
export { getSingleValueTextColor } from './getSingleValueTextColor.js'
export { getSingleValueCustomSVGOptions } from './config/getSingleValueCustomSVGOptions.js'
export { getSingleValueBackgroundColor } from './config/getSingleValueBackgroundColor.js'
export { getSingleValueTextColor } from './config/getSingleValueTextColor.js'
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const parser = new DOMParser()

export function addIconElement(svgString, color) {
const svgIconDocument = parser.parseFromString(svgString, 'image/svg+xml')
const iconElHeight = svgIconDocument.documentElement.getAttribute('height')
const iconElWidth = svgIconDocument.documentElement.getAttribute('width')
const iconGroup = this.renderer
.g('icon')
.attr('data-test', 'visualization-icon')
.css({
color,
})
/* Force the group element to have the same dimensions as the original
* SVG image by adding this rect. This ensures the icon has the intended
* whitespace around it and makes scaling and translating easier. */
this.renderer.rect(0, 0, iconElWidth, iconElHeight).add(iconGroup)

Array.from(svgIconDocument.documentElement.children).forEach((node) =>
iconGroup.element.appendChild(node)
)

iconGroup.add()

return iconGroup
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function checkIfFitsWithinContainer(
availableSpace,
valueElement,
subTextElement,
icon,
subText,
spacing
) {
const valueRect = valueElement.getBBox()
const subTextRect = subTextElement.getBBox()
const requiredValueWidth = icon
? valueRect.width + spacing.iconGap + spacing.iconSize
: valueRect.width
const requiredHeight = subText
? valueRect.height + spacing.subTextTop + subTextRect.height
: valueRect.height
const fitsHorizontally =
availableSpace.width > requiredValueWidth &&
availableSpace.width > subTextRect.width
const fitsVertically = availableSpace.height > requiredHeight

return fitsHorizontally && fitsVertically
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function computeSpacingTop(valueSpacingTop) {
if (this.subtitle.textStr) {
/* If a subtitle is present this will be below the title so base
* the value X position on this */
const subTitleRect = this.subtitle.element.getBBox()
return subTitleRect.y + subTitleRect.height + valueSpacingTop
} else if (this.title.textStr) {
// Otherwise base on title
const titleRect = this.title.element.getBBox()
return titleRect.y + titleRect.height + valueSpacingTop
} else {
// If neither are present only adjust for valueSpacingTop
return this.chartHeight - valueSpacingTop
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { computeSpacingTop } from './computeSpacingTop.js'
import { MIN_SIDE_WHITESPACE } from './styles.js'

export function getAvailableSpace(valueSpacingTop) {
return {
height:
this.chartHeight - computeSpacingTop.call(this, valueSpacingTop),
width: this.chartWidth - MIN_SIDE_WHITESPACE * 2,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { computeSpacingTop } from './computeSpacingTop.js'

export function positionElements(
valueElement,
subTextElement,
iconElement,
spacing
) {
console.log(
'++++positionElements++++',
'\nvalueElement: ',
valueElement,
'\nsubTextElement: ',
subTextElement,
'\niconElement: ',
iconElement,
'\nspacing: ',
spacing,
'\n==============='
)
/* Layout here refers to a virtual rect that wraps
* all indiviual parts of the single value visualization
* (value, subtext and icon) */
const layoutRect = computeLayoutRect.call(
this,
valueElement,
subTextElement,
iconElement,
spacing
)

// DEBUGGING THE RECT
const debugRect = this.renderer
.rect(layoutRect.x, layoutRect.y, layoutRect.width, layoutRect.height)
.attr({ fill: 'orange', opacity: 0.3 })
.add()

const myBBox = debugRect.getBBox()

// const valueBox = valueElement.getBBox()
// const valueTranslateX = iconElement
// ? layoutRect.x + spacing.iconSize + spacing.iconGap
// : layoutRect.x
// valueElement.css({
// transform: `translate(${valueTranslateX}px, ${layoutRect.y}px)`,
// })
// valueElement.attr({
// // TODO: cover the case where subtext is wider than value
// x: iconElement
// ? layoutRect.x + spacing.iconSize + spacing.iconGap
// : layoutRect.x,
// y: layoutRect.y,
// dy: valueBox.height,
// })
const valueElementBox = valueElement.getBBox()
valueElement.align(
{
align: 'right',
verticalAlign: 'top',
alignByTranslate: false,
x: valueElementBox.width * -1,
y: valueElementBox.height * (2 / 3),
},
false,
layoutRect
)

if (iconElement) {
const { height } = iconElement.getBBox()
const scaleFactor = spacing.iconSize / height

// This all needs to be done using CSS translate because of the path cooordinates in the SVG icon
iconElement.css({
transform: `translate(${layoutRect.x}px, ${layoutRect.y}px) scale(${scaleFactor})`,
})
}

if (subTextElement) {
const { height: subTextHeight } = subTextElement.getBBox()
subTextElement.attr({
x: iconElement
? layoutRect.x + spacing.iconSize + spacing.iconGap
: layoutRect.x,
y: layoutRect.y + layoutRect.height - subTextHeight,
})
}

console.log(
'++++positionElements++++',
'\nvalueElement: ',
valueElement,
'\nsubTextElement: ',
subTextElement,
'\niconElement: ',
iconElement,
'\nspacing: ',
spacing,
'\nlayoutRect: ',
layoutRect,
'\n==============='
)
}

function computeLayoutRect(valueElement, subTextElement, iconElement, spacing) {
const valueRect = valueElement.getBBox()
const containerCenterY = this.chartHeight / 2
const containerCenterX = this.chartWidth / 2
const minY = computeSpacingTop.call(this, spacing.valueTop)

let width = valueRect.width
let height = valueRect.height

if (iconElement) {
width += spacing.iconGap + spacing.iconSize
}

if (subTextElement) {
const subTextRect = subTextElement.getBBox()
console.log(
`What is bigger? valueWidth: ${width} subTexttWidth ${subTextRect.width}`
)
width = Math.max(width, subTextRect.width)
height += spacing.subTextTop + subTextRect.height
}

return {
x: containerCenterX - width / 2,
y: Math.max(containerCenterY - height / 2, minY),
width,
height,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { addIconElement } from './addIconElement.js'
import { checkIfFitsWithinContainer } from './checkIfFitsWithinContainer.js'
import { getAvailableSpace } from './getAvailableSpace.js'
import { positionElements } from './positionElements.js'
import { DynamicStyles } from './styles.js'

export function renderSingleValueSVG() {
const color = this.title.styles.color
const { dashboard, formattedValue, icon, subText } =
this.userOptions.customSVGOptions
const dynamicStyles = new DynamicStyles()
const valueElement = this.renderer
.text(formattedValue)
.css({ color, visibility: 'visible' })
.add()
const subTextElement = subText
? this.renderer
.text(subText)
.css({ color, visibility: 'visible' })
.add()
: null
const iconElement = icon ? addIconElement.call(this, icon) : null

let fitsWithinContainer = false
let styles = {}

while (!fitsWithinContainer && dynamicStyles.hasNext()) {
styles = dynamicStyles.next()

valueElement.css(styles.value)
subTextElement?.css(styles.subText)

fitsWithinContainer = checkIfFitsWithinContainer(
getAvailableSpace.call(this, styles.spacing.valueTop),
valueElement,
subTextElement,
icon,
subText,
styles.spacing
)
}

positionElements.call(
this,
valueElement,
subTextElement,
iconElement,
styles.spacing
)

console.log(
'+++++Render the SVG++++++',
'\ncolor: ',
color,
'\ndashboard: ',
dashboard,
'\nformattedValue: ',
formattedValue,
'\nicon: ',
icon,
'\nsubText: ',
subText,
'\n============='
)
console.log('CHART', this)
}
Loading

0 comments on commit 86beba0

Please sign in to comment.