@rsalianto/git-heatmap-angular 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ import * as _rsalianto_git_heatmap_core from '@rsalianto/git-heatmap-core';
2
+ import { HeatmapData, LevelConfig, HeatmapTheme, HeatmapDay } from '@rsalianto/git-heatmap-core';
3
+ import { OnInit, OnChanges, EventEmitter, SimpleChanges } from '@angular/core';
4
+
5
+ declare class GitHeatmapComponent implements OnInit, OnChanges {
6
+ data?: HeatmapData;
7
+ apiUrl?: string;
8
+ fetchData?: () => Promise<HeatmapData>;
9
+ levels: LevelConfig[];
10
+ cellSize: number;
11
+ cellGap: number;
12
+ cellRadius: number;
13
+ showTotal: boolean;
14
+ showLegend: boolean;
15
+ showMonthLabels: boolean;
16
+ showDayLabels: boolean;
17
+ theme: Partial<HeatmapTheme>;
18
+ label: string;
19
+ dayClick: EventEmitter<HeatmapDay>;
20
+ calendarData: HeatmapData | null;
21
+ status: "idle" | "loading" | "success" | "error";
22
+ error: Error | null;
23
+ tappedDay: HeatmapDay | null;
24
+ tooltip: {
25
+ day: HeatmapDay;
26
+ x: number;
27
+ y: number;
28
+ } | null;
29
+ get step(): number;
30
+ get skeletonCols(): unknown[];
31
+ get skeletonRows(): unknown[];
32
+ get dayLabels(): Record<number, string>;
33
+ get cssVars(): Record<string, string>;
34
+ get monthLabels(): _rsalianto_git_heatmap_core.MonthLabel[];
35
+ private cdr;
36
+ private el;
37
+ private renderer;
38
+ ngOnInit(): void;
39
+ ngOnChanges(changes: SimpleChanges): void;
40
+ private load;
41
+ formatTooltip(day: HeatmapDay): string;
42
+ onMouseEnter(e: MouseEvent, day: HeatmapDay): void;
43
+ onCellClick(day: HeatmapDay): void;
44
+ private injectStyles;
45
+ }
46
+
47
+ export { GitHeatmapComponent };
@@ -0,0 +1,47 @@
1
+ import * as _rsalianto_git_heatmap_core from '@rsalianto/git-heatmap-core';
2
+ import { HeatmapData, LevelConfig, HeatmapTheme, HeatmapDay } from '@rsalianto/git-heatmap-core';
3
+ import { OnInit, OnChanges, EventEmitter, SimpleChanges } from '@angular/core';
4
+
5
+ declare class GitHeatmapComponent implements OnInit, OnChanges {
6
+ data?: HeatmapData;
7
+ apiUrl?: string;
8
+ fetchData?: () => Promise<HeatmapData>;
9
+ levels: LevelConfig[];
10
+ cellSize: number;
11
+ cellGap: number;
12
+ cellRadius: number;
13
+ showTotal: boolean;
14
+ showLegend: boolean;
15
+ showMonthLabels: boolean;
16
+ showDayLabels: boolean;
17
+ theme: Partial<HeatmapTheme>;
18
+ label: string;
19
+ dayClick: EventEmitter<HeatmapDay>;
20
+ calendarData: HeatmapData | null;
21
+ status: "idle" | "loading" | "success" | "error";
22
+ error: Error | null;
23
+ tappedDay: HeatmapDay | null;
24
+ tooltip: {
25
+ day: HeatmapDay;
26
+ x: number;
27
+ y: number;
28
+ } | null;
29
+ get step(): number;
30
+ get skeletonCols(): unknown[];
31
+ get skeletonRows(): unknown[];
32
+ get dayLabels(): Record<number, string>;
33
+ get cssVars(): Record<string, string>;
34
+ get monthLabels(): _rsalianto_git_heatmap_core.MonthLabel[];
35
+ private cdr;
36
+ private el;
37
+ private renderer;
38
+ ngOnInit(): void;
39
+ ngOnChanges(changes: SimpleChanges): void;
40
+ private load;
41
+ formatTooltip(day: HeatmapDay): string;
42
+ onMouseEnter(e: MouseEvent, day: HeatmapDay): void;
43
+ onCellClick(day: HeatmapDay): void;
44
+ private injectStyles;
45
+ }
46
+
47
+ export { GitHeatmapComponent };
package/dist/index.js ADDED
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
22
+ if (decorator = decorators[i])
23
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
24
+ if (kind && result) __defProp(target, key, result);
25
+ return result;
26
+ };
27
+
28
+ // src/index.ts
29
+ var index_exports = {};
30
+ __export(index_exports, {
31
+ GitHeatmapComponent: () => GitHeatmapComponent
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/lib/git-heatmap.component.ts
36
+ var import_core = require("@angular/core");
37
+ var import_common = require("@angular/common");
38
+ var import_git_heatmap_core = require("@rsalianto/git-heatmap-core");
39
+ var GitHeatmapComponent = class {
40
+ constructor() {
41
+ this.levels = import_git_heatmap_core.DEFAULT_LEVELS;
42
+ this.cellSize = import_git_heatmap_core.CELL;
43
+ this.cellGap = import_git_heatmap_core.GAP;
44
+ this.cellRadius = 2;
45
+ this.showTotal = true;
46
+ this.showLegend = true;
47
+ this.showMonthLabels = true;
48
+ this.showDayLabels = true;
49
+ this.theme = {};
50
+ this.label = "Contribution heatmap";
51
+ this.dayClick = new import_core.EventEmitter();
52
+ this.calendarData = null;
53
+ this.status = "idle";
54
+ this.error = null;
55
+ this.tappedDay = null;
56
+ this.tooltip = null;
57
+ this.cdr = (0, import_core.inject)(import_core.ChangeDetectorRef);
58
+ this.el = (0, import_core.inject)(import_core.ElementRef);
59
+ this.renderer = (0, import_core.inject)(import_core.Renderer2);
60
+ }
61
+ get step() {
62
+ return this.cellSize + this.cellGap;
63
+ }
64
+ get skeletonCols() {
65
+ return Array.from({ length: 53 });
66
+ }
67
+ get skeletonRows() {
68
+ return Array.from({ length: 7 });
69
+ }
70
+ get dayLabels() {
71
+ return import_git_heatmap_core.DAY_LABELS;
72
+ }
73
+ get cssVars() {
74
+ const t = { ...import_git_heatmap_core.DEFAULT_THEME, ...this.theme };
75
+ return {
76
+ "--ghm-color-l0": t.colorL0,
77
+ "--ghm-color-l1": t.colorL1,
78
+ "--ghm-color-l2": t.colorL2,
79
+ "--ghm-color-l3": t.colorL3,
80
+ "--ghm-color-l4": t.colorL4,
81
+ "--ghm-text": t.textColor,
82
+ "--ghm-tooltip-bg": t.tooltipBg,
83
+ "--ghm-tooltip-border": t.tooltipBorderColor,
84
+ "--ghm-tooltip-text": t.tooltipTextColor,
85
+ "--ghm-font": t.fontFamily,
86
+ "--ghm-fs": t.fontSize
87
+ };
88
+ }
89
+ get monthLabels() {
90
+ return this.showMonthLabels && this.calendarData ? (0, import_git_heatmap_core.buildMonthLabels)(this.calendarData.weeks) : [];
91
+ }
92
+ ngOnInit() {
93
+ this.injectStyles();
94
+ this.load();
95
+ }
96
+ ngOnChanges(changes) {
97
+ if (changes["apiUrl"] || changes["data"]) this.load();
98
+ }
99
+ async load() {
100
+ if (this.data) {
101
+ this.calendarData = this.data;
102
+ this.status = "success";
103
+ this.cdr.markForCheck();
104
+ return;
105
+ }
106
+ const resolver = this.fetchData ?? (this.apiUrl ? () => fetch(this.apiUrl).then((r) => r.json()) : null);
107
+ if (!resolver) return;
108
+ this.status = "loading";
109
+ this.cdr.markForCheck();
110
+ try {
111
+ this.calendarData = await resolver();
112
+ this.status = "success";
113
+ } catch (e) {
114
+ this.error = e instanceof Error ? e : new Error(String(e));
115
+ this.status = "error";
116
+ }
117
+ this.cdr.markForCheck();
118
+ }
119
+ formatTooltip(day) {
120
+ return day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
121
+ }
122
+ onMouseEnter(e, day) {
123
+ const target = e.currentTarget;
124
+ const r = target.getBoundingClientRect();
125
+ const wr = this.el.nativeElement.getBoundingClientRect();
126
+ this.tooltip = { day, x: r.left - wr.left + this.cellSize / 2, y: r.top - wr.top };
127
+ this.cdr.markForCheck();
128
+ }
129
+ onCellClick(day) {
130
+ this.tappedDay = this.tappedDay?.date === day.date ? null : day;
131
+ this.dayClick.emit(day);
132
+ this.cdr.markForCheck();
133
+ }
134
+ injectStyles() {
135
+ if (typeof document === "undefined") return;
136
+ if (document.getElementById("ghm-style")) return;
137
+ const style = this.renderer.createElement("style");
138
+ style.id = "ghm-style";
139
+ style.textContent = `
140
+ [data-git-heatmap] {
141
+ display:block; position:relative; width:100%;
142
+ font-size:var(--ghm-fs); font-family:var(--ghm-font);
143
+ color:var(--ghm-text); user-select:none; box-sizing:border-box;
144
+ }
145
+ [data-git-heatmap] * { box-sizing:border-box; }
146
+ `;
147
+ this.renderer.appendChild(document.head, style);
148
+ }
149
+ };
150
+ __decorateClass([
151
+ (0, import_core.Input)()
152
+ ], GitHeatmapComponent.prototype, "data", 2);
153
+ __decorateClass([
154
+ (0, import_core.Input)()
155
+ ], GitHeatmapComponent.prototype, "apiUrl", 2);
156
+ __decorateClass([
157
+ (0, import_core.Input)()
158
+ ], GitHeatmapComponent.prototype, "fetchData", 2);
159
+ __decorateClass([
160
+ (0, import_core.Input)()
161
+ ], GitHeatmapComponent.prototype, "levels", 2);
162
+ __decorateClass([
163
+ (0, import_core.Input)()
164
+ ], GitHeatmapComponent.prototype, "cellSize", 2);
165
+ __decorateClass([
166
+ (0, import_core.Input)()
167
+ ], GitHeatmapComponent.prototype, "cellGap", 2);
168
+ __decorateClass([
169
+ (0, import_core.Input)()
170
+ ], GitHeatmapComponent.prototype, "cellRadius", 2);
171
+ __decorateClass([
172
+ (0, import_core.Input)()
173
+ ], GitHeatmapComponent.prototype, "showTotal", 2);
174
+ __decorateClass([
175
+ (0, import_core.Input)()
176
+ ], GitHeatmapComponent.prototype, "showLegend", 2);
177
+ __decorateClass([
178
+ (0, import_core.Input)()
179
+ ], GitHeatmapComponent.prototype, "showMonthLabels", 2);
180
+ __decorateClass([
181
+ (0, import_core.Input)()
182
+ ], GitHeatmapComponent.prototype, "showDayLabels", 2);
183
+ __decorateClass([
184
+ (0, import_core.Input)()
185
+ ], GitHeatmapComponent.prototype, "theme", 2);
186
+ __decorateClass([
187
+ (0, import_core.Input)()
188
+ ], GitHeatmapComponent.prototype, "label", 2);
189
+ __decorateClass([
190
+ (0, import_core.Output)()
191
+ ], GitHeatmapComponent.prototype, "dayClick", 2);
192
+ GitHeatmapComponent = __decorateClass([
193
+ (0, import_core.Component)({
194
+ selector: "git-heatmap",
195
+ standalone: true,
196
+ imports: [import_common.NgFor, import_common.NgIf, import_common.NgStyle],
197
+ encapsulation: import_core.ViewEncapsulation.None,
198
+ changeDetection: import_core.ChangeDetectionStrategy.OnPush,
199
+ template: `
200
+ <div class="ghm-wrapper" [attr.data-git-heatmap]="''" [ngStyle]="cssVars" [attr.aria-label]="label">
201
+
202
+ <ng-container *ngIf="error">
203
+ <p style="opacity:0.6; padding:1rem 0;">Could not load contribution data.</p>
204
+ </ng-container>
205
+
206
+ <ng-container *ngIf="!error && (status === 'loading' || status === 'idle')">
207
+ <div *ngIf="showTotal" style="height:16px;width:160px;background:var(--ghm-text);border-radius:4px;margin-bottom:12px;opacity:0.2;"></div>
208
+ <div [ngStyle]="{ display:'flex', gap: cellGap + 'px', opacity: 0.4 }">
209
+ <div *ngFor="let _ of skeletonCols" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }">
210
+ <div *ngFor="let __ of skeletonRows" [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l0)' }"></div>
211
+ </div>
212
+ </div>
213
+ </ng-container>
214
+
215
+ <ng-container *ngIf="!error && status === 'success' && calendarData">
216
+ <p *ngIf="showTotal" style="margin-bottom:12px;color:var(--ghm-text);">
217
+ {{ calendarData.totalContributions.toLocaleString() }} contributions in the last year
218
+ </p>
219
+
220
+ <div #scrollRef style="overflow-x:auto;overflow-y:visible;padding-bottom:4px;">
221
+ <div [ngStyle]="{ display:'inline-flex', flexDirection:'column', minWidth: (calendarData.weeks.length * step) + 'px' }">
222
+
223
+ <div *ngIf="showMonthLabels" [ngStyle]="{ display:'flex', marginBottom: cellGap + 'px', marginLeft: showDayLabels ? '32px' : '0' }">
224
+ <div *ngFor="let ml of monthLabels; let idx = index"
225
+ [ngStyle]="{ width: ((monthLabels[idx+1]?.col ?? calendarData.weeks.length) - ml.col) * step + 'px', whiteSpace:'nowrap', fontSize:'0.9em' }">
226
+ {{ ml.label }}
227
+ </div>
228
+ </div>
229
+
230
+ <div style="display:flex;">
231
+ <div *ngIf="showDayLabels" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px', marginRight:'6px' }">
232
+ <div *ngFor="let dow of [0,1,2,3,4,5,6]"
233
+ [ngStyle]="{ height: cellSize + 'px', lineHeight: cellSize + 'px', width:'26px', textAlign:'right', paddingRight:'4px', fontSize:'0.9em' }">
234
+ {{ dayLabels[dow] || '' }}
235
+ </div>
236
+ </div>
237
+
238
+ <div [ngStyle]="{ display:'flex', gap: cellGap + 'px' }">
239
+ <div *ngFor="let week of calendarData.weeks; let wi = index" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }">
240
+ <ng-container *ngFor="let dow of [0,1,2,3,4,5,6]">
241
+ <div *ngIf="week.days[dow]?.date; else emptyCell"
242
+ [ngStyle]="{
243
+ width: cellSize + 'px', height: cellSize + 'px',
244
+ borderRadius: cellRadius + 'px',
245
+ background: 'var(--ghm-color-l' + week.days[dow].level + ')',
246
+ cursor: 'pointer', transition: 'opacity 0.15s'
247
+ }"
248
+ [attr.aria-label]="formatTooltip(week.days[dow])"
249
+ role="button"
250
+ tabindex="0"
251
+ (mouseenter)="onMouseEnter($event, week.days[dow])"
252
+ (mouseleave)="tooltip = null; cdr.markForCheck()"
253
+ (mouseover)="$any($event.currentTarget).style.opacity='0.7'"
254
+ (mouseout)="$any($event.currentTarget).style.opacity='1'"
255
+ (click)="onCellClick(week.days[dow])">
256
+ </div>
257
+ <ng-template #emptyCell>
258
+ <div [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px' }"></div>
259
+ </ng-template>
260
+ </ng-container>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+ </div>
266
+
267
+ <div *ngIf="tappedDay" style="margin-top:8px;font-size:0.9em;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:4px;padding:6px 12px;">
268
+ {{ formatTooltip(tappedDay) }}
269
+ </div>
270
+
271
+ <div *ngIf="showLegend" style="display:flex;align-items:center;justify-content:flex-end;margin-top:8px;">
272
+ <div style="display:flex;align-items:center;gap:6px;font-size:0.9em;">
273
+ <span>Less</span>
274
+ <div *ngFor="let lvl of levels; let i = index"
275
+ [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l' + i + ')' }"
276
+ [attr.title]="lvl.label">
277
+ </div>
278
+ <span>More</span>
279
+ </div>
280
+ </div>
281
+
282
+ <div *ngIf="tooltip" [ngStyle]="{
283
+ pointerEvents:'none', position:'absolute', zIndex:50,
284
+ left: tooltip.x + 'px', top: (tooltip.y - 6) + 'px',
285
+ transform:'translate(-50%,-100%)',
286
+ background:'var(--ghm-tooltip-bg)',
287
+ border:'1px solid var(--ghm-tooltip-border)',
288
+ color:'var(--ghm-tooltip-text)',
289
+ fontSize:'0.9em', borderRadius:'4px', padding:'3px 8px', whiteSpace:'nowrap'
290
+ }">
291
+ {{ formatTooltip(tooltip.day) }}
292
+ </div>
293
+ </ng-container>
294
+
295
+ </div>
296
+ `
297
+ })
298
+ ], GitHeatmapComponent);
299
+ // Annotate the CommonJS export names for ESM import in node:
300
+ 0 && (module.exports = {
301
+ GitHeatmapComponent
302
+ });
303
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/git-heatmap.component.ts"],"sourcesContent":["export { GitHeatmapComponent } from \"./lib/git-heatmap.component\";\n","import {\n Component, Input, Output, EventEmitter,\n OnInit, OnChanges, SimpleChanges,\n ChangeDetectionStrategy, ChangeDetectorRef,\n ViewEncapsulation, ElementRef, Renderer2,\n inject,\n} from \"@angular/core\";\nimport { NgFor, NgIf, NgStyle } from \"@angular/common\";\nimport {\n buildMonthLabels, CELL, GAP, STEP, DAY_LABELS,\n DEFAULT_LEVELS, DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\n\n@Component({\n selector: \"git-heatmap\",\n standalone: true,\n imports: [NgFor, NgIf, NgStyle],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"ghm-wrapper\" [attr.data-git-heatmap]=\"''\" [ngStyle]=\"cssVars\" [attr.aria-label]=\"label\">\n\n <ng-container *ngIf=\"error\">\n <p style=\"opacity:0.6; padding:1rem 0;\">Could not load contribution data.</p>\n </ng-container>\n\n <ng-container *ngIf=\"!error && (status === 'loading' || status === 'idle')\">\n <div *ngIf=\"showTotal\" style=\"height:16px;width:160px;background:var(--ghm-text);border-radius:4px;margin-bottom:12px;opacity:0.2;\"></div>\n <div [ngStyle]=\"{ display:'flex', gap: cellGap + 'px', opacity: 0.4 }\">\n <div *ngFor=\"let _ of skeletonCols\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }\">\n <div *ngFor=\"let __ of skeletonRows\" [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l0)' }\"></div>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!error && status === 'success' && calendarData\">\n <p *ngIf=\"showTotal\" style=\"margin-bottom:12px;color:var(--ghm-text);\">\n {{ calendarData.totalContributions.toLocaleString() }} contributions in the last year\n </p>\n\n <div #scrollRef style=\"overflow-x:auto;overflow-y:visible;padding-bottom:4px;\">\n <div [ngStyle]=\"{ display:'inline-flex', flexDirection:'column', minWidth: (calendarData.weeks.length * step) + 'px' }\">\n\n <div *ngIf=\"showMonthLabels\" [ngStyle]=\"{ display:'flex', marginBottom: cellGap + 'px', marginLeft: showDayLabels ? '32px' : '0' }\">\n <div *ngFor=\"let ml of monthLabels; let idx = index\"\n [ngStyle]=\"{ width: ((monthLabels[idx+1]?.col ?? calendarData.weeks.length) - ml.col) * step + 'px', whiteSpace:'nowrap', fontSize:'0.9em' }\">\n {{ ml.label }}\n </div>\n </div>\n\n <div style=\"display:flex;\">\n <div *ngIf=\"showDayLabels\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px', marginRight:'6px' }\">\n <div *ngFor=\"let dow of [0,1,2,3,4,5,6]\"\n [ngStyle]=\"{ height: cellSize + 'px', lineHeight: cellSize + 'px', width:'26px', textAlign:'right', paddingRight:'4px', fontSize:'0.9em' }\">\n {{ dayLabels[dow] || '' }}\n </div>\n </div>\n\n <div [ngStyle]=\"{ display:'flex', gap: cellGap + 'px' }\">\n <div *ngFor=\"let week of calendarData.weeks; let wi = index\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }\">\n <ng-container *ngFor=\"let dow of [0,1,2,3,4,5,6]\">\n <div *ngIf=\"week.days[dow]?.date; else emptyCell\"\n [ngStyle]=\"{\n width: cellSize + 'px', height: cellSize + 'px',\n borderRadius: cellRadius + 'px',\n background: 'var(--ghm-color-l' + week.days[dow].level + ')',\n cursor: 'pointer', transition: 'opacity 0.15s'\n }\"\n [attr.aria-label]=\"formatTooltip(week.days[dow])\"\n role=\"button\"\n tabindex=\"0\"\n (mouseenter)=\"onMouseEnter($event, week.days[dow])\"\n (mouseleave)=\"tooltip = null; cdr.markForCheck()\"\n (mouseover)=\"$any($event.currentTarget).style.opacity='0.7'\"\n (mouseout)=\"$any($event.currentTarget).style.opacity='1'\"\n (click)=\"onCellClick(week.days[dow])\">\n </div>\n <ng-template #emptyCell>\n <div [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px' }\"></div>\n </ng-template>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"tappedDay\" style=\"margin-top:8px;font-size:0.9em;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:4px;padding:6px 12px;\">\n {{ formatTooltip(tappedDay) }}\n </div>\n\n <div *ngIf=\"showLegend\" style=\"display:flex;align-items:center;justify-content:flex-end;margin-top:8px;\">\n <div style=\"display:flex;align-items:center;gap:6px;font-size:0.9em;\">\n <span>Less</span>\n <div *ngFor=\"let lvl of levels; let i = index\"\n [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l' + i + ')' }\"\n [attr.title]=\"lvl.label\">\n </div>\n <span>More</span>\n </div>\n </div>\n\n <div *ngIf=\"tooltip\" [ngStyle]=\"{\n pointerEvents:'none', position:'absolute', zIndex:50,\n left: tooltip.x + 'px', top: (tooltip.y - 6) + 'px',\n transform:'translate(-50%,-100%)',\n background:'var(--ghm-tooltip-bg)',\n border:'1px solid var(--ghm-tooltip-border)',\n color:'var(--ghm-tooltip-text)',\n fontSize:'0.9em', borderRadius:'4px', padding:'3px 8px', whiteSpace:'nowrap'\n }\">\n {{ formatTooltip(tooltip.day) }}\n </div>\n </ng-container>\n\n </div>\n `,\n})\nexport class GitHeatmapComponent implements OnInit, OnChanges {\n @Input() data?: HeatmapData;\n @Input() apiUrl?: string;\n @Input() fetchData?: () => Promise<HeatmapData>;\n @Input() levels: LevelConfig[] = DEFAULT_LEVELS;\n @Input() cellSize = CELL;\n @Input() cellGap = GAP;\n @Input() cellRadius = 2;\n @Input() showTotal = true;\n @Input() showLegend = true;\n @Input() showMonthLabels = true;\n @Input() showDayLabels = true;\n @Input() theme: Partial<HeatmapTheme> = {};\n @Input() label = \"Contribution heatmap\";\n @Output() dayClick = new EventEmitter<HeatmapDay>();\n\n calendarData: HeatmapData | null = null;\n status: \"idle\" | \"loading\" | \"success\" | \"error\" = \"idle\";\n error: Error | null = null;\n tappedDay: HeatmapDay | null = null;\n tooltip: { day: HeatmapDay; x: number; y: number } | null = null;\n\n get step() { return this.cellSize + this.cellGap; }\n get skeletonCols() { return Array.from({ length: 53 }); }\n get skeletonRows() { return Array.from({ length: 7 }); }\n get dayLabels() { return DAY_LABELS; }\n\n get cssVars(): Record<string, string> {\n const t: HeatmapTheme = { ...DEFAULT_THEME, ...this.theme };\n return {\n \"--ghm-color-l0\": t.colorL0, \"--ghm-color-l1\": t.colorL1,\n \"--ghm-color-l2\": t.colorL2, \"--ghm-color-l3\": t.colorL3,\n \"--ghm-color-l4\": t.colorL4, \"--ghm-text\": t.textColor,\n \"--ghm-tooltip-bg\": t.tooltipBg, \"--ghm-tooltip-border\": t.tooltipBorderColor,\n \"--ghm-tooltip-text\": t.tooltipTextColor, \"--ghm-font\": t.fontFamily,\n \"--ghm-fs\": t.fontSize,\n };\n }\n\n get monthLabels() {\n return this.showMonthLabels && this.calendarData ? buildMonthLabels(this.calendarData.weeks) : [];\n }\n\n private cdr = inject(ChangeDetectorRef);\n private el = inject(ElementRef);\n private renderer = inject(Renderer2);\n\n ngOnInit() { this.injectStyles(); this.load(); }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes[\"apiUrl\"] || changes[\"data\"]) this.load();\n }\n\n private async load() {\n if (this.data) {\n this.calendarData = this.data;\n this.status = \"success\";\n this.cdr.markForCheck();\n return;\n }\n const resolver: (() => Promise<HeatmapData>) | null =\n this.fetchData ?? (this.apiUrl ? () => fetch(this.apiUrl!).then((r) => r.json()) : null);\n if (!resolver) return;\n\n this.status = \"loading\";\n this.cdr.markForCheck();\n try {\n this.calendarData = await resolver();\n this.status = \"success\";\n } catch (e: unknown) {\n this.error = e instanceof Error ? e : new Error(String(e));\n this.status = \"error\";\n }\n this.cdr.markForCheck();\n }\n\n formatTooltip(day: HeatmapDay): string {\n return day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n }\n\n onMouseEnter(e: MouseEvent, day: HeatmapDay) {\n const target = e.currentTarget as HTMLElement;\n const r = target.getBoundingClientRect();\n const wr = this.el.nativeElement.getBoundingClientRect();\n this.tooltip = { day, x: r.left - wr.left + this.cellSize / 2, y: r.top - wr.top };\n this.cdr.markForCheck();\n }\n\n onCellClick(day: HeatmapDay) {\n this.tappedDay = this.tappedDay?.date === day.date ? null : day;\n this.dayClick.emit(day);\n this.cdr.markForCheck();\n }\n\n private injectStyles() {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(\"ghm-style\")) return;\n const style = this.renderer.createElement(\"style\") as HTMLStyleElement;\n style.id = \"ghm-style\";\n style.textContent = `\n [data-git-heatmap] {\n display:block; position:relative; width:100%;\n font-size:var(--ghm-fs); font-family:var(--ghm-font);\n color:var(--ghm-text); user-select:none; box-sizing:border-box;\n }\n [data-git-heatmap] * { box-sizing:border-box; }\n `;\n this.renderer.appendChild(document.head, style);\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,297 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/lib/git-heatmap.component.ts
13
+ import {
14
+ Component,
15
+ Input,
16
+ Output,
17
+ EventEmitter,
18
+ ChangeDetectionStrategy,
19
+ ChangeDetectorRef,
20
+ ViewEncapsulation,
21
+ ElementRef,
22
+ Renderer2,
23
+ inject
24
+ } from "@angular/core";
25
+ import { NgFor, NgIf, NgStyle } from "@angular/common";
26
+ import {
27
+ buildMonthLabels,
28
+ CELL,
29
+ GAP,
30
+ DAY_LABELS,
31
+ DEFAULT_LEVELS,
32
+ DEFAULT_THEME
33
+ } from "@rsalianto/git-heatmap-core";
34
+ var GitHeatmapComponent = class {
35
+ constructor() {
36
+ this.levels = DEFAULT_LEVELS;
37
+ this.cellSize = CELL;
38
+ this.cellGap = GAP;
39
+ this.cellRadius = 2;
40
+ this.showTotal = true;
41
+ this.showLegend = true;
42
+ this.showMonthLabels = true;
43
+ this.showDayLabels = true;
44
+ this.theme = {};
45
+ this.label = "Contribution heatmap";
46
+ this.dayClick = new EventEmitter();
47
+ this.calendarData = null;
48
+ this.status = "idle";
49
+ this.error = null;
50
+ this.tappedDay = null;
51
+ this.tooltip = null;
52
+ this.cdr = inject(ChangeDetectorRef);
53
+ this.el = inject(ElementRef);
54
+ this.renderer = inject(Renderer2);
55
+ }
56
+ get step() {
57
+ return this.cellSize + this.cellGap;
58
+ }
59
+ get skeletonCols() {
60
+ return Array.from({ length: 53 });
61
+ }
62
+ get skeletonRows() {
63
+ return Array.from({ length: 7 });
64
+ }
65
+ get dayLabels() {
66
+ return DAY_LABELS;
67
+ }
68
+ get cssVars() {
69
+ const t = { ...DEFAULT_THEME, ...this.theme };
70
+ return {
71
+ "--ghm-color-l0": t.colorL0,
72
+ "--ghm-color-l1": t.colorL1,
73
+ "--ghm-color-l2": t.colorL2,
74
+ "--ghm-color-l3": t.colorL3,
75
+ "--ghm-color-l4": t.colorL4,
76
+ "--ghm-text": t.textColor,
77
+ "--ghm-tooltip-bg": t.tooltipBg,
78
+ "--ghm-tooltip-border": t.tooltipBorderColor,
79
+ "--ghm-tooltip-text": t.tooltipTextColor,
80
+ "--ghm-font": t.fontFamily,
81
+ "--ghm-fs": t.fontSize
82
+ };
83
+ }
84
+ get monthLabels() {
85
+ return this.showMonthLabels && this.calendarData ? buildMonthLabels(this.calendarData.weeks) : [];
86
+ }
87
+ ngOnInit() {
88
+ this.injectStyles();
89
+ this.load();
90
+ }
91
+ ngOnChanges(changes) {
92
+ if (changes["apiUrl"] || changes["data"]) this.load();
93
+ }
94
+ async load() {
95
+ if (this.data) {
96
+ this.calendarData = this.data;
97
+ this.status = "success";
98
+ this.cdr.markForCheck();
99
+ return;
100
+ }
101
+ const resolver = this.fetchData ?? (this.apiUrl ? () => fetch(this.apiUrl).then((r) => r.json()) : null);
102
+ if (!resolver) return;
103
+ this.status = "loading";
104
+ this.cdr.markForCheck();
105
+ try {
106
+ this.calendarData = await resolver();
107
+ this.status = "success";
108
+ } catch (e) {
109
+ this.error = e instanceof Error ? e : new Error(String(e));
110
+ this.status = "error";
111
+ }
112
+ this.cdr.markForCheck();
113
+ }
114
+ formatTooltip(day) {
115
+ return day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
116
+ }
117
+ onMouseEnter(e, day) {
118
+ const target = e.currentTarget;
119
+ const r = target.getBoundingClientRect();
120
+ const wr = this.el.nativeElement.getBoundingClientRect();
121
+ this.tooltip = { day, x: r.left - wr.left + this.cellSize / 2, y: r.top - wr.top };
122
+ this.cdr.markForCheck();
123
+ }
124
+ onCellClick(day) {
125
+ this.tappedDay = this.tappedDay?.date === day.date ? null : day;
126
+ this.dayClick.emit(day);
127
+ this.cdr.markForCheck();
128
+ }
129
+ injectStyles() {
130
+ if (typeof document === "undefined") return;
131
+ if (document.getElementById("ghm-style")) return;
132
+ const style = this.renderer.createElement("style");
133
+ style.id = "ghm-style";
134
+ style.textContent = `
135
+ [data-git-heatmap] {
136
+ display:block; position:relative; width:100%;
137
+ font-size:var(--ghm-fs); font-family:var(--ghm-font);
138
+ color:var(--ghm-text); user-select:none; box-sizing:border-box;
139
+ }
140
+ [data-git-heatmap] * { box-sizing:border-box; }
141
+ `;
142
+ this.renderer.appendChild(document.head, style);
143
+ }
144
+ };
145
+ __decorateClass([
146
+ Input()
147
+ ], GitHeatmapComponent.prototype, "data", 2);
148
+ __decorateClass([
149
+ Input()
150
+ ], GitHeatmapComponent.prototype, "apiUrl", 2);
151
+ __decorateClass([
152
+ Input()
153
+ ], GitHeatmapComponent.prototype, "fetchData", 2);
154
+ __decorateClass([
155
+ Input()
156
+ ], GitHeatmapComponent.prototype, "levels", 2);
157
+ __decorateClass([
158
+ Input()
159
+ ], GitHeatmapComponent.prototype, "cellSize", 2);
160
+ __decorateClass([
161
+ Input()
162
+ ], GitHeatmapComponent.prototype, "cellGap", 2);
163
+ __decorateClass([
164
+ Input()
165
+ ], GitHeatmapComponent.prototype, "cellRadius", 2);
166
+ __decorateClass([
167
+ Input()
168
+ ], GitHeatmapComponent.prototype, "showTotal", 2);
169
+ __decorateClass([
170
+ Input()
171
+ ], GitHeatmapComponent.prototype, "showLegend", 2);
172
+ __decorateClass([
173
+ Input()
174
+ ], GitHeatmapComponent.prototype, "showMonthLabels", 2);
175
+ __decorateClass([
176
+ Input()
177
+ ], GitHeatmapComponent.prototype, "showDayLabels", 2);
178
+ __decorateClass([
179
+ Input()
180
+ ], GitHeatmapComponent.prototype, "theme", 2);
181
+ __decorateClass([
182
+ Input()
183
+ ], GitHeatmapComponent.prototype, "label", 2);
184
+ __decorateClass([
185
+ Output()
186
+ ], GitHeatmapComponent.prototype, "dayClick", 2);
187
+ GitHeatmapComponent = __decorateClass([
188
+ Component({
189
+ selector: "git-heatmap",
190
+ standalone: true,
191
+ imports: [NgFor, NgIf, NgStyle],
192
+ encapsulation: ViewEncapsulation.None,
193
+ changeDetection: ChangeDetectionStrategy.OnPush,
194
+ template: `
195
+ <div class="ghm-wrapper" [attr.data-git-heatmap]="''" [ngStyle]="cssVars" [attr.aria-label]="label">
196
+
197
+ <ng-container *ngIf="error">
198
+ <p style="opacity:0.6; padding:1rem 0;">Could not load contribution data.</p>
199
+ </ng-container>
200
+
201
+ <ng-container *ngIf="!error && (status === 'loading' || status === 'idle')">
202
+ <div *ngIf="showTotal" style="height:16px;width:160px;background:var(--ghm-text);border-radius:4px;margin-bottom:12px;opacity:0.2;"></div>
203
+ <div [ngStyle]="{ display:'flex', gap: cellGap + 'px', opacity: 0.4 }">
204
+ <div *ngFor="let _ of skeletonCols" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }">
205
+ <div *ngFor="let __ of skeletonRows" [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l0)' }"></div>
206
+ </div>
207
+ </div>
208
+ </ng-container>
209
+
210
+ <ng-container *ngIf="!error && status === 'success' && calendarData">
211
+ <p *ngIf="showTotal" style="margin-bottom:12px;color:var(--ghm-text);">
212
+ {{ calendarData.totalContributions.toLocaleString() }} contributions in the last year
213
+ </p>
214
+
215
+ <div #scrollRef style="overflow-x:auto;overflow-y:visible;padding-bottom:4px;">
216
+ <div [ngStyle]="{ display:'inline-flex', flexDirection:'column', minWidth: (calendarData.weeks.length * step) + 'px' }">
217
+
218
+ <div *ngIf="showMonthLabels" [ngStyle]="{ display:'flex', marginBottom: cellGap + 'px', marginLeft: showDayLabels ? '32px' : '0' }">
219
+ <div *ngFor="let ml of monthLabels; let idx = index"
220
+ [ngStyle]="{ width: ((monthLabels[idx+1]?.col ?? calendarData.weeks.length) - ml.col) * step + 'px', whiteSpace:'nowrap', fontSize:'0.9em' }">
221
+ {{ ml.label }}
222
+ </div>
223
+ </div>
224
+
225
+ <div style="display:flex;">
226
+ <div *ngIf="showDayLabels" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px', marginRight:'6px' }">
227
+ <div *ngFor="let dow of [0,1,2,3,4,5,6]"
228
+ [ngStyle]="{ height: cellSize + 'px', lineHeight: cellSize + 'px', width:'26px', textAlign:'right', paddingRight:'4px', fontSize:'0.9em' }">
229
+ {{ dayLabels[dow] || '' }}
230
+ </div>
231
+ </div>
232
+
233
+ <div [ngStyle]="{ display:'flex', gap: cellGap + 'px' }">
234
+ <div *ngFor="let week of calendarData.weeks; let wi = index" [ngStyle]="{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }">
235
+ <ng-container *ngFor="let dow of [0,1,2,3,4,5,6]">
236
+ <div *ngIf="week.days[dow]?.date; else emptyCell"
237
+ [ngStyle]="{
238
+ width: cellSize + 'px', height: cellSize + 'px',
239
+ borderRadius: cellRadius + 'px',
240
+ background: 'var(--ghm-color-l' + week.days[dow].level + ')',
241
+ cursor: 'pointer', transition: 'opacity 0.15s'
242
+ }"
243
+ [attr.aria-label]="formatTooltip(week.days[dow])"
244
+ role="button"
245
+ tabindex="0"
246
+ (mouseenter)="onMouseEnter($event, week.days[dow])"
247
+ (mouseleave)="tooltip = null; cdr.markForCheck()"
248
+ (mouseover)="$any($event.currentTarget).style.opacity='0.7'"
249
+ (mouseout)="$any($event.currentTarget).style.opacity='1'"
250
+ (click)="onCellClick(week.days[dow])">
251
+ </div>
252
+ <ng-template #emptyCell>
253
+ <div [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px' }"></div>
254
+ </ng-template>
255
+ </ng-container>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ </div>
261
+
262
+ <div *ngIf="tappedDay" style="margin-top:8px;font-size:0.9em;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:4px;padding:6px 12px;">
263
+ {{ formatTooltip(tappedDay) }}
264
+ </div>
265
+
266
+ <div *ngIf="showLegend" style="display:flex;align-items:center;justify-content:flex-end;margin-top:8px;">
267
+ <div style="display:flex;align-items:center;gap:6px;font-size:0.9em;">
268
+ <span>Less</span>
269
+ <div *ngFor="let lvl of levels; let i = index"
270
+ [ngStyle]="{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l' + i + ')' }"
271
+ [attr.title]="lvl.label">
272
+ </div>
273
+ <span>More</span>
274
+ </div>
275
+ </div>
276
+
277
+ <div *ngIf="tooltip" [ngStyle]="{
278
+ pointerEvents:'none', position:'absolute', zIndex:50,
279
+ left: tooltip.x + 'px', top: (tooltip.y - 6) + 'px',
280
+ transform:'translate(-50%,-100%)',
281
+ background:'var(--ghm-tooltip-bg)',
282
+ border:'1px solid var(--ghm-tooltip-border)',
283
+ color:'var(--ghm-tooltip-text)',
284
+ fontSize:'0.9em', borderRadius:'4px', padding:'3px 8px', whiteSpace:'nowrap'
285
+ }">
286
+ {{ formatTooltip(tooltip.day) }}
287
+ </div>
288
+ </ng-container>
289
+
290
+ </div>
291
+ `
292
+ })
293
+ ], GitHeatmapComponent);
294
+ export {
295
+ GitHeatmapComponent
296
+ };
297
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/git-heatmap.component.ts"],"sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n OnInit, OnChanges, SimpleChanges,\n ChangeDetectionStrategy, ChangeDetectorRef,\n ViewEncapsulation, ElementRef, Renderer2,\n inject,\n} from \"@angular/core\";\nimport { NgFor, NgIf, NgStyle } from \"@angular/common\";\nimport {\n buildMonthLabels, CELL, GAP, STEP, DAY_LABELS,\n DEFAULT_LEVELS, DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\n\n@Component({\n selector: \"git-heatmap\",\n standalone: true,\n imports: [NgFor, NgIf, NgStyle],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"ghm-wrapper\" [attr.data-git-heatmap]=\"''\" [ngStyle]=\"cssVars\" [attr.aria-label]=\"label\">\n\n <ng-container *ngIf=\"error\">\n <p style=\"opacity:0.6; padding:1rem 0;\">Could not load contribution data.</p>\n </ng-container>\n\n <ng-container *ngIf=\"!error && (status === 'loading' || status === 'idle')\">\n <div *ngIf=\"showTotal\" style=\"height:16px;width:160px;background:var(--ghm-text);border-radius:4px;margin-bottom:12px;opacity:0.2;\"></div>\n <div [ngStyle]=\"{ display:'flex', gap: cellGap + 'px', opacity: 0.4 }\">\n <div *ngFor=\"let _ of skeletonCols\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }\">\n <div *ngFor=\"let __ of skeletonRows\" [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l0)' }\"></div>\n </div>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"!error && status === 'success' && calendarData\">\n <p *ngIf=\"showTotal\" style=\"margin-bottom:12px;color:var(--ghm-text);\">\n {{ calendarData.totalContributions.toLocaleString() }} contributions in the last year\n </p>\n\n <div #scrollRef style=\"overflow-x:auto;overflow-y:visible;padding-bottom:4px;\">\n <div [ngStyle]=\"{ display:'inline-flex', flexDirection:'column', minWidth: (calendarData.weeks.length * step) + 'px' }\">\n\n <div *ngIf=\"showMonthLabels\" [ngStyle]=\"{ display:'flex', marginBottom: cellGap + 'px', marginLeft: showDayLabels ? '32px' : '0' }\">\n <div *ngFor=\"let ml of monthLabels; let idx = index\"\n [ngStyle]=\"{ width: ((monthLabels[idx+1]?.col ?? calendarData.weeks.length) - ml.col) * step + 'px', whiteSpace:'nowrap', fontSize:'0.9em' }\">\n {{ ml.label }}\n </div>\n </div>\n\n <div style=\"display:flex;\">\n <div *ngIf=\"showDayLabels\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px', marginRight:'6px' }\">\n <div *ngFor=\"let dow of [0,1,2,3,4,5,6]\"\n [ngStyle]=\"{ height: cellSize + 'px', lineHeight: cellSize + 'px', width:'26px', textAlign:'right', paddingRight:'4px', fontSize:'0.9em' }\">\n {{ dayLabels[dow] || '' }}\n </div>\n </div>\n\n <div [ngStyle]=\"{ display:'flex', gap: cellGap + 'px' }\">\n <div *ngFor=\"let week of calendarData.weeks; let wi = index\" [ngStyle]=\"{ display:'flex', flexDirection:'column', gap: cellGap + 'px' }\">\n <ng-container *ngFor=\"let dow of [0,1,2,3,4,5,6]\">\n <div *ngIf=\"week.days[dow]?.date; else emptyCell\"\n [ngStyle]=\"{\n width: cellSize + 'px', height: cellSize + 'px',\n borderRadius: cellRadius + 'px',\n background: 'var(--ghm-color-l' + week.days[dow].level + ')',\n cursor: 'pointer', transition: 'opacity 0.15s'\n }\"\n [attr.aria-label]=\"formatTooltip(week.days[dow])\"\n role=\"button\"\n tabindex=\"0\"\n (mouseenter)=\"onMouseEnter($event, week.days[dow])\"\n (mouseleave)=\"tooltip = null; cdr.markForCheck()\"\n (mouseover)=\"$any($event.currentTarget).style.opacity='0.7'\"\n (mouseout)=\"$any($event.currentTarget).style.opacity='1'\"\n (click)=\"onCellClick(week.days[dow])\">\n </div>\n <ng-template #emptyCell>\n <div [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px' }\"></div>\n </ng-template>\n </ng-container>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"tappedDay\" style=\"margin-top:8px;font-size:0.9em;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:4px;padding:6px 12px;\">\n {{ formatTooltip(tappedDay) }}\n </div>\n\n <div *ngIf=\"showLegend\" style=\"display:flex;align-items:center;justify-content:flex-end;margin-top:8px;\">\n <div style=\"display:flex;align-items:center;gap:6px;font-size:0.9em;\">\n <span>Less</span>\n <div *ngFor=\"let lvl of levels; let i = index\"\n [ngStyle]=\"{ width: cellSize + 'px', height: cellSize + 'px', borderRadius: cellRadius + 'px', background: 'var(--ghm-color-l' + i + ')' }\"\n [attr.title]=\"lvl.label\">\n </div>\n <span>More</span>\n </div>\n </div>\n\n <div *ngIf=\"tooltip\" [ngStyle]=\"{\n pointerEvents:'none', position:'absolute', zIndex:50,\n left: tooltip.x + 'px', top: (tooltip.y - 6) + 'px',\n transform:'translate(-50%,-100%)',\n background:'var(--ghm-tooltip-bg)',\n border:'1px solid var(--ghm-tooltip-border)',\n color:'var(--ghm-tooltip-text)',\n fontSize:'0.9em', borderRadius:'4px', padding:'3px 8px', whiteSpace:'nowrap'\n }\">\n {{ formatTooltip(tooltip.day) }}\n </div>\n </ng-container>\n\n </div>\n `,\n})\nexport class GitHeatmapComponent implements OnInit, OnChanges {\n @Input() data?: HeatmapData;\n @Input() apiUrl?: string;\n @Input() fetchData?: () => Promise<HeatmapData>;\n @Input() levels: LevelConfig[] = DEFAULT_LEVELS;\n @Input() cellSize = CELL;\n @Input() cellGap = GAP;\n @Input() cellRadius = 2;\n @Input() showTotal = true;\n @Input() showLegend = true;\n @Input() showMonthLabels = true;\n @Input() showDayLabels = true;\n @Input() theme: Partial<HeatmapTheme> = {};\n @Input() label = \"Contribution heatmap\";\n @Output() dayClick = new EventEmitter<HeatmapDay>();\n\n calendarData: HeatmapData | null = null;\n status: \"idle\" | \"loading\" | \"success\" | \"error\" = \"idle\";\n error: Error | null = null;\n tappedDay: HeatmapDay | null = null;\n tooltip: { day: HeatmapDay; x: number; y: number } | null = null;\n\n get step() { return this.cellSize + this.cellGap; }\n get skeletonCols() { return Array.from({ length: 53 }); }\n get skeletonRows() { return Array.from({ length: 7 }); }\n get dayLabels() { return DAY_LABELS; }\n\n get cssVars(): Record<string, string> {\n const t: HeatmapTheme = { ...DEFAULT_THEME, ...this.theme };\n return {\n \"--ghm-color-l0\": t.colorL0, \"--ghm-color-l1\": t.colorL1,\n \"--ghm-color-l2\": t.colorL2, \"--ghm-color-l3\": t.colorL3,\n \"--ghm-color-l4\": t.colorL4, \"--ghm-text\": t.textColor,\n \"--ghm-tooltip-bg\": t.tooltipBg, \"--ghm-tooltip-border\": t.tooltipBorderColor,\n \"--ghm-tooltip-text\": t.tooltipTextColor, \"--ghm-font\": t.fontFamily,\n \"--ghm-fs\": t.fontSize,\n };\n }\n\n get monthLabels() {\n return this.showMonthLabels && this.calendarData ? buildMonthLabels(this.calendarData.weeks) : [];\n }\n\n private cdr = inject(ChangeDetectorRef);\n private el = inject(ElementRef);\n private renderer = inject(Renderer2);\n\n ngOnInit() { this.injectStyles(); this.load(); }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes[\"apiUrl\"] || changes[\"data\"]) this.load();\n }\n\n private async load() {\n if (this.data) {\n this.calendarData = this.data;\n this.status = \"success\";\n this.cdr.markForCheck();\n return;\n }\n const resolver: (() => Promise<HeatmapData>) | null =\n this.fetchData ?? (this.apiUrl ? () => fetch(this.apiUrl!).then((r) => r.json()) : null);\n if (!resolver) return;\n\n this.status = \"loading\";\n this.cdr.markForCheck();\n try {\n this.calendarData = await resolver();\n this.status = \"success\";\n } catch (e: unknown) {\n this.error = e instanceof Error ? e : new Error(String(e));\n this.status = \"error\";\n }\n this.cdr.markForCheck();\n }\n\n formatTooltip(day: HeatmapDay): string {\n return day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n }\n\n onMouseEnter(e: MouseEvent, day: HeatmapDay) {\n const target = e.currentTarget as HTMLElement;\n const r = target.getBoundingClientRect();\n const wr = this.el.nativeElement.getBoundingClientRect();\n this.tooltip = { day, x: r.left - wr.left + this.cellSize / 2, y: r.top - wr.top };\n this.cdr.markForCheck();\n }\n\n onCellClick(day: HeatmapDay) {\n this.tappedDay = this.tappedDay?.date === day.date ? null : day;\n this.dayClick.emit(day);\n this.cdr.markForCheck();\n }\n\n private injectStyles() {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(\"ghm-style\")) return;\n const style = this.renderer.createElement(\"style\") as HTMLStyleElement;\n style.id = \"ghm-style\";\n style.textContent = `\n [data-git-heatmap] {\n display:block; position:relative; width:100%;\n font-size:var(--ghm-fs); font-family:var(--ghm-font);\n color:var(--ghm-text); user-select:none; box-sizing:border-box;\n }\n [data-git-heatmap] * { box-sizing:border-box; }\n `;\n this.renderer.appendChild(document.head, style);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA;AAAA,EACE;AAAA,EAAW;AAAA,EAAO;AAAA,EAAQ;AAAA,EAE1B;AAAA,EAAyB;AAAA,EACzB;AAAA,EAAmB;AAAA,EAAY;AAAA,EAC/B;AAAA,OACK;AACP,SAAS,OAAO,MAAM,eAAe;AACrC;AAAA,EACE;AAAA,EAAkB;AAAA,EAAM;AAAA,EAAW;AAAA,EACnC;AAAA,EAAgB;AAAA,OACX;AA4GA,IAAM,sBAAN,MAAuD;AAAA,EAAvD;AAII,kBAAwB;AACxB,oBAAW;AACX,mBAAU;AACV,sBAAa;AACb,qBAAY;AACZ,sBAAa;AACb,2BAAkB;AAClB,yBAAgB;AAChB,iBAA+B,CAAC;AAChC,iBAAQ;AACP,oBAAW,IAAI,aAAyB;AAElD,wBAAmC;AACnC,kBAAmD;AACnD,iBAAsB;AACtB,qBAA+B;AAC/B,mBAA4D;AAuB5D,SAAQ,MAAM,OAAO,iBAAiB;AACtC,SAAQ,KAAM,OAAO,UAAU;AAC/B,SAAQ,WAAW,OAAO,SAAS;AAAA;AAAA,EAvBnC,IAAI,OAAO;AAAE,WAAO,KAAK,WAAW,KAAK;AAAA,EAAS;AAAA,EAClD,IAAI,eAAe;AAAE,WAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC;AAAA,EAAG;AAAA,EACxD,IAAI,eAAe;AAAE,WAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC;AAAA,EAAG;AAAA,EACvD,IAAI,YAAY;AAAE,WAAO;AAAA,EAAY;AAAA,EAErC,IAAI,UAAkC;AACpC,UAAM,IAAkB,EAAE,GAAG,eAAe,GAAG,KAAK,MAAM;AAC1D,WAAO;AAAA,MACL,kBAAkB,EAAE;AAAA,MAAS,kBAAkB,EAAE;AAAA,MACjD,kBAAkB,EAAE;AAAA,MAAS,kBAAkB,EAAE;AAAA,MACjD,kBAAkB,EAAE;AAAA,MAAS,cAAc,EAAE;AAAA,MAC7C,oBAAoB,EAAE;AAAA,MAAW,wBAAwB,EAAE;AAAA,MAC3D,sBAAsB,EAAE;AAAA,MAAkB,cAAc,EAAE;AAAA,MAC1D,YAAY,EAAE;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,KAAK,mBAAmB,KAAK,eAAe,iBAAiB,KAAK,aAAa,KAAK,IAAI,CAAC;AAAA,EAClG;AAAA,EAMA,WAAW;AAAE,SAAK,aAAa;AAAG,SAAK,KAAK;AAAA,EAAG;AAAA,EAE/C,YAAY,SAAwB;AAClC,QAAI,QAAQ,QAAQ,KAAK,QAAQ,MAAM,EAAG,MAAK,KAAK;AAAA,EACtD;AAAA,EAEA,MAAc,OAAO;AACnB,QAAI,KAAK,MAAM;AACb,WAAK,eAAe,KAAK;AACzB,WAAK,SAAS;AACd,WAAK,IAAI,aAAa;AACtB;AAAA,IACF;AACA,UAAM,WACJ,KAAK,cAAc,KAAK,SAAS,MAAM,MAAM,KAAK,MAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI;AACrF,QAAI,CAAC,SAAU;AAEf,SAAK,SAAS;AACd,SAAK,IAAI,aAAa;AACtB,QAAI;AACF,WAAK,eAAe,MAAM,SAAS;AACnC,WAAK,SAAS;AAAA,IAChB,SAAS,GAAY;AACnB,WAAK,QAAQ,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AACzD,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,IAAI,aAAa;AAAA,EACxB;AAAA,EAEA,cAAc,KAAyB;AACrC,WAAO,IAAI,UAAU,IACjB,uBAAuB,IAAI,IAAI,KAC/B,GAAG,IAAI,KAAK,gBAAgB,IAAI,QAAQ,IAAI,MAAM,EAAE,OAAO,IAAI,IAAI;AAAA,EACzE;AAAA,EAEA,aAAa,GAAe,KAAiB;AAC3C,UAAM,SAAS,EAAE;AACjB,UAAM,IAAI,OAAO,sBAAsB;AACvC,UAAM,KAAK,KAAK,GAAG,cAAc,sBAAsB;AACvD,SAAK,UAAU,EAAE,KAAK,GAAG,EAAE,OAAO,GAAG,OAAO,KAAK,WAAW,GAAG,GAAG,EAAE,MAAM,GAAG,IAAI;AACjF,SAAK,IAAI,aAAa;AAAA,EACxB;AAAA,EAEA,YAAY,KAAiB;AAC3B,SAAK,YAAY,KAAK,WAAW,SAAS,IAAI,OAAO,OAAO;AAC5D,SAAK,SAAS,KAAK,GAAG;AACtB,SAAK,IAAI,aAAa;AAAA,EACxB;AAAA,EAEQ,eAAe;AACrB,QAAI,OAAO,aAAa,YAAa;AACrC,QAAI,SAAS,eAAe,WAAW,EAAG;AAC1C,UAAM,QAAQ,KAAK,SAAS,cAAc,OAAO;AACjD,UAAM,KAAK;AACX,UAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQpB,SAAK,SAAS,YAAY,SAAS,MAAM,KAAK;AAAA,EAChD;AACF;AA9GW;AAAA,EAAR,MAAM;AAAA,GADI,oBACF;AACA;AAAA,EAAR,MAAM;AAAA,GAFI,oBAEF;AACA;AAAA,EAAR,MAAM;AAAA,GAHI,oBAGF;AACA;AAAA,EAAR,MAAM;AAAA,GAJI,oBAIF;AACA;AAAA,EAAR,MAAM;AAAA,GALI,oBAKF;AACA;AAAA,EAAR,MAAM;AAAA,GANI,oBAMF;AACA;AAAA,EAAR,MAAM;AAAA,GAPI,oBAOF;AACA;AAAA,EAAR,MAAM;AAAA,GARI,oBAQF;AACA;AAAA,EAAR,MAAM;AAAA,GATI,oBASF;AACA;AAAA,EAAR,MAAM;AAAA,GAVI,oBAUF;AACA;AAAA,EAAR,MAAM;AAAA,GAXI,oBAWF;AACA;AAAA,EAAR,MAAM;AAAA,GAZI,oBAYF;AACA;AAAA,EAAR,MAAM;AAAA,GAbI,oBAaF;AACC;AAAA,EAAT,OAAO;AAAA,GAdG,oBAcD;AAdC,sBAAN;AAAA,EAzGN,UAAU;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS,CAAC,OAAO,MAAM,OAAO;AAAA,IAC9B,eAAe,kBAAkB;AAAA,IACjC,iBAAiB,wBAAwB;AAAA,IACzC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkGZ,CAAC;AAAA,GACY;","names":[]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@rsalianto/git-heatmap-angular",
3
+ "version": "0.1.0",
4
+ "description": "Angular component for git contribution heatmap",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": ["dist", "README.md"],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "dev": "tsup --watch",
20
+ "test": "vitest run",
21
+ "clean": "rm -rf dist"
22
+ },
23
+ "dependencies": {
24
+ "@rsalianto/git-heatmap-core": "*"
25
+ },
26
+ "peerDependencies": {
27
+ "@angular/common": ">=16.0.0",
28
+ "@angular/core": ">=16.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "*",
32
+ "typescript": "*",
33
+ "vitest": "*"
34
+ }
35
+ }