675 lines
19 KiB
Markdown
675 lines
19 KiB
Markdown
---
|
||
cssclasses:
|
||
- wide-dashboard
|
||
- no-properties
|
||
- note-bg-blur
|
||
- styled-checkbox
|
||
- styled-hr
|
||
- small-text
|
||
---
|
||
<div style="
|
||
width: 100%;
|
||
height: 200px;
|
||
background-image: url('https://images8.alphacoders.com/127/thumb-1920-1275563.jpg');
|
||
background-size: cover;
|
||
background-position: center 35%;
|
||
filter: blur(2px);
|
||
border-radius: 0;
|
||
margin: 0;
|
||
"></div>
|
||
|
||
---
|
||
# Dailys
|
||
## Week
|
||
```dataviewjs
|
||
// Force English locale for weekdays
|
||
moment.locale("en");
|
||
|
||
const today = moment();
|
||
|
||
// === CONFIGURE WEEK RANGE HERE ===
|
||
// For current week:
|
||
const weekStart = today.clone().startOf("isoWeek");
|
||
const weekEnd = today.clone().endOf("isoWeek");
|
||
|
||
// For next week, uncomment these:
|
||
// const weekStart = today.clone().add(1, "week").startOf("isoWeek");
|
||
// const weekEnd = today.clone().add(1, "week").endOf("isoWeek");
|
||
|
||
// For weekend only (Sat & Sun):
|
||
// const weekStart = today.clone().startOf("isoWeek").add(5, "days"); // Saturday
|
||
// const weekEnd = today.clone().startOf("isoWeek").add(6, "days"); // Sunday
|
||
|
||
// All notes tagged #Calendar within the week
|
||
const pages = dv.pages("#Calendar")
|
||
.where(p => {
|
||
const d = moment(p.file.name, "DD.MM.YYYY", true);
|
||
return d.isValid() && d.isBetween(weekStart, weekEnd, "day", "[]");
|
||
});
|
||
|
||
// Generate days array (Mon–Sun or custom)
|
||
const totalDays = weekEnd.diff(weekStart, "days") + 1;
|
||
const weekDays = Array.from({ length: totalDays }, (_, i) => weekStart.clone().add(i, "days"));
|
||
|
||
// Separate days with and without notes
|
||
const daysWithNotes = [];
|
||
const daysWithoutNotes = [];
|
||
|
||
for (const day of weekDays) {
|
||
const page = pages.find(p => p.file.name === day.format("DD.MM.YYYY"));
|
||
if (page) {
|
||
daysWithNotes.push({ date: day, page });
|
||
} else {
|
||
daysWithoutNotes.push(day);
|
||
}
|
||
}
|
||
|
||
// Build Columns plugin Markdown
|
||
let md = "````col\nheight=shortest\ntextAlign=start\n===\n";
|
||
|
||
// Columns for days with notes
|
||
for (const { date, page } of daysWithNotes) {
|
||
const isToday = date.isSame(today, "day");
|
||
const label = `${isToday ? "📌 " : ""}${date.format("ddd")}<br>${date.format("DD.MM.YYYY")}`;
|
||
|
||
md += "```col-md\n";
|
||
md += `### ${label}\n`;
|
||
|
||
// Show reason from frontmatter under the title
|
||
if (page.reason) {
|
||
md += `**Reason:** ${page.reason}\n\n`;
|
||
}
|
||
|
||
// Get tasks from the note
|
||
const tasks = page.file.tasks;
|
||
|
||
// Filter tasks under # Tasks heading if possible
|
||
const tasksInSection = tasks.filter(t => {
|
||
if (t.heading) return t.heading.toLowerCase().includes("tasks");
|
||
return true; // fallback: include all tasks if heading not available
|
||
});
|
||
|
||
if (tasksInSection.length > 0) {
|
||
for (const t of tasksInSection) {
|
||
md += `- [${t.completed ? "x" : " "}] ${t.text}\n`;
|
||
}
|
||
} else {
|
||
md += "_No tasks found_\n";
|
||
}
|
||
|
||
md += "```\n\n";
|
||
}
|
||
|
||
// Single column for days without notes
|
||
if (daysWithoutNotes.length > 0) {
|
||
md += "```col-md\n";
|
||
md += "### Days without notes\n";
|
||
for (const day of daysWithoutNotes) {
|
||
md += `- ${day.format("ddd")} — ${day.format("DD.MM.YYYY")}\n`;
|
||
}
|
||
md += "```\n\n";
|
||
}
|
||
|
||
md += "````\n";
|
||
|
||
// Render the dashboard
|
||
dv.paragraph(md);
|
||
|
||
```
|
||
## Weekend
|
||
```dataviewjs
|
||
// Force English locale for weekdays
|
||
moment.locale("en");
|
||
|
||
const today = moment();
|
||
|
||
// === CONFIGURE WEEK RANGE HERE ===
|
||
// For current week:
|
||
//const weekStart = today.clone().startOf("isoWeek");
|
||
//const weekEnd = today.clone().endOf("isoWeek");
|
||
|
||
// For next week, uncomment these:
|
||
// const weekStart = today.clone().add(1, "week").startOf("isoWeek");
|
||
// const weekEnd = today.clone().add(1, "week").endOf("isoWeek");
|
||
|
||
// For weekend only (Sat & Sun):
|
||
const weekStart = today.clone().startOf("isoWeek").add(5, "days"); // Saturday
|
||
const weekEnd = today.clone().startOf("isoWeek").add(6, "days"); // Sunday
|
||
|
||
// All notes tagged #Calendar within the week
|
||
const pages = dv.pages("#Calendar")
|
||
.where(p => {
|
||
const d = moment(p.file.name, "DD.MM.YYYY", true);
|
||
return d.isValid() && d.isBetween(weekStart, weekEnd, "day", "[]");
|
||
});
|
||
|
||
// Generate days array (Mon–Sun or custom)
|
||
const totalDays = weekEnd.diff(weekStart, "days") + 1;
|
||
const weekDays = Array.from({ length: totalDays }, (_, i) => weekStart.clone().add(i, "days"));
|
||
|
||
// Separate days with and without notes
|
||
const daysWithNotes = [];
|
||
const daysWithoutNotes = [];
|
||
|
||
for (const day of weekDays) {
|
||
const page = pages.find(p => p.file.name === day.format("DD.MM.YYYY"));
|
||
if (page) {
|
||
daysWithNotes.push({ date: day, page });
|
||
} else {
|
||
daysWithoutNotes.push(day);
|
||
}
|
||
}
|
||
|
||
// Build Columns plugin Markdown
|
||
let md = "````col\nheight=shortest\ntextAlign=start\n===\n";
|
||
|
||
// Columns for days with notes
|
||
for (const { date, page } of daysWithNotes) {
|
||
const isToday = date.isSame(today, "day");
|
||
const label = `${isToday ? "📌 " : ""}${date.format("ddd")}<br>${date.format("DD.MM.YYYY")}`;
|
||
|
||
md += "```col-md\n";
|
||
md += `### ${label}\n`;
|
||
|
||
// Show reason from frontmatter under the title
|
||
if (page.reason) {
|
||
md += `**Reason:** ${page.reason}\n\n`;
|
||
}
|
||
|
||
// Get tasks from the note
|
||
const tasks = page.file.tasks;
|
||
|
||
// Filter tasks under # Tasks heading if possible
|
||
const tasksInSection = tasks.filter(t => {
|
||
if (t.heading) return t.heading.toLowerCase().includes("tasks");
|
||
return true; // fallback: include all tasks if heading not available
|
||
});
|
||
|
||
if (tasksInSection.length > 0) {
|
||
for (const t of tasksInSection) {
|
||
md += `- [${t.completed ? "x" : " "}] ${t.text}\n`;
|
||
}
|
||
} else {
|
||
md += "_No tasks found_\n";
|
||
}
|
||
|
||
md += "```\n\n";
|
||
}
|
||
|
||
// Single column for days without notes
|
||
if (daysWithoutNotes.length > 0) {
|
||
md += "```col-md\n";
|
||
md += "### Days without notes\n";
|
||
for (const day of daysWithoutNotes) {
|
||
md += `- ${day.format("ddd")} — ${day.format("DD.MM.YYYY")}\n`;
|
||
}
|
||
md += "```\n\n";
|
||
}
|
||
|
||
md += "````\n";
|
||
|
||
// Render the dashboard
|
||
dv.paragraph(md);
|
||
|
||
```
|
||
---
|
||
|
||
# Next Week
|
||
```dataviewjs
|
||
// Force English locale for weekdays
|
||
moment.locale("en");
|
||
|
||
const today = moment();
|
||
|
||
// === CONFIGURE WEEK RANGE HERE ===
|
||
// For current week:
|
||
//const weekStart = today.clone().startOf("isoWeek");
|
||
//const weekEnd = today.clone().endOf("isoWeek");
|
||
|
||
// For next week, uncomment these:
|
||
const weekStart = today.clone().add(1, "week").startOf("isoWeek");
|
||
const weekEnd = today.clone().add(1, "week").endOf("isoWeek");
|
||
|
||
// For weekend only (Sat & Sun):
|
||
// const weekStart = today.clone().startOf("isoWeek").add(5, "days"); // Saturday
|
||
// const weekEnd = today.clone().startOf("isoWeek").add(6, "days"); // Sunday
|
||
|
||
// All notes tagged #Calendar within the week
|
||
const pages = dv.pages("#Calendar")
|
||
.where(p => {
|
||
const d = moment(p.file.name, "DD.MM.YYYY", true);
|
||
return d.isValid() && d.isBetween(weekStart, weekEnd, "day", "[]");
|
||
});
|
||
|
||
// Generate days array (Mon–Sun or custom)
|
||
const totalDays = weekEnd.diff(weekStart, "days") + 1;
|
||
const weekDays = Array.from({ length: totalDays }, (_, i) => weekStart.clone().add(i, "days"));
|
||
|
||
// Separate days with and without notes
|
||
const daysWithNotes = [];
|
||
const daysWithoutNotes = [];
|
||
|
||
for (const day of weekDays) {
|
||
const page = pages.find(p => p.file.name === day.format("DD.MM.YYYY"));
|
||
if (page) {
|
||
daysWithNotes.push({ date: day, page });
|
||
} else {
|
||
daysWithoutNotes.push(day);
|
||
}
|
||
}
|
||
|
||
// Build Columns plugin Markdown
|
||
let md = "````col\nheight=shortest\ntextAlign=start\n===\n";
|
||
|
||
// Columns for days with notes
|
||
for (const { date, page } of daysWithNotes) {
|
||
const isToday = date.isSame(today, "day");
|
||
const label = `${isToday ? "📌 " : ""}${date.format("ddd")}<br>${date.format("DD.MM.YYYY")}`;
|
||
|
||
md += "```col-md\n";
|
||
md += `### ${label}\n`;
|
||
|
||
// Show reason from frontmatter under the title
|
||
if (page.reason) {
|
||
md += `**Reason:** ${page.reason}\n\n`;
|
||
}
|
||
|
||
// Get tasks from the note
|
||
const tasks = page.file.tasks;
|
||
|
||
// Filter tasks under # Tasks heading if possible
|
||
const tasksInSection = tasks.filter(t => {
|
||
if (t.heading) return t.heading.toLowerCase().includes("tasks");
|
||
return true; // fallback: include all tasks if heading not available
|
||
});
|
||
|
||
if (tasksInSection.length > 0) {
|
||
for (const t of tasksInSection) {
|
||
md += `- [${t.completed ? "x" : " "}] ${t.text}\n`;
|
||
}
|
||
} else {
|
||
md += "_No tasks found_\n";
|
||
}
|
||
|
||
md += "```\n\n";
|
||
}
|
||
|
||
// Single column for days without notes
|
||
if (daysWithoutNotes.length > 0) {
|
||
md += "```col-md\n";
|
||
md += "### Days without notes\n";
|
||
for (const day of daysWithoutNotes) {
|
||
md += `- ${day.format("ddd")} — ${day.format("DD.MM.YYYY")}\n`;
|
||
}
|
||
md += "```\n\n";
|
||
}
|
||
|
||
md += "````\n";
|
||
|
||
// Render the dashboard
|
||
dv.paragraph(md);
|
||
|
||
```
|
||
---
|
||
|
||
![[Veranstaltungen - 2025]]
|
||
|
||
---
|
||
|
||
![[Wohnorte]]
|
||
|
||
---
|
||
# Stats
|
||
```dataviewjs
|
||
// === Config ===
|
||
const splitOnSlash = false; // true -> merge hierarchical tags (#project/obsidian -> project)
|
||
const topN = 50; // show top N tags (null for all)
|
||
|
||
// === Catppuccin Mocha Palette ===
|
||
const catppuccin = {
|
||
rosewater: '#f5e0dc',
|
||
flamingo: '#f2cdcd',
|
||
pink: '#f5c2e7',
|
||
mauve: '#cba6f7',
|
||
red: '#f38ba8',
|
||
maroon: '#eba0ac',
|
||
peach: '#fab387',
|
||
yellow: '#f9e2af',
|
||
green: '#a6e3a1',
|
||
teal: '#94e2d5',
|
||
sky: '#89dceb',
|
||
sapphire: '#74c7ec',
|
||
blue: '#89b4fa',
|
||
lavender: '#b4befe',
|
||
text: '#cdd6f4',
|
||
subtext1: '#bac2de',
|
||
base: '#1e1e2e'
|
||
};
|
||
|
||
const catppuccinColors = [
|
||
catppuccin.rosewater,
|
||
catppuccin.flamingo,
|
||
catppuccin.pink,
|
||
catppuccin.mauve,
|
||
catppuccin.red,
|
||
catppuccin.maroon,
|
||
catppuccin.peach,
|
||
catppuccin.yellow,
|
||
catppuccin.green,
|
||
catppuccin.teal,
|
||
catppuccin.sky,
|
||
catppuccin.sapphire,
|
||
catppuccin.blue,
|
||
catppuccin.lavender
|
||
];
|
||
|
||
// === Helpers ===
|
||
function flattenTags(input, out = []) {
|
||
if (!input) return out;
|
||
if (Array.isArray(input)) {
|
||
for (const v of input) flattenTags(v, out);
|
||
} else if (typeof input === 'string') {
|
||
out.push(input);
|
||
} else if (typeof input === 'object') {
|
||
if (typeof input.tag === 'string') out.push(input.tag);
|
||
else if (typeof input.path === 'string') out.push(input.path);
|
||
else {
|
||
const s = String(input);
|
||
if (s && s !== '[object Object]') out.push(s);
|
||
}
|
||
} else {
|
||
out.push(String(input));
|
||
}
|
||
return out;
|
||
}
|
||
|
||
// Convert a string to Title Case (preserving slashes/hyphens)
|
||
function toTitleCase(str) {
|
||
return str.replace(/[\w]+/g, w =>
|
||
w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
||
);
|
||
}
|
||
|
||
// === Collect tags from all pages ===
|
||
const pages = dv.pages();
|
||
let allTags = [];
|
||
for (const p of pages) {
|
||
if (p.file?.tags) flattenTags(p.file.tags, allTags);
|
||
if (p.tags) flattenTags(p.tags, allTags);
|
||
if (p.tags_list) flattenTags(p.tags_list, allTags);
|
||
}
|
||
|
||
// === Normalize and count ===
|
||
const counts = {};
|
||
for (let tag of allTags) {
|
||
if (!tag) continue;
|
||
tag = String(tag).replace(/^#/, '').trim().toLowerCase();
|
||
if (!tag) continue;
|
||
if (splitOnSlash && tag.includes('/')) tag = tag.split('/')[0];
|
||
counts[tag] = (counts[tag] || 0) + 1;
|
||
}
|
||
|
||
// === Handle no tags ===
|
||
const tagEntries = Object.entries(counts);
|
||
if (tagEntries.length === 0) {
|
||
dv.el("p", "⚠️ No tags found in your vault.");
|
||
} else {
|
||
// === Sort + limit ===
|
||
tagEntries.sort((a, b) => b[1] - a[1]);
|
||
const limited = (topN && tagEntries.length > topN)
|
||
? tagEntries.slice(0, topN)
|
||
: tagEntries;
|
||
|
||
// Convert to title case for display
|
||
const labels = limited.map(e => toTitleCase(e[0].replace(/[-_]/g, ' ')));
|
||
const dataValues = limited.map(e => e[1]);
|
||
|
||
// === Color setup ===
|
||
const bgColors = [];
|
||
const borderColors = [];
|
||
for (let i = 0; i < labels.length; i++) {
|
||
const color = catppuccinColors[i % catppuccinColors.length];
|
||
bgColors.push(color + "55");
|
||
borderColors.push(color);
|
||
}
|
||
|
||
// === Chart.js config ===
|
||
const chartData = {
|
||
type: 'bar',
|
||
data: {
|
||
labels: labels,
|
||
datasets: [{
|
||
label: 'Tag Usage',
|
||
data: dataValues,
|
||
backgroundColor: bgColors,
|
||
borderColor: borderColors,
|
||
borderWidth: 1
|
||
}]
|
||
},
|
||
options: {
|
||
plugins: {
|
||
legend: { display: false },
|
||
title: {
|
||
display: true,
|
||
text: `Tag Usage (Top ${labels.length})`,
|
||
color: catppuccin.text,
|
||
font: { size: 16 }
|
||
}
|
||
},
|
||
scales: {
|
||
x: {
|
||
ticks: { color: catppuccin.subtext1, autoSkip: false, maxRotation: 60, minRotation: 30 },
|
||
title: { display: true, text: 'Tags', color: catppuccin.text },
|
||
grid: { color: catppuccin.base }
|
||
},
|
||
y: {
|
||
ticks: { color: catppuccin.subtext1 },
|
||
title: { display: true, text: 'Count', color: catppuccin.text },
|
||
beginAtZero: true,
|
||
grid: { color: catppuccin.base }
|
||
}
|
||
},
|
||
maintainAspectRatio: false
|
||
}
|
||
};
|
||
|
||
// === Render ===
|
||
const wrapper = this.container.createEl('div');
|
||
wrapper.style.minHeight = '320px';
|
||
wrapper.style.maxHeight = '60vh';
|
||
wrapper.style.overflow = 'auto';
|
||
wrapper.style.borderRadius = '8px';
|
||
wrapper.style.padding = '8px';
|
||
|
||
window.renderChart(chartData, wrapper);
|
||
}
|
||
```
|
||
```dataviewjs
|
||
// === Config ===
|
||
const splitOnSlash = false; // true -> merge hierarchical tags (#project/obsidian -> project)
|
||
const topN = 20; // show top N tags (null for all)
|
||
|
||
// === Catppuccin Mocha Palette ===
|
||
const catppuccin = {
|
||
rosewater: '#f5e0dc',
|
||
flamingo: '#f2cdcd',
|
||
pink: '#f5c2e7',
|
||
mauve: '#cba6f7',
|
||
red: '#f38ba8',
|
||
maroon: '#eba0ac',
|
||
peach: '#fab387',
|
||
yellow: '#f9e2af',
|
||
green: '#a6e3a1',
|
||
teal: '#94e2d5',
|
||
sky: '#89dceb',
|
||
sapphire: '#74c7ec',
|
||
blue: '#89b4fa',
|
||
lavender: '#b4befe',
|
||
text: '#cdd6f4',
|
||
subtext1: '#bac2de',
|
||
base: '#1e1e2e'
|
||
};
|
||
|
||
const catppuccinColors = [
|
||
catppuccin.rosewater,
|
||
catppuccin.flamingo,
|
||
catppuccin.pink,
|
||
catppuccin.red,
|
||
catppuccin.maroon,
|
||
catppuccin.peach,
|
||
catppuccin.yellow,
|
||
catppuccin.green,
|
||
catppuccin.teal,
|
||
catppuccin.sky,
|
||
catppuccin.sapphire,
|
||
catppuccin.blue,
|
||
catppuccin.lavender,
|
||
catppuccin.mauve
|
||
];
|
||
|
||
// === Helpers ===
|
||
function flattenTags(input, out = []) {
|
||
if (!input) return out;
|
||
if (Array.isArray(input)) {
|
||
for (const v of input) flattenTags(v, out);
|
||
} else if (typeof input === 'string') {
|
||
out.push(input);
|
||
} else if (typeof input === 'object') {
|
||
if (typeof input.tag === 'string') out.push(input.tag);
|
||
else if (typeof input.path === 'string') out.push(input.path);
|
||
else {
|
||
const s = String(input);
|
||
if (s && s !== '[object Object]') out.push(s);
|
||
}
|
||
} else {
|
||
out.push(String(input));
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function toTitleCase(str) {
|
||
return str.replace(/[\w]+/g, w =>
|
||
w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
||
);
|
||
}
|
||
|
||
// === Collect tags ===
|
||
const pages = dv.pages();
|
||
let allTags = [];
|
||
for (const p of pages) {
|
||
if (p.file?.tags) flattenTags(p.file.tags, allTags);
|
||
if (p.tags) flattenTags(p.tags, allTags);
|
||
if (p.tags_list) flattenTags(p.tags_list, allTags);
|
||
}
|
||
|
||
// === Normalize + count ===
|
||
const counts = {};
|
||
for (let tag of allTags) {
|
||
if (!tag) continue;
|
||
tag = String(tag).replace(/^#/, '').trim().toLowerCase();
|
||
if (!tag) continue;
|
||
if (splitOnSlash && tag.includes('/')) tag = tag.split('/')[0];
|
||
counts[tag] = (counts[tag] || 0) + 1;
|
||
}
|
||
|
||
const tagEntries = Object.entries(counts);
|
||
if (tagEntries.length === 0) {
|
||
dv.el("p", "⚠️ No tags found in your vault.");
|
||
} else {
|
||
// === Sort + limit ===
|
||
tagEntries.sort((a, b) => b[1] - a[1]);
|
||
const limited = (topN && tagEntries.length > topN)
|
||
? tagEntries.slice(0, topN)
|
||
: tagEntries;
|
||
|
||
const labels = limited.map(e => toTitleCase(e[0].replace(/[-_]/g, ' ')));
|
||
const dataValues = limited.map(e => e[1]);
|
||
const total = dataValues.reduce((a, b) => a + b, 0);
|
||
|
||
// === Colors ===
|
||
const bgColors = [], borderColors = [];
|
||
for (let i = 0; i < labels.length; i++) {
|
||
const color = catppuccinColors[i % catppuccinColors.length];
|
||
bgColors.push(color + "aa");
|
||
borderColors.push(color);
|
||
}
|
||
|
||
// === Create container ===
|
||
const wrapper = this.container.createEl('div');
|
||
wrapper.style.minHeight = '400px';
|
||
wrapper.style.maxHeight = '70vh';
|
||
wrapper.style.position = 'relative';
|
||
wrapper.style.borderRadius = '8px';
|
||
wrapper.style.padding = '8px';
|
||
wrapper.style.overflow = 'auto';
|
||
|
||
// === Center label ===
|
||
const centerLabel = document.createElement('div');
|
||
centerLabel.style.position = 'absolute';
|
||
centerLabel.style.top = '50%';
|
||
centerLabel.style.left = '44%';
|
||
centerLabel.style.transform = 'translate(-50%, -50%)';
|
||
centerLabel.style.color = catppuccin.text;
|
||
centerLabel.style.fontSize = '14px';
|
||
centerLabel.style.fontWeight = '600';
|
||
centerLabel.style.textAlign = 'center';
|
||
centerLabel.innerText = 'Tag Usage';
|
||
wrapper.appendChild(centerLabel);
|
||
|
||
// === Chart.js config ===
|
||
const chartData = {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: labels,
|
||
datasets: [{
|
||
data: dataValues,
|
||
backgroundColor: bgColors,
|
||
borderColor: borderColors,
|
||
borderWidth: 1
|
||
}]
|
||
},
|
||
options: {
|
||
cutout: '60%',
|
||
plugins: {
|
||
legend: {
|
||
display: true,
|
||
position: 'right',
|
||
labels: { color: catppuccin.subtext1, font: { size: 12 } }
|
||
},
|
||
title: {
|
||
display: true,
|
||
text: `Tag Distribution (Top ${labels.length})`,
|
||
color: catppuccin.text,
|
||
font: { size: 16 }
|
||
},
|
||
tooltip: {
|
||
callbacks: {
|
||
label: ctx => {
|
||
const val = ctx.raw;
|
||
const pct = ((val / total) * 100).toFixed(1);
|
||
return `${ctx.label}: ${val} (${pct}%)`;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
onHover: (evt, elements) => {
|
||
if (elements.length > 0) {
|
||
const el = elements[0];
|
||
const label = labels[el.index];
|
||
const value = dataValues[el.index];
|
||
const pct = ((value / total) * 100).toFixed(1);
|
||
centerLabel.innerText = `${label}\n${pct}%`;
|
||
} else {
|
||
centerLabel.innerText = 'Tag Usage';
|
||
}
|
||
},
|
||
maintainAspectRatio: false
|
||
}
|
||
};
|
||
|
||
window.renderChart(chartData, wrapper);
|
||
}
|
||
```
|
||
|
||
---
|