Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/format/dashboard/format-dashboard-valuebox.ts
6451 views
1
/*
2
* format-dashboard-valuebox.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { Document, Element } from "../../core/deno-dom.ts";
8
import {
9
applyClasses,
10
kValueboxClass,
11
processAndRemoveAttr,
12
} from "./format-dashboard-shared.ts";
13
14
const kValueboxBodySelector = ".card-body > div";
15
const kValueboxShowcaseClass = ".value-box-showcase";
16
const kValueboxTitleClass = "value-box-title";
17
const kValueBoxValueClass = "value-box-value";
18
const kToParagraphsClz = [kValueboxTitleClass, kValueBoxValueClass];
19
20
const kValueBoxColorAttr = "data-color";
21
const kValueBoxBgColorAttr = "data-bg-color";
22
const kValueBoxFgColorAttr = "data-fg-color";
23
const kValueBoxIconAttr = "data-icon";
24
const kValueBoxShowcasePositionAttr = "data-showcase-position";
25
26
const kDefaultShowcasePosition = "left-center";
27
28
const bsLibValueBoxClass = "bslib-value-box";
29
const bsLibValueBoxGridClass = "value-box-grid";
30
31
// The list of colors that should be used when automatically assigning a color
32
// We'll just iterate through the list as we go (circular)
33
const kDefaultColors = ["secondary"];
34
35
export function isValueBox(el: Element) {
36
return el.classList.contains(kValueboxClass) ||
37
el.classList.contains(bsLibValueBoxClass);
38
}
39
40
export function processValueBoxes(doc: Document) {
41
// Process value boxes
42
const valueboxNodes = doc.body.querySelectorAll(`.${kValueboxClass}`);
43
let autoColorizeCount = 0;
44
for (const valueboxNode of valueboxNodes) {
45
const valueboxEl = valueboxNode as Element;
46
applyClasses(valueboxEl, [bsLibValueBoxClass]);
47
const valueboxBodyEl = valueboxEl.querySelector(kValueboxBodySelector);
48
if (valueboxBodyEl) {
49
applyClasses(valueboxBodyEl, [bsLibValueBoxGridClass]);
50
51
// Convert any divs to paragraphs
52
kToParagraphsClz.forEach((cls) => {
53
const toParaEl = valueboxEl.querySelector(`.${cls}`);
54
if (toParaEl && toParaEl.tagName !== "P") {
55
const paraEl = doc.createElement("P");
56
paraEl.childNodes = toParaEl.childNodes;
57
for (const toClass of toParaEl.classList) {
58
paraEl.classList.add(toClass);
59
}
60
toParaEl.replaceWith(paraEl);
61
}
62
});
63
64
// Resolve colors, first try the general color theme
65
let colorProcessed = false;
66
processAndRemoveAttr(
67
valueboxBodyEl,
68
kValueBoxColorAttr,
69
(_el: Element, attrValue: string) => {
70
colorEl(valueboxEl, attrValue, "background");
71
colorProcessed = true;
72
},
73
);
74
75
// Resolve colors, next try the background/foreground specific colors
76
if (!colorProcessed) {
77
processAndRemoveAttr(
78
valueboxBodyEl,
79
kValueBoxBgColorAttr,
80
(_el: Element, attrValue: string) => {
81
colorEl(valueboxEl, attrValue, "background");
82
colorProcessed = true;
83
},
84
);
85
86
processAndRemoveAttr(
87
valueboxBodyEl,
88
kValueBoxFgColorAttr,
89
(_el: Element, attrValue: string) => {
90
colorEl(valueboxEl, attrValue, "foreground");
91
colorProcessed = true;
92
},
93
);
94
}
95
96
// Finally, try automatically assigning a color
97
if (!colorProcessed) {
98
const suggestedColorIndex = autoColorizeCount % kDefaultColors.length;
99
colorEl(valueboxEl, kDefaultColors[suggestedColorIndex], "background");
100
autoColorizeCount++;
101
}
102
}
103
104
// Resolve the showcase
105
const showcaseEl = valueboxEl.querySelector(kValueboxShowcaseClass);
106
if (showcaseEl) {
107
// Icon
108
processAndRemoveAttr(
109
showcaseEl,
110
kValueBoxIconAttr,
111
(el: Element, attrValue: string) => {
112
const iconEl = doc.createElement("I");
113
iconEl.classList.add("bi");
114
iconEl.classList.add(`bi-${attrValue}`);
115
el.append(iconEl);
116
},
117
);
118
119
processAndRemoveAttr(
120
showcaseEl,
121
kValueBoxShowcasePositionAttr,
122
(_el: Element, attrValue: string) => {
123
valueboxEl.classList.add(`showcase-${attrValue}`);
124
},
125
kDefaultShowcasePosition,
126
);
127
}
128
}
129
}
130
131
const isHtmlColor = (color: string) => {
132
return color.startsWith("#");
133
};
134
135
const colorEl = (
136
el: Element,
137
color: string,
138
type: "background" | "foreground",
139
) => {
140
if (isHtmlColor(color)) {
141
const styleName = type === "background" ? "background" : "color";
142
const style = el.getAttribute("style");
143
const currentStyle = style !== null ? style : "";
144
el.setAttribute("style", currentStyle + ` ${styleName}: ${color};`);
145
} else {
146
const clsPrefix = type === "background" ? "bg-" : "text-";
147
el.classList.add(`${clsPrefix}${color}`);
148
}
149
};
150
151