Skip to content

Commit

Permalink
Interactive SVG background: Allow processing whole SVG group for state (
Browse files Browse the repository at this point in the history
#2872)

Allows to flash and handle state on whole SVG groups without a proxy by
using the group's path elements instead, e.g. this whole group can be used directly

---------

Signed-off-by: Stefan Höhn <[email protected]>
  • Loading branch information
stefan-hoehn authored Nov 16, 2024
1 parent e3daf94 commit e199066
Showing 1 changed file with 115 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ export default {
* @param {HTMLElement} el
*/
svgOnMouseOver (el) {
function flashElement (el, fillColor) {
if (el && !el.flashing) {
const attributeName = (el.style.fill !== 'none') ? 'fill' : 'stroke'
const oldFill = el.style.getPropertyValue(attributeName)
const oldOpacity = el.style.opacity
el.style.setProperty(attributeName, fillColor)
el.style.opacity = 1
el.flashing = true
setTimeout(() => {
el.style.setProperty(attributeName, oldFill)
el.style.opacity = oldOpacity
el.flashing = false
}, 200)
}
}

if (this.context.editmode || (!this.context.editmode && this.config.embedSvgFlashing)) {
const tagName = el.tagName
// fill green if item config is available, red if config is still missing
Expand All @@ -160,19 +176,15 @@ export default {
el.style.setProperty(attributeName, oldFill)
}, 200)
} else { // groups cannot be filled, so we need to fill special element marked as "flash"
const flashElement = el.querySelector('[flash]')
if (flashElement && !flashElement.flashing) {
const attributeName = (flashElement.style.fill !== 'none') ? 'fill' : 'stroke'
const oldFill = flashElement.style.getPropertyValue(attributeName)
const oldOpacity = flashElement.style.opacity
flashElement.style.setProperty(attributeName, fillColor)
flashElement.style.opacity = 1
flashElement.flashing = true
setTimeout(() => {
flashElement.style.setProperty(attributeName, oldFill)
flashElement.style.opacity = oldOpacity
flashElement.flashing = false
}, 200)
const elementToFlash = el.querySelector('[flash]')
if (elementToFlash) {
flashElement(elementToFlash, fillColor)
} else {
// let's try flashing all path elements in the group
const flashElements = el.querySelectorAll('path')
for (const path of flashElements) {
flashElement(path, fillColor)
}
}
}
}
Expand Down Expand Up @@ -292,100 +304,107 @@ export default {
svgElement.innerHTML = state
}

switch (stateType) {
// currently no distinction is made regarding different state-types, only the following are supported (yet)
case 'OpenClosed':
case 'Percent':
case 'HSB':
case 'OnOff':
const useProxy = tagName === 'g' && svgElementConfig.useProxyElementForState // if proxy should be used and element is of type group
const element = (useProxy) ? svgElement.querySelector('[flash]') : svgElement
if (!element) {
console.warn(`Element ${svgElement} is a group element but has no containing element with the attribute "flash"`)
return
function processState (useProxy, element) {
if (state === 'ON' || stateType === 'HSB') {
if (useProxy && svgElementConfig.stateAsOpacity) { // we use the flash element
let opacity = (state === 'ON') ? 1 : 0
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
element.style.opacity = opacity
} else {
element.oldFill = element.style.fill
element.style.fill = stateOnColorRgbStyle
}
if (state === 'ON' || stateType === 'HSB') {
if (useProxy && svgElementConfig.stateAsOpacity) { // we use the flash element
let opacity = (state === 'ON') ? 1 : 0
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
// TODO: use fill-opacity if fill not available
element.style.opacity = opacity
} else {
element.oldFill = element.style.fill
element.style.fill = stateOnColorRgbStyle
}
if (svgElementConfig.stateOnAsStyleClass) {
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
for (const offState of offStatesArray) {
const elementClassInfo = offState.split(':')
const offStateElement = document.getElementById(elementClassInfo[0].trim())
if (offStateElement) {
offStateElement.classList.remove(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
}
}
}
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
for (const onState of onStatesArray) {
const elementClassInfo = onState.split(':')
const onStateElement = document.getElementById(elementClassInfo[0].trim())
if (onStateElement) {
onStateElement.classList.add(elementClassInfo[1].trim())
if (svgElementConfig.stateOnAsStyleClass) {
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
for (const offState of offStatesArray) {
const elementClassInfo = offState.split(':')
const offStateElement = document.getElementById(elementClassInfo[0].trim())
if (offStateElement) {
offStateElement.classList.remove(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
}
}
}
} else if (state === 'OFF') {
const updateColor = (stateOffColorRgbStyle) || ((element?.oldFill !== 'undefined') ? element?.oldFill : 'undefined')
if (updateColor !== 'undefined') {
element.style.fill = updateColor
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
for (const onState of onStatesArray) {
const elementClassInfo = onState.split(':')
const onStateElement = document.getElementById(elementClassInfo[0].trim())
if (onStateElement) {
onStateElement.classList.add(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
}
}
if (svgElementConfig.stateAsOpacity) { // we use the flash element
let opacity = (svgElementConfig.invertStateOpacity) ? 1 : 0
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
element.style.opacity = opacity
}
} else if (state === 'OFF') {
const updateColor = (stateOffColorRgbStyle) || ((element?.oldFill !== 'undefined') ? element?.oldFill : 'undefined')
if (updateColor !== 'undefined') {
element.style.fill = updateColor
}
if (svgElementConfig.stateAsOpacity) { // we use the flash element
let opacity = (svgElementConfig.invertStateOpacity) ? 1 : 0
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
element.style.opacity = opacity
}
if (svgElementConfig.stateOnAsStyleClass) {
// remove OnState-Styles first
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
for (const onState of onStatesArray) {
const elementClassInfo = onState.split(':')
const onStateElement = document.getElementById(elementClassInfo[0].trim())
if (onStateElement) {
onStateElement.classList.remove(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
}
}
if (svgElementConfig.stateOnAsStyleClass) {
// remove OnState-Styles first
let onStatesArray = svgElementConfig.stateOnAsStyleClass.split(',')
for (const onState of onStatesArray) {
const elementClassInfo = onState.split(':')
const onStateElement = document.getElementById(elementClassInfo[0].trim())
if (onStateElement) {
onStateElement.classList.remove(elementClassInfo[1].trim())
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
for (const offState of offStatesArray) {
const elementClassInfo = offState.split(':')
const offStateElement = document.getElementById(elementClassInfo[0].trim())
if (offStateElement) {
offStateElement.classList.add(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOnAsStyleClass expression of ${element.id}`)
}
}
if (svgElementConfig.stateOffAsStyleClass) { // if offStates are provided add OffStates
let offStatesArray = svgElementConfig.stateOffAsStyleClass.split(',')
for (const offState of offStatesArray) {
const elementClassInfo = offState.split(':')
const offStateElement = document.getElementById(elementClassInfo[0].trim())
if (offStateElement) {
offStateElement.classList.add(elementClassInfo[1].trim())
} else {
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
}
console.warn(`Target element ${elementClassInfo[0].trim()} not found. Please check style stateOffAsStyleClass expression of ${element.id}`)
}
}
}
} else { // Percent, OpenClosed
if (svgElementConfig.stateAsOpacity && state) {
// we expect that number between 0 - 100
let opacity
if (stateType === 'OpenClosed') {
opacity = (state === 'OPEN') ? 1 : 0
} else if (stateType === 'Percent' && !isNaN(state)) {
opacity = parseFloat(state) / 100.0
}
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
element.style.opacity = opacity
}
} else { // Percent, OpenClosed
if (svgElementConfig.stateAsOpacity && state) {
// we expect that number between 0 - 100
let opacity
if (stateType === 'OpenClosed') {
opacity = (state === 'OPEN') ? 1 : 0
} else if (stateType === 'Percent' && !isNaN(state)) {
opacity = parseFloat(state) / 100.0
}
opacity = (svgElementConfig.invertStateOpacity) ? 1 - opacity : opacity
opacity = (opacity < svgElementConfig.stateMinOpacity) ? svgElementConfig.stateMinOpacity : opacity
element.style.opacity = opacity
}
}
}

switch (stateType) {
// currently no distinction is made regarding different state-types, only the following are supported (yet)
case 'OpenClosed':
case 'Percent':
case 'HSB':
case 'OnOff':
const useProxy = tagName === 'g' && svgElementConfig.useProxyElementForState // if proxy should be used and element is of type group
const element = (useProxy) ? svgElement.querySelector('[flash]') : svgElement
if (element) {
processState(useProxy, element)
} else {
// let's try processing all paths within the group instead
const pathElements = svgElement.querySelectorAll('path')
for (const path of pathElements) {
processState(useProxy, path)
}
}
break
Expand Down

0 comments on commit e199066

Please sign in to comment.