vran-dev_obsidian-contribut.../src/render/graphRender.ts

304 lines
7.1 KiB
TypeScript
Raw Normal View History

import { DEFAULT_RULES } from "src/constants";
import {
ContributionGraphConfig,
ContributionCellData,
CellStyleRule,
ContributionItem,
} from "src/types";
import { parseDate } from "src/util/dateUtils";
import {
generateByLatestDays,
generateByFixedDate,
generateByData,
} from "./matrixDataGenerator";
import { matchCellStyleRule } from "src/util/utils";
import { setTooltip } from "obsidian";
import { Locals } from "src/i18/messages";
export interface GraphRender {
render(container: HTMLElement, graphConfig: ContributionGraphConfig): void;
graphType(): string;
}
export abstract class BaseGraphRender implements GraphRender {
constructor() {}
render(container: HTMLElement, graphConfig: ContributionGraphConfig): void {
throw new Error("Method not implemented.");
}
abstract graphType(): string;
renderTitle(
graphConfig: ContributionGraphConfig,
parent: HTMLElement
): HTMLElement {
2023-12-22 02:04:22 +00:00
const titleEl = document.createElement("div");
titleEl.className = "title";
if (graphConfig.title) {
titleEl.innerText = graphConfig.title;
}
if (graphConfig.titleStyle) {
Object.assign(titleEl.style, graphConfig.titleStyle);
}
parent.appendChild(titleEl);
return titleEl;
2023-12-22 02:04:22 +00:00
}
renderCellRuleIndicator(
graphConfig: ContributionGraphConfig,
parent: HTMLElement
) {
if (graphConfig.showCellRuleIndicators === false) {
return;
}
const cellRuleIndicatorContainer = createDiv({
cls: "cell-rule-indicator-container",
parent: parent,
});
const cellRules = this.getCellRules(graphConfig);
createDiv({
cls: "cell text",
text: "less",
parent: cellRuleIndicatorContainer,
});
cellRules
.sort((a, b) => a.min - b.min)
.forEach((rule) => {
const cellEl = createDiv({
cls: ["cell"],
parent: cellRuleIndicatorContainer,
});
cellEl.className = "cell";
cellEl.style.backgroundColor = rule.color;
cellEl.innerText = rule.text || "";
// bind tips event
const summary = `${rule.min} ≤ contributions ${rule.max}`;
setTooltip(cellEl, summary);
});
createDiv({
cls: "cell text",
text: "more",
parent: cellRuleIndicatorContainer,
});
}
renderActivityContainer(
graphConfig: ContributionGraphConfig,
parent: HTMLElement
): HTMLElement {
const activityContainer = createDiv({
cls: "activity-container",
parent: parent,
});
return activityContainer;
}
renderActivity(
graphConfig: ContributionGraphConfig,
cellData: ContributionCellData,
contaienr: HTMLElement
) {
contaienr.empty();
const closeButton = createEl("button", {
cls: "close-button",
text: 'x',
parent: contaienr,
})
closeButton.onclick = () => {
contaienr.empty();
}
let summary;
if (cellData.value > 0) {
summary = Locals.get().you_have_contributed_to
.replace("{date}", cellData.date)
.replace("{value}", cellData.value.toString());
} else {
summary = Locals.get().you_have_no_contributions_on
.replace("{date}", cellData.date)
.replace("{value}", "0");
}
createDiv({
cls: "activity-summary",
parent: contaienr,
text: summary,
});
const content = createDiv({
cls: "activity-content",
parent: contaienr,
});
// list-main
const list = createDiv({
cls: "activity-list",
parent: content,
});
// show top 10 items
const size = 10;
const items = cellData.items || [];
renderActivityItem(items.slice(0, size), list);
const navigation = createDiv({
cls: "activity-navigation",
parent: content,
});
let page = 1;
if (items.length > size) {
const loadMore = createEl("a", {
text: Locals.get().click_to_load_more,
href: "#",
parent: navigation,
});
loadMore.onclick = (event) => {
event.preventDefault();
page++;
renderActivityItem(
items.slice((page - 1) * size, page * size),
list
);
if (page * size >= items.length) {
loadMore.remove();
}
};
}
}
generateContributionData(graphConfig: ContributionGraphConfig) {
if (graphConfig.days) {
return generateByLatestDays(graphConfig.days, graphConfig.data);
} else if (graphConfig.fromDate && graphConfig.toDate) {
const fromDate = parseDate(graphConfig.fromDate);
const toDate = parseDate(graphConfig.toDate);
return generateByFixedDate(fromDate, toDate, graphConfig.data);
} else {
return generateByData(graphConfig.data);
}
}
getCellRules(graphConfig: ContributionGraphConfig) {
return graphConfig.cellStyleRules &&
graphConfig.cellStyleRules.length > 0
? graphConfig.cellStyleRules
: DEFAULT_RULES;
}
bindMonthTips(
monthCell: HTMLElement,
contributionItem: ContributionCellData,
contributionMapByYearMonth: Map<string, number>
) {
const yearMonth = `${contributionItem.year}-${
contributionItem.month + 1
}`;
const yearMonthValue = contributionMapByYearMonth.get(yearMonth) || 0;
// tips event
setTooltip(
monthCell,
`${yearMonthValue} contributions on ${yearMonth}.`
);
}
applyCellGlobalStyle(
cellEl: HTMLElement,
graphConfig: ContributionGraphConfig
) {
if (graphConfig.cellStyle) {
Object.assign(cellEl.style, graphConfig.cellStyle);
}
}
applyCellStyleRule(
cellEl: HTMLElement,
contributionItem: ContributionCellData,
cellRules: CellStyleRule[],
defaultCellStyleRule?: () => CellStyleRule
) {
const cellStyleRule = matchCellStyleRule(
contributionItem.value,
cellRules
);
if (cellStyleRule != null) {
cellEl.style.backgroundColor = cellStyleRule.color;
cellEl.innerText = cellStyleRule.text || "";
return;
}
if (defaultCellStyleRule) {
const defaultRule = defaultCellStyleRule();
cellEl.style.backgroundColor = defaultRule.color;
cellEl.innerText = defaultRule.text || "";
}
}
bindCellAttribute(
cellEl: HTMLElement,
contributionItem: ContributionCellData
) {
cellEl.setAttribute("data-year", contributionItem.year.toString());
cellEl.setAttribute("data-month", contributionItem.month.toString());
cellEl.setAttribute("data-date", contributionItem.date.toString());
}
bindCellClickEvent(
cellEl: HTMLElement,
contributionItem: ContributionCellData,
graphConfig: ContributionGraphConfig,
activityContainer?: HTMLElement
) {
cellEl.onclick = (event: MouseEvent) => {
if (graphConfig.onCellClick) {
graphConfig.onCellClick(contributionItem, event);
}
if (activityContainer) {
this.renderActivity(
graphConfig,
contributionItem,
activityContainer
);
}
};
}
bindCellTips(cellEl: HTMLElement, contributionItem: ContributionCellData) {
cellEl.addEventListener("mouseenter", (event) => {
const summary = contributionItem.summary
? contributionItem.summary
: `${contributionItem.value} contributions on ${contributionItem.date}.`;
setTooltip(cellEl, summary);
});
}
}
function renderActivityItem(items: ContributionItem[], listMain: HTMLElement) {
(items || []).slice(0, 10).forEach((item) => {
const listItem = createDiv({
cls: "activity-item",
parent: listMain,
});
const linkEl = createEl("a", {
text: item.label,
href: "#",
parent: listItem,
cls: "label",
});
linkEl.onclick = (event) => {
event.preventDefault();
if (item.open) {
item.open(event);
}
};
});
}