Path: blob/main/src/format/dashboard/format-dashboard-valuebox.ts
6451 views
/*1* format-dashboard-valuebox.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { Document, Element } from "../../core/deno-dom.ts";7import {8applyClasses,9kValueboxClass,10processAndRemoveAttr,11} from "./format-dashboard-shared.ts";1213const kValueboxBodySelector = ".card-body > div";14const kValueboxShowcaseClass = ".value-box-showcase";15const kValueboxTitleClass = "value-box-title";16const kValueBoxValueClass = "value-box-value";17const kToParagraphsClz = [kValueboxTitleClass, kValueBoxValueClass];1819const kValueBoxColorAttr = "data-color";20const kValueBoxBgColorAttr = "data-bg-color";21const kValueBoxFgColorAttr = "data-fg-color";22const kValueBoxIconAttr = "data-icon";23const kValueBoxShowcasePositionAttr = "data-showcase-position";2425const kDefaultShowcasePosition = "left-center";2627const bsLibValueBoxClass = "bslib-value-box";28const bsLibValueBoxGridClass = "value-box-grid";2930// The list of colors that should be used when automatically assigning a color31// We'll just iterate through the list as we go (circular)32const kDefaultColors = ["secondary"];3334export function isValueBox(el: Element) {35return el.classList.contains(kValueboxClass) ||36el.classList.contains(bsLibValueBoxClass);37}3839export function processValueBoxes(doc: Document) {40// Process value boxes41const valueboxNodes = doc.body.querySelectorAll(`.${kValueboxClass}`);42let autoColorizeCount = 0;43for (const valueboxNode of valueboxNodes) {44const valueboxEl = valueboxNode as Element;45applyClasses(valueboxEl, [bsLibValueBoxClass]);46const valueboxBodyEl = valueboxEl.querySelector(kValueboxBodySelector);47if (valueboxBodyEl) {48applyClasses(valueboxBodyEl, [bsLibValueBoxGridClass]);4950// Convert any divs to paragraphs51kToParagraphsClz.forEach((cls) => {52const toParaEl = valueboxEl.querySelector(`.${cls}`);53if (toParaEl && toParaEl.tagName !== "P") {54const paraEl = doc.createElement("P");55paraEl.childNodes = toParaEl.childNodes;56for (const toClass of toParaEl.classList) {57paraEl.classList.add(toClass);58}59toParaEl.replaceWith(paraEl);60}61});6263// Resolve colors, first try the general color theme64let colorProcessed = false;65processAndRemoveAttr(66valueboxBodyEl,67kValueBoxColorAttr,68(_el: Element, attrValue: string) => {69colorEl(valueboxEl, attrValue, "background");70colorProcessed = true;71},72);7374// Resolve colors, next try the background/foreground specific colors75if (!colorProcessed) {76processAndRemoveAttr(77valueboxBodyEl,78kValueBoxBgColorAttr,79(_el: Element, attrValue: string) => {80colorEl(valueboxEl, attrValue, "background");81colorProcessed = true;82},83);8485processAndRemoveAttr(86valueboxBodyEl,87kValueBoxFgColorAttr,88(_el: Element, attrValue: string) => {89colorEl(valueboxEl, attrValue, "foreground");90colorProcessed = true;91},92);93}9495// Finally, try automatically assigning a color96if (!colorProcessed) {97const suggestedColorIndex = autoColorizeCount % kDefaultColors.length;98colorEl(valueboxEl, kDefaultColors[suggestedColorIndex], "background");99autoColorizeCount++;100}101}102103// Resolve the showcase104const showcaseEl = valueboxEl.querySelector(kValueboxShowcaseClass);105if (showcaseEl) {106// Icon107processAndRemoveAttr(108showcaseEl,109kValueBoxIconAttr,110(el: Element, attrValue: string) => {111const iconEl = doc.createElement("I");112iconEl.classList.add("bi");113iconEl.classList.add(`bi-${attrValue}`);114el.append(iconEl);115},116);117118processAndRemoveAttr(119showcaseEl,120kValueBoxShowcasePositionAttr,121(_el: Element, attrValue: string) => {122valueboxEl.classList.add(`showcase-${attrValue}`);123},124kDefaultShowcasePosition,125);126}127}128}129130const isHtmlColor = (color: string) => {131return color.startsWith("#");132};133134const colorEl = (135el: Element,136color: string,137type: "background" | "foreground",138) => {139if (isHtmlColor(color)) {140const styleName = type === "background" ? "background" : "color";141const style = el.getAttribute("style");142const currentStyle = style !== null ? style : "";143el.setAttribute("style", currentStyle + ` ${styleName}: ${color};`);144} else {145const clsPrefix = type === "background" ? "bg-" : "text-";146el.classList.add(`${clsPrefix}${color}`);147}148};149150151