OcttKB/Wiki/plugins/echarts/$__plugins_Gk0Wk_echarts_addons_GitHubHeatMap.ts.js

200 lines
6.2 KiB
JavaScript

import { IScriptAddon } from '../../scriptAddon';
import * as ECharts from '$:/plugins/Gk0Wk/echarts/echarts.min.js';
const getFilterByDate = (date: string, subfilter: string) =>
`[all[tiddlers]sameday:created[${date}]][all[tiddlers]sameday:modified[${date}]] +${subfilter} +[sort[]]`;
const yearDates: Map<number, [string, string][]> = new Map();
const dayTime = 3600 * 24 * 1000;
const getData = (year: number, subfilter: string) => {
if (!yearDates.has(year)) {
const startDate = (ECharts as any).number
.parseDate(`${year}-01-01`)
.getTime();
const endDate = (ECharts as any).number
.parseDate(`${year + 1}-01-01`)
.getTime();
const dates: [string, string][] = [];
for (let time = startDate; time < endDate; time += dayTime) {
const timeFmt: string = (ECharts as any).format.formatTime(
'yyyy-MM-dd',
time,
);
const timeTW = timeFmt.replace(/-/g, '');
dates.push([timeFmt, timeTW]);
}
yearDates.set(year, dates);
}
let total = 0;
return [
yearDates.get(year)!.map(([timeFmt, timeTW]) => {
const count = $tw.wiki.filterTiddlers(
getFilterByDate(timeTW, subfilter),
).length;
total += count;
return [timeFmt, count];
}),
total,
] as [[string, number][], number];
};
const getPlatteColor = (name: string) =>
$tw.wiki.renderText(
'text/plain',
'text/vnd.tiddlywiki',
`<$transclude tiddler={{$:/palette}} index="${name}"><$transclude tiddler="$:/palettes/Vanilla" index="${name}"><$transclude tiddler="$:/config/DefaultColourMappings/${name}"/></$transclude></$transclude>`,
{},
);
const checkIfChinese = () =>
$tw.wiki.getTiddlerText('$:/language')?.includes('zh') === true;
const checkIfDarkMode = () =>
$tw.wiki.getTiddler($tw.wiki.getTiddlerText('$:/palette')!)?.fields?.[
'color-scheme'
] === 'dark';
const GitHubHeatMapAddon: IScriptAddon<any> = {
shouldUpdate: (_, changedTiddlers) => $tw.utils.count(changedTiddlers) > 0,
onUpdate: (myChart, _state, addonAttributes) => {
const year = parseInt(addonAttributes.year, 10) || new Date().getFullYear();
const subfilter = addonAttributes.subfilter || '[!is[shadow]!prefix[$:/]]';
const [data, total] = getData(year, subfilter);
const tooltipFormatter = (dateValue: string, count: number) => {
if (count === 0) {
return checkIfChinese()
? `${(ECharts as any).format.formatTime(
'yyyy年M月d日',
dateValue,
)} 无条目。`
: `${$tw.utils.formatDateString(
$tw.utils.parseDate(dateValue.replace(/-/g, ''))!,
'MMM DDD, YYYY',
)} no tiddler.`;
}
const p = $tw.utils.domMaker('p', {
text: checkIfChinese()
? `${(ECharts as any).format.formatTime(
'yyyy年M月d日',
dateValue,
)} 共有 ${count} 篇:`
: `${$tw.utils.formatDateString(
$tw.utils.parseDate(dateValue.replace(/-/g, ''))!,
'MMM DDD, YYYY',
)} ${count} tiddler${count > 1 ? 's' : ''}.`,
});
const ul = $tw.utils.domMaker('ul', {});
const tiddlers = $tw.wiki.filterTiddlers(
getFilterByDate(dateValue.replace(/-/g, ''), subfilter),
);
const len = tiddlers.length;
for (let i = 0; i < len; i++) {
const tiddler = tiddlers[i];
const li = $tw.utils.domMaker('li', {});
const a = $tw.utils.domMaker('a', {
text: tiddler,
class:
'tc-tiddlylink tc-tiddlylink-resolves tc-popup-handle tc-popup-absolute',
style: {
cursor: 'pointer',
},
});
// eslint-disable-next-line @typescript-eslint/no-loop-func
a.addEventListener('click', () =>
new $tw.Story().navigateTiddler(tiddler),
);
li.appendChild(a);
ul.appendChild(li);
}
return [p, ul];
};
let lastDateValue = '';
let lastCount = 0;
let cache: Element[] | string | undefined;
const cachedTooltipFormatter = ({
value: [dateValue, count],
}: {
value: [string, number];
}) => {
if (dateValue !== lastDateValue || count !== lastCount || !cache) {
cache = tooltipFormatter(dateValue, count);
lastDateValue = dateValue;
lastCount = count;
}
return cache;
};
myChart.setOption({
title: {
top: 0,
left: 'center',
text: checkIfChinese()
? `今年产出 ${total} 篇文章`
: `Produced ${total} tiddlers this year`,
},
tooltip: {
position: 'top',
formatter: cachedTooltipFormatter,
triggerOn: 'mousemove|click',
enterable: true,
hideDelay: 800,
backgroundColor: getPlatteColor('page-background'),
borderColor: getPlatteColor('very-muted-foreground'),
},
visualMap: {
type: 'piecewise',
orient: 'horizontal',
calculable: true,
showLabel: false,
right: 0,
top: 175,
pieces: [
// 设置分段范围
{ lte: 0, color: checkIfDarkMode() ? '#161B22' : '#EBEDF0' },
{ gt: 0, lte: 3, color: '#0E4429' },
{ gt: 3, lte: 7, color: '#006D32' },
{ gt: 7, lte: 15, color: '#26A641' },
{ gt: 15, color: '#39D353' },
],
},
calendar: {
top: 60,
left: 0,
right: 0,
cellSize: 15,
orient: 'horizontal',
range: year,
itemStyle: {
borderWidth: 3,
borderCap: 'round',
borderJoin: 'round',
borderColor: getPlatteColor('background'),
},
splitLine: {
show: false,
},
dayLabel: {
show: true,
nameMap: checkIfChinese() ? 'ZH' : 'EN',
},
monthLabel: {
show: true,
nameMap: checkIfChinese() ? 'ZH' : 'EN',
},
yearLabel: {
show: true,
position: 'bottom',
margin: 12,
verticalAlign: 'top',
},
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
calendarIndex: 0,
data,
},
} as any);
},
};
export default GitHubHeatMapAddon;