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-sidebar.ts
6451 views
1
/*
2
* format-dashboard-sidebar.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { asCssSize } from "../../core/css.ts";
8
import { Document, Element } from "../../core/deno-dom.ts";
9
import { recursiveApplyFillClasses } from "./format-dashboard-layout.ts";
10
import { makeEl, processAndRemoveAttr } from "./format-dashboard-shared.ts";
11
12
const kSidebarPanelClass = "sidebar-panel";
13
const kSidebarClass = "sidebar";
14
const kSidebarContentClass = "sidebar-content";
15
16
const kSidebarAttrPosition = "data-position";
17
const kSidebarAttrPositionRight = "right";
18
const kBsLibSidebarRight = "sidebar-right";
19
20
export function makeSidebar(
21
id: string,
22
sidebar: Element,
23
contents: Element[],
24
doc: Document,
25
) {
26
// Create the sidebar container
27
const sidebarContainerEl = makeEl("div", {
28
classes: ["bslib-sidebar-layout", "html-fill-item"],
29
attributes: {
30
"data-bslib-sidebar-open": "desktop",
31
"data-bslib-sidebar-init": "true",
32
},
33
}, doc);
34
35
// Capture the content (the sidebar's next sibling)
36
const sidebarMainEl = makeEl("div", {
37
classes: ["main", "html-fill-container"],
38
}, doc);
39
contents.forEach((content) => {
40
sidebarMainEl.appendChild(content);
41
});
42
43
// See if there is a width
44
// Read the position and apply class if needed
45
processAndRemoveAttr(
46
sidebar,
47
kSidebarAttrPosition,
48
(_el: Element, value: string) => {
49
if (value === kSidebarAttrPositionRight) {
50
sidebarContainerEl.classList.add(kBsLibSidebarRight);
51
}
52
},
53
);
54
55
processAndRemoveAttr(
56
sidebar,
57
"data-width",
58
(_el: Element, value: string) => {
59
const size = asCssSize(value);
60
61
const styleRaw = sidebarContainerEl.getAttribute("style");
62
const styleVal = styleRaw !== null ? styleRaw : "";
63
const newStyle = styleVal + " --bslib-sidebar-width: " + size;
64
sidebarContainerEl.setAttribute("style", newStyle);
65
},
66
);
67
68
// Remove the sidebar class
69
sidebar?.classList.remove(kSidebarClass);
70
71
const sidebarAsideEl = makeEl("aside", {
72
id,
73
classes: [kSidebarClass, "html-fill-container", "html-fill-item"],
74
}, doc);
75
76
// place contents inside
77
if (sidebar !== null) {
78
sidebar.classList.add("html-fill-container", "html-fill-item");
79
sidebarAsideEl.appendChild(sidebar);
80
}
81
82
sidebarContainerEl.appendChild(sidebarMainEl);
83
recursiveApplyFillClasses(sidebarContainerEl);
84
85
sidebarContainerEl.appendChild(sidebarAsideEl);
86
sidebarContainerEl.append(...sidebarToggle(id, doc));
87
return sidebarContainerEl;
88
}
89
90
export function processSidebars(doc: Document) {
91
// use a counter to provision ids
92
const sidebarNodes = doc.querySelectorAll(`.${kSidebarPanelClass}`);
93
let sidebarCount = 1;
94
for (const sidebarNode of sidebarNodes) {
95
const sidebarEl = sidebarNode as Element;
96
const sidebarId = sidebarEl.id !== ""
97
? sidebarEl.id
98
: `bslib-sidebar-${sidebarCount++}`;
99
100
const sidebarContentsEl = sidebarEl.querySelector(`.${kSidebarClass}`);
101
const sidebarMainContentsEl = sidebarEl.querySelector(
102
`.${kSidebarContentClass}`,
103
);
104
105
let sidebarContainerEl = undefined;
106
if (sidebarContentsEl !== null && sidebarMainContentsEl !== null) {
107
sidebarContainerEl = makeSidebar(sidebarId, sidebarContentsEl, [
108
sidebarMainContentsEl,
109
], doc);
110
sidebarEl.replaceWith(sidebarContainerEl);
111
sidebarContainerEl.parentElement?.classList.add(
112
"dashboard-sidebar-container",
113
);
114
}
115
}
116
117
// Decorate the body of the document if there is a top level sidebar panel
118
const topLevelSidebar = doc.querySelector(
119
".page-layout-custom > .bslib-sidebar-layout, .page-layout-custom .dashboard-page > .bslib-sidebar-layout",
120
);
121
if (topLevelSidebar !== null) {
122
topLevelSidebar.setAttribute("data-bslib-sidebar-border", "false");
123
topLevelSidebar.setAttribute("data-bslib-sidebar-border-radius", "false");
124
125
doc.body.classList.add("dashboard-sidebar");
126
}
127
}
128
129
function sidebarToggle(id: string, doc: Document) {
130
const html = `
131
<button class="collapse-toggle" type="button" title="Toggle sidebar" aria-expanded="true" aria-controls="${id}">
132
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="bi bi-chevron-left collapse-icon" style="fill:currentColor;" aria-hidden="true" role="img">
133
<path fill-rule="evenodd" d="M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z"></path>
134
</svg>
135
</button>
136
<script data-bslib-sidebar-init>
137
bslib.Sidebar.initCollapsibleAll()
138
</script>
139
`;
140
const container = doc.createElement("DIV");
141
container.innerHTML = html;
142
return container.childNodes;
143
}
144
145