@pearpages/heatmap 0.0.2 → 0.1.2

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.
package/dist/index.css ADDED
@@ -0,0 +1,444 @@
1
+ /* src/ContributionHeatmap/index.scss */
2
+ .contribution-heatmap {
3
+ --day-size: 12px;
4
+ --day-gap: 2px;
5
+ --border-radius: 6px;
6
+ --transition: all 0.2s ease;
7
+ --shadow-color: rgba(0, 0, 0, 0.1);
8
+ --color-bg: #ffffff;
9
+ --color-border: #e1e4e8;
10
+ --color-text: #24292f;
11
+ --color-text-muted: #656d76;
12
+ --color-level-0: #ebedf0;
13
+ --color-level-1: #9be9a8;
14
+ --color-level-2: #40c463;
15
+ --color-level-3: #30a14e;
16
+ --color-level-4: #216e39;
17
+ }
18
+ @media (prefers-color-scheme: dark) {
19
+ .contribution-heatmap {
20
+ --color-bg: #0d1117;
21
+ --color-border: #30363d;
22
+ --color-text: #f0f6fc;
23
+ --color-text-muted: #8b949e;
24
+ --color-level-0: #161b22;
25
+ --color-level-1: #0e4429;
26
+ --color-level-2: #006d32;
27
+ --color-level-3: #26a641;
28
+ --color-level-4: #39d353;
29
+ }
30
+ }
31
+ .contribution-heatmap {
32
+ background: var(--color-bg);
33
+ border-radius: var(--border-radius);
34
+ padding: 20px;
35
+ box-shadow: 0 2px 8px var(--shadow-color);
36
+ border: 1px solid var(--color-border);
37
+ transition: var(--transition);
38
+ max-width: 100%;
39
+ overflow-x: auto;
40
+ }
41
+ .contribution-heatmap__month-header {
42
+ color: var(--color-text-muted);
43
+ }
44
+ .contribution-heatmap:hover {
45
+ box-shadow: 0 4px 16px var(--shadow-color);
46
+ }
47
+ @media (max-width: 768px) {
48
+ .contribution-heatmap {
49
+ --day-size: 10px;
50
+ --day-gap: 1px;
51
+ padding: 15px;
52
+ }
53
+ .contribution-heatmap__container {
54
+ padding: 0;
55
+ }
56
+ }
57
+ @media (max-width: 480px) {
58
+ .contribution-heatmap {
59
+ --day-size: 8px;
60
+ --day-gap: 1px;
61
+ padding: 10px;
62
+ }
63
+ }
64
+ .contribution-heatmap__header {
65
+ margin-bottom: 16px;
66
+ text-align: center;
67
+ }
68
+ @media (max-width: 768px) {
69
+ .contribution-heatmap__header {
70
+ margin-bottom: 12px;
71
+ }
72
+ }
73
+ .contribution-heatmap__title {
74
+ color: var(--color-text);
75
+ font-size: 18px;
76
+ font-weight: 600;
77
+ margin: 0 0 4px 0;
78
+ letter-spacing: -0.2px;
79
+ }
80
+ @media (max-width: 768px) {
81
+ .contribution-heatmap__title {
82
+ font-size: 16px;
83
+ }
84
+ }
85
+ .contribution-heatmap__subtitle {
86
+ color: var(--color-text-muted);
87
+ font-size: 14px;
88
+ margin: 0;
89
+ font-weight: 400;
90
+ }
91
+ @media (max-width: 768px) {
92
+ .contribution-heatmap__subtitle {
93
+ font-size: 12px;
94
+ }
95
+ }
96
+ .contribution-heatmap__container {
97
+ display: grid;
98
+ gap: 8px;
99
+ padding: 16px;
100
+ background: var(--color-bg);
101
+ border-radius: calc(var(--border-radius) - 2px);
102
+ position: relative;
103
+ grid-template-areas: ". months" "days grid";
104
+ grid-template-columns: 30px 1fr;
105
+ grid-template-rows: auto 1fr;
106
+ }
107
+ @media (max-width: 768px) {
108
+ .contribution-heatmap__container {
109
+ padding: 8px;
110
+ gap: 6px;
111
+ grid-template-columns: 20px 1fr;
112
+ }
113
+ }
114
+ .contribution-heatmap__months {
115
+ grid-area: months;
116
+ display: flex;
117
+ gap: var(--day-gap);
118
+ margin-bottom: 4px;
119
+ }
120
+ @media (max-width: 768px) {
121
+ .contribution-heatmap__months {
122
+ margin-bottom: 2px;
123
+ }
124
+ }
125
+ .contribution-heatmap__month {
126
+ color: var(--color-text-muted);
127
+ font-size: 12px;
128
+ font-weight: 500;
129
+ text-align: left;
130
+ padding: 0 2px;
131
+ flex-grow: 0;
132
+ flex-shrink: 0;
133
+ }
134
+ @media (max-width: 768px) {
135
+ .contribution-heatmap__month {
136
+ font-size: 10px;
137
+ }
138
+ }
139
+ @media (max-width: 480px) {
140
+ .contribution-heatmap__month {
141
+ font-size: 8px;
142
+ }
143
+ }
144
+ .contribution-heatmap__days {
145
+ grid-area: days;
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: var(--day-gap);
149
+ align-items: flex-end;
150
+ justify-content: flex-start;
151
+ padding-right: 4px;
152
+ }
153
+ @media (max-width: 768px) {
154
+ .contribution-heatmap__days {
155
+ padding-right: 2px;
156
+ }
157
+ }
158
+ .contribution-heatmap__day-label {
159
+ color: var(--color-text-muted);
160
+ font-size: 10px;
161
+ font-weight: 500;
162
+ height: var(--day-size);
163
+ display: flex;
164
+ align-items: center;
165
+ width: 24px;
166
+ }
167
+ @media (max-width: 768px) {
168
+ .contribution-heatmap__day-label {
169
+ font-size: 8px;
170
+ width: 16px;
171
+ }
172
+ }
173
+ @media (max-width: 480px) {
174
+ .contribution-heatmap__day-label {
175
+ font-size: 7px;
176
+ width: 12px;
177
+ }
178
+ }
179
+ .contribution-heatmap__grid {
180
+ grid-area: grid;
181
+ display: flex;
182
+ gap: var(--day-gap);
183
+ overflow-x: auto;
184
+ scroll-behavior: smooth;
185
+ }
186
+ .contribution-heatmap__grid::-webkit-scrollbar {
187
+ height: 4px;
188
+ }
189
+ .contribution-heatmap__grid::-webkit-scrollbar-track {
190
+ background: var(--color-border);
191
+ border-radius: 2px;
192
+ }
193
+ .contribution-heatmap__grid::-webkit-scrollbar-thumb {
194
+ background: var(--color-level-2);
195
+ border-radius: 2px;
196
+ }
197
+ .contribution-heatmap__grid::-webkit-scrollbar-thumb:hover {
198
+ background: var(--color-level-3);
199
+ }
200
+ .contribution-heatmap__week {
201
+ display: flex;
202
+ flex-direction: column;
203
+ gap: var(--day-gap);
204
+ min-width: var(--day-size);
205
+ }
206
+ .contribution-heatmap__day {
207
+ width: var(--day-size);
208
+ height: var(--day-size);
209
+ border-radius: 2px;
210
+ cursor: pointer;
211
+ transition: var(--transition);
212
+ position: relative;
213
+ border: 1px solid transparent;
214
+ }
215
+ .contribution-heatmap__day--level-0 {
216
+ background-color: var(--color-level-0);
217
+ }
218
+ .contribution-heatmap__day--level-0:hover {
219
+ background-color: var(--color-level-1);
220
+ border-color: var(--color-level-2);
221
+ }
222
+ .contribution-heatmap__day--level-1 {
223
+ background-color: var(--color-level-1);
224
+ }
225
+ .contribution-heatmap__day--level-1:hover {
226
+ background-color: var(--color-level-2);
227
+ border-color: var(--color-level-3);
228
+ }
229
+ .contribution-heatmap__day--level-2 {
230
+ background-color: var(--color-level-2);
231
+ }
232
+ .contribution-heatmap__day--level-2:hover {
233
+ background-color: var(--color-level-3);
234
+ border-color: var(--color-level-4);
235
+ }
236
+ .contribution-heatmap__day--level-3 {
237
+ background-color: var(--color-level-3);
238
+ }
239
+ .contribution-heatmap__day--level-3:hover {
240
+ background-color: var(--color-level-4);
241
+ border-color: var(--color-level-4);
242
+ }
243
+ .contribution-heatmap__day--level-4 {
244
+ background-color: var(--color-level-4);
245
+ }
246
+ .contribution-heatmap__day--level-4:hover {
247
+ filter: brightness(1.1);
248
+ border-color: var(--color-level-4);
249
+ }
250
+ .contribution-heatmap__day:hover {
251
+ transform: scale(1.2);
252
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
253
+ z-index: 10;
254
+ border-radius: 3px;
255
+ }
256
+ .contribution-heatmap__day:focus-visible {
257
+ outline: 2px solid var(--color-level-3);
258
+ outline-offset: 2px;
259
+ }
260
+ .contribution-heatmap__legend {
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ gap: 4px;
265
+ margin-top: 12px;
266
+ flex-wrap: wrap;
267
+ }
268
+ @media (max-width: 768px) {
269
+ .contribution-heatmap__legend {
270
+ gap: 3px;
271
+ margin-top: 8px;
272
+ }
273
+ }
274
+ .contribution-heatmap__legend-text {
275
+ color: var(--color-text-muted);
276
+ font-size: 12px;
277
+ font-weight: 500;
278
+ margin: 0 4px;
279
+ }
280
+ @media (max-width: 768px) {
281
+ .contribution-heatmap__legend-text {
282
+ font-size: 10px;
283
+ margin: 0 2px;
284
+ }
285
+ }
286
+ .contribution-heatmap__legend-item {
287
+ width: 12px;
288
+ height: 12px;
289
+ border-radius: 2px;
290
+ cursor: help;
291
+ transition: var(--transition);
292
+ border: 1px solid var(--color-border);
293
+ }
294
+ @media (max-width: 768px) {
295
+ .contribution-heatmap__legend-item {
296
+ width: 10px;
297
+ height: 10px;
298
+ }
299
+ }
300
+ .contribution-heatmap__legend-item:hover {
301
+ transform: scale(1.2);
302
+ border-color: var(--color-text-muted);
303
+ }
304
+ .contribution-heatmap__legend-item--level-0 {
305
+ background-color: var(--color-level-0);
306
+ }
307
+ .contribution-heatmap__legend-item--level-1 {
308
+ background-color: var(--color-level-1);
309
+ }
310
+ .contribution-heatmap__legend-item--level-2 {
311
+ background-color: var(--color-level-2);
312
+ }
313
+ .contribution-heatmap__legend-item--level-3 {
314
+ background-color: var(--color-level-3);
315
+ }
316
+ .contribution-heatmap__legend-item--level-4 {
317
+ background-color: var(--color-level-4);
318
+ }
319
+ @keyframes fadeInUp {
320
+ from {
321
+ opacity: 0;
322
+ transform: translateY(20px);
323
+ }
324
+ to {
325
+ opacity: 1;
326
+ transform: translateY(0);
327
+ }
328
+ }
329
+ .contribution-heatmap {
330
+ animation: fadeInUp 0.6s ease-out;
331
+ }
332
+ .contribution-heatmap__day {
333
+ animation: fadeInUp 0.3s ease-out;
334
+ animation-delay: var(--animation-delay, 0s);
335
+ animation-fill-mode: both;
336
+ }
337
+ .contribution-heatmap--ocean {
338
+ --color-level-0: #e6f3ff;
339
+ --color-level-1: #b3daff;
340
+ --color-level-2: #66b3ff;
341
+ --color-level-3: #1a8cff;
342
+ --color-level-4: #0066cc;
343
+ }
344
+ .contribution-heatmap--sunset {
345
+ --color-level-0: #fff2e6;
346
+ --color-level-1: #ffcc99;
347
+ --color-level-2: #ff9933;
348
+ --color-level-3: #ff6600;
349
+ --color-level-4: #cc3300;
350
+ }
351
+ .contribution-heatmap--purple {
352
+ --color-level-0: #f3e6ff;
353
+ --color-level-1: #d9b3ff;
354
+ --color-level-2: #b366ff;
355
+ --color-level-3: #8a1aff;
356
+ --color-level-4: #6600cc;
357
+ }
358
+
359
+ /* src/Example/index.scss */
360
+ .example__controls {
361
+ background: rgba(255, 255, 255, 0.95);
362
+ backdrop-filter: blur(10px);
363
+ padding: 24px;
364
+ border-radius: 16px;
365
+ margin-bottom: 30px;
366
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
367
+ border: 1px solid rgba(255, 255, 255, 0.2);
368
+ }
369
+ @media (max-width: 768px) {
370
+ .example__controls {
371
+ padding: 20px;
372
+ margin-bottom: 20px;
373
+ border-radius: 12px;
374
+ }
375
+ }
376
+ .example__heatmap {
377
+ margin-bottom: 40px;
378
+ }
379
+ @media (max-width: 768px) {
380
+ .example__heatmap {
381
+ margin-bottom: 30px;
382
+ }
383
+ }
384
+
385
+ /* src/Example/controlGroups.scss */
386
+ .control-group {
387
+ margin-bottom: 24px;
388
+ }
389
+ .control-group:last-child {
390
+ margin-bottom: 0;
391
+ }
392
+ .control-group__label {
393
+ display: block;
394
+ font-weight: 600;
395
+ color: #333;
396
+ margin-bottom: 12px;
397
+ font-size: 1rem;
398
+ }
399
+ @media (max-width: 768px) {
400
+ .control-group__label {
401
+ font-size: 0.9rem;
402
+ margin-bottom: 10px;
403
+ }
404
+ }
405
+ .control-group__buttons {
406
+ display: flex;
407
+ gap: 12px;
408
+ flex-wrap: wrap;
409
+ }
410
+ @media (max-width: 768px) {
411
+ .control-group__buttons {
412
+ gap: 8px;
413
+ }
414
+ }
415
+ .control-group__checkbox {
416
+ display: flex;
417
+ align-items: center;
418
+ gap: 8px;
419
+ cursor: pointer;
420
+ font-size: 1rem;
421
+ color: #555;
422
+ margin: 0;
423
+ }
424
+ @media (max-width: 768px) {
425
+ .control-group__checkbox {
426
+ font-size: 0.9rem;
427
+ }
428
+ }
429
+ .control-group__checkbox input[type=checkbox] {
430
+ width: 18px;
431
+ height: 18px;
432
+ accent-color: #667eea;
433
+ cursor: pointer;
434
+ }
435
+ @media (max-width: 768px) {
436
+ .control-group__checkbox input[type=checkbox] {
437
+ width: 16px;
438
+ height: 16px;
439
+ }
440
+ }
441
+ .control-group__checkbox:hover {
442
+ color: #333;
443
+ }
444
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.scss","../src/index.scss","../src/controlGroups.scss"],"sourcesContent":["// ContributionHeatmap.scss\n// GitHub-style contribution heatmap with BEM methodology\n\n.contribution-heatmap {\n // Component variables\n --day-size: 12px;\n --day-gap: 2px;\n --border-radius: 6px;\n --transition: all 0.2s ease;\n --shadow-color: rgba(0, 0, 0, 0.1);\n \n // Color scheme - GitHub-inspired\n --color-bg: #ffffff;\n --color-border: #e1e4e8;\n --color-text: #24292f;\n --color-text-muted: #656d76;\n --color-level-0: #ebedf0;\n --color-level-1: #9be9a8;\n --color-level-2: #40c463;\n --color-level-3: #30a14e;\n --color-level-4: #216e39;\n \n // Dark mode support\n @media (prefers-color-scheme: dark) {\n --color-bg: #0d1117;\n --color-border: #30363d;\n --color-text: #f0f6fc;\n --color-text-muted: #8b949e;\n --color-level-0: #161b22;\n --color-level-1: #0e4429;\n --color-level-2: #006d32;\n --color-level-3: #26a641;\n --color-level-4: #39d353;\n }\n \n background: var(--color-bg);\n border-radius: var(--border-radius);\n padding: 20px;\n box-shadow: 0 2px 8px var(--shadow-color);\n border: 1px solid var(--color-border);\n transition: var(--transition);\n max-width: 100%;\n overflow-x: auto;\n\n &__month-header {\n color: var(--color-text-muted);\n }\n\n // Hover effect for the entire component\n &:hover {\n box-shadow: 0 4px 16px var(--shadow-color);\n }\n\n // Mobile responsiveness\n @media (max-width: 768px) {\n --day-size: 10px;\n --day-gap: 1px;\n padding: 15px;\n \n // Reduce padding on mobile\n &__container {\n padding: 0;\n }\n }\n\n @media (max-width: 480px) {\n --day-size: 8px;\n --day-gap: 1px;\n padding: 10px;\n }\n\n // Header section\n &__header {\n margin-bottom: 16px;\n text-align: center;\n \n @media (max-width: 768px) {\n margin-bottom: 12px;\n }\n }\n\n &__title {\n color: var(--color-text);\n font-size: 18px;\n font-weight: 600;\n margin: 0 0 4px 0;\n letter-spacing: -0.2px;\n \n @media (max-width: 768px) {\n font-size: 16px;\n }\n }\n\n &__subtitle {\n color: var(--color-text-muted);\n font-size: 14px;\n margin: 0;\n font-weight: 400;\n \n @media (max-width: 768px) {\n font-size: 12px;\n }\n }\n\n // Main container\n &__container {\n display: grid;\n gap: 8px;\n padding: 16px;\n background: var(--color-bg);\n border-radius: calc(var(--border-radius) - 2px);\n position: relative;\n grid-template-areas: \n \". months\"\n \"days grid\";\n grid-template-columns: 30px 1fr;\n grid-template-rows: auto 1fr;\n \n @media (max-width: 768px) {\n padding: 8px;\n gap: 6px;\n grid-template-columns: 20px 1fr;\n }\n }\n\n // Month labels\n &__months {\n grid-area: months;\n display: flex;\n gap: var(--day-gap);\n margin-bottom: 4px;\n \n @media (max-width: 768px) {\n margin-bottom: 2px;\n }\n }\n\n &__month {\n color: var(--color-text-muted);\n font-size: 12px;\n font-weight: 500;\n text-align: left;\n padding: 0 2px;\n flex-grow: 0;\n flex-shrink: 0;\n \n @media (max-width: 768px) {\n font-size: 10px;\n }\n \n @media (max-width: 480px) {\n font-size: 8px;\n }\n }\n\n // Day labels (Mon, Tue, etc.)\n &__days {\n grid-area: days;\n display: flex;\n flex-direction: column;\n gap: var(--day-gap);\n align-items: flex-end;\n justify-content: flex-start;\n padding-right: 4px;\n \n @media (max-width: 768px) {\n padding-right: 2px;\n }\n }\n\n &__day-label {\n color: var(--color-text-muted);\n font-size: 10px;\n font-weight: 500;\n height: var(--day-size);\n display: flex;\n align-items: center;\n width: 24px;\n \n @media (max-width: 768px) {\n font-size: 8px;\n width: 16px;\n }\n \n @media (max-width: 480px) {\n font-size: 7px;\n width: 12px;\n }\n }\n\n // Main grid\n &__grid {\n grid-area: grid;\n display: flex;\n gap: var(--day-gap);\n overflow-x: auto;\n scroll-behavior: smooth;\n \n // Custom scrollbar\n &::-webkit-scrollbar {\n height: 4px;\n }\n \n &::-webkit-scrollbar-track {\n background: var(--color-border);\n border-radius: 2px;\n }\n \n &::-webkit-scrollbar-thumb {\n background: var(--color-level-2);\n border-radius: 2px;\n \n &:hover {\n background: var(--color-level-3);\n }\n }\n }\n\n // Week column\n &__week {\n display: flex;\n flex-direction: column;\n gap: var(--day-gap);\n min-width: var(--day-size);\n }\n\n // Individual day square\n &__day {\n width: var(--day-size);\n height: var(--day-size);\n border-radius: 2px;\n cursor: pointer;\n transition: var(--transition);\n position: relative;\n border: 1px solid transparent;\n \n // Contribution levels\n &--level-0 {\n background-color: var(--color-level-0);\n \n &:hover {\n background-color: var(--color-level-1);\n border-color: var(--color-level-2);\n }\n }\n \n &--level-1 {\n background-color: var(--color-level-1);\n \n &:hover {\n background-color: var(--color-level-2);\n border-color: var(--color-level-3);\n }\n }\n \n &--level-2 {\n background-color: var(--color-level-2);\n \n &:hover {\n background-color: var(--color-level-3);\n border-color: var(--color-level-4);\n }\n }\n \n &--level-3 {\n background-color: var(--color-level-3);\n \n &:hover {\n background-color: var(--color-level-4);\n border-color: var(--color-level-4);\n }\n }\n \n &--level-4 {\n background-color: var(--color-level-4);\n \n &:hover {\n filter: brightness(1.1);\n border-color: var(--color-level-4);\n }\n }\n \n // Hover effects\n &:hover {\n transform: scale(1.2);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n z-index: 10;\n border-radius: 3px;\n }\n \n // Focus styles for accessibility\n &:focus-visible {\n outline: 2px solid var(--color-level-3);\n outline-offset: 2px;\n }\n }\n\n // Legend\n &__legend {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n margin-top: 12px;\n flex-wrap: wrap;\n \n @media (max-width: 768px) {\n gap: 3px;\n margin-top: 8px;\n }\n }\n\n &__legend-text {\n color: var(--color-text-muted);\n font-size: 12px;\n font-weight: 500;\n margin: 0 4px;\n \n @media (max-width: 768px) {\n font-size: 10px;\n margin: 0 2px;\n }\n }\n\n &__legend-item {\n width: 12px;\n height: 12px;\n border-radius: 2px;\n cursor: help;\n transition: var(--transition);\n border: 1px solid var(--color-border);\n \n @media (max-width: 768px) {\n width: 10px;\n height: 10px;\n }\n \n &:hover {\n transform: scale(1.2);\n border-color: var(--color-text-muted);\n }\n \n &--level-0 { background-color: var(--color-level-0); }\n &--level-1 { background-color: var(--color-level-1); }\n &--level-2 { background-color: var(--color-level-2); }\n &--level-3 { background-color: var(--color-level-3); }\n &--level-4 { background-color: var(--color-level-4); }\n }\n}\n\n// Animation for initial load\n@keyframes fadeInUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.contribution-heatmap {\n animation: fadeInUp 0.6s ease-out;\n \n &__day {\n animation: fadeInUp 0.3s ease-out;\n animation-delay: var(--animation-delay, 0s);\n animation-fill-mode: both;\n }\n}\n\n// Utilities for different themes\n.contribution-heatmap {\n // Ocean theme\n &--ocean {\n --color-level-0: #e6f3ff;\n --color-level-1: #b3daff;\n --color-level-2: #66b3ff;\n --color-level-3: #1a8cff;\n --color-level-4: #0066cc;\n }\n \n // Sunset theme\n &--sunset {\n --color-level-0: #fff2e6;\n --color-level-1: #ffcc99;\n --color-level-2: #ff9933;\n --color-level-3: #ff6600;\n --color-level-4: #cc3300;\n }\n \n // Purple theme\n &--purple {\n --color-level-0: #f3e6ff;\n --color-level-1: #d9b3ff;\n --color-level-2: #b366ff;\n --color-level-3: #8a1aff;\n --color-level-4: #6600cc;\n }\n}\n",".example {\n &__controls {\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(10px);\n padding: 24px;\n border-radius: 16px;\n margin-bottom: 30px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.2);\n\n @media (max-width: 768px) {\n padding: 20px;\n margin-bottom: 20px;\n border-radius: 12px;\n }\n }\n\n &__heatmap {\n margin-bottom: 40px;\n\n @media (max-width: 768px) {\n margin-bottom: 30px;\n }\n }\n}\n","// Control groups\n.control-group {\n margin-bottom: 24px;\n\n &:last-child {\n margin-bottom: 0;\n }\n\n &__label {\n display: block;\n font-weight: 600;\n color: #333;\n margin-bottom: 12px;\n font-size: 1rem;\n\n @media (max-width: 768px) {\n font-size: 0.9rem;\n margin-bottom: 10px;\n }\n }\n\n &__buttons {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n\n @media (max-width: 768px) {\n gap: 8px;\n }\n }\n\n &__checkbox {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n font-size: 1rem;\n color: #555;\n margin: 0;\n\n @media (max-width: 768px) {\n font-size: 0.9rem;\n }\n\n input[type='checkbox'] {\n width: 18px;\n height: 18px;\n accent-color: #667eea;\n cursor: pointer;\n\n @media (max-width: 768px) {\n width: 16px;\n height: 16px;\n }\n }\n\n &:hover {\n color: #333;\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,11 +1,34 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ReactNode, ButtonHTMLAttributes } from 'react';
3
2
 
4
- type ButtonProps = {
5
- children?: ReactNode;
6
- ariaLabel?: string;
7
- type?: "button" | "submit" | "reset";
8
- } & ButtonHTMLAttributes<HTMLButtonElement>;
9
- declare function Button({ children, ariaLabel, type, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
3
+ interface ContributionData {
4
+ date: string;
5
+ count: number;
6
+ level: 0 | 1 | 2 | 3 | 4;
7
+ }
8
+ type Week = [
9
+ ContributionData,
10
+ ContributionData,
11
+ ContributionData,
12
+ ContributionData,
13
+ ContributionData,
14
+ ContributionData,
15
+ ContributionData
16
+ ];
17
+ type Period = {
18
+ start: Date;
19
+ end: Date;
20
+ };
10
21
 
11
- export { Button };
22
+ interface ContributionHeatmapProps {
23
+ data: {
24
+ contribution: ContributionData[];
25
+ period: Period;
26
+ weeks: Week[];
27
+ };
28
+ className?: string;
29
+ }
30
+ declare function ContributionHeatmap({ data: { contribution, period, weeks }, className, }: ContributionHeatmapProps): react_jsx_runtime.JSX.Element;
31
+
32
+ declare function ContributionHeatmapExample(): react_jsx_runtime.JSX.Element;
33
+
34
+ export { ContributionHeatmap, ContributionHeatmapExample };
package/dist/index.js CHANGED
@@ -1,15 +1,395 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
2
3
 
3
- // src/Button.tsx
4
- function Button({
5
- children = "Click me",
6
- ariaLabel = "Click me",
7
- type = "button",
8
- ...props
4
+ // src/ContributionHeatmap/models.tsx
5
+ var monthNames = [
6
+ "Jan",
7
+ "Feb",
8
+ "Mar",
9
+ "Apr",
10
+ "May",
11
+ "Jun",
12
+ "Jul",
13
+ "Aug",
14
+ "Sep",
15
+ "Oct",
16
+ "Nov",
17
+ "Dec"
18
+ ];
19
+ var dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
20
+ function getLastYearPeriod() {
21
+ const start = new Date(
22
+ (/* @__PURE__ */ new Date()).setFullYear(
23
+ (/* @__PURE__ */ new Date()).getFullYear() - 1,
24
+ (/* @__PURE__ */ new Date()).getMonth(),
25
+ (/* @__PURE__ */ new Date()).getDate() + 1
26
+ )
27
+ );
28
+ const end = /* @__PURE__ */ new Date();
29
+ return { start, end };
30
+ }
31
+ var createDateString = (date) => date.toISOString().split("T")[0];
32
+
33
+ // src/ContributionHeatmap/getMonthsForHeader.ts
34
+ function getMonthsForHeader({
35
+ weeks,
36
+ period: period2
9
37
  }) {
10
- return /* @__PURE__ */ jsx("button", { type, "aria-label": ariaLabel, tabIndex: 0, ...props, children });
38
+ const months = [];
39
+ const { start, end } = period2;
40
+ let currentMonth = -1;
41
+ let currentSpan = 0;
42
+ weeks.forEach((week) => {
43
+ if (week.length > 0) {
44
+ let monthToUse = -1;
45
+ const actualStartDate = new Date(start);
46
+ const actualEndDate = new Date(end);
47
+ for (const day of week) {
48
+ const dayDate = new Date(day.date);
49
+ if (dayDate >= actualStartDate && dayDate <= actualEndDate) {
50
+ monthToUse = dayDate.getMonth();
51
+ break;
52
+ }
53
+ }
54
+ if (monthToUse === -1) {
55
+ const middleDay = week[3];
56
+ monthToUse = new Date(middleDay.date).getMonth();
57
+ }
58
+ if (monthToUse !== currentMonth) {
59
+ if (currentMonth !== -1) {
60
+ months.push({ name: monthNames[currentMonth], span: currentSpan });
61
+ }
62
+ currentMonth = monthToUse;
63
+ currentSpan = 1;
64
+ } else {
65
+ currentSpan++;
66
+ }
67
+ }
68
+ });
69
+ if (currentMonth !== -1) {
70
+ months.push({ name: monthNames[currentMonth], span: currentSpan });
71
+ }
72
+ if (months.length > 0 && months[months.length - 1].span === 1) {
73
+ months[months.length - 1].span = 2;
74
+ }
75
+ return months;
76
+ }
77
+ function Legend() {
78
+ return /* @__PURE__ */ jsxs("div", { className: "contribution-heatmap__legend", children: [
79
+ /* @__PURE__ */ jsx("span", { className: "contribution-heatmap__legend-text", children: "Less" }),
80
+ [0, 1, 2, 3, 4].map((level) => /* @__PURE__ */ jsx(
81
+ "div",
82
+ {
83
+ className: `contribution-heatmap__legend-item contribution-heatmap__legend-item--level-${level}`,
84
+ title: `Level ${level}`
85
+ },
86
+ level
87
+ )),
88
+ /* @__PURE__ */ jsx("span", { className: "contribution-heatmap__legend-text", children: "More" })
89
+ ] });
90
+ }
91
+ function Header({ contributions }) {
92
+ return /* @__PURE__ */ jsxs("div", { className: "contribution-heatmap__header", children: [
93
+ /* @__PURE__ */ jsx("h3", { className: "contribution-heatmap__title", children: "Activity Overview" }),
94
+ /* @__PURE__ */ jsxs("p", { className: "contribution-heatmap__subtitle", children: [
95
+ contributions,
96
+ ' "healthy" days in the last year'
97
+ ] })
98
+ ] });
99
+ }
100
+
101
+ // src/ContributionHeatmap/formatTooltip.tsx
102
+ function isInRange(date, { start, end }) {
103
+ const actualStartDate = new Date(start);
104
+ const actualEndDate = new Date(end);
105
+ const contributionDate = new Date(date);
106
+ return contributionDate > actualStartDate && contributionDate < actualEndDate;
107
+ }
108
+ var formatTooltip = (contribution, { start, end }) => {
109
+ const date = new Date(contribution.date);
110
+ const formattedDate = date.toLocaleDateString("en-US", {
111
+ weekday: "short",
112
+ year: "numeric",
113
+ month: "short",
114
+ day: "numeric"
115
+ });
116
+ if (!isInRange(new Date(contribution.date), { start, end }) || contribution.count === 0) {
117
+ return `${formattedDate}: No contributions`;
118
+ }
119
+ return `${formattedDate}: ${contribution.count} contribution${contribution.count !== 1 ? "s" : ""}`;
120
+ };
121
+
122
+ // src/ContributionHeatmap/addExtraWeekIfNeeded.ts
123
+ var createEmptyContribution = (date) => ({
124
+ date,
125
+ count: 0,
126
+ level: 0
127
+ });
128
+ var createEmptyWeek = (startDate) => {
129
+ const week = [];
130
+ const currentDate = new Date(startDate);
131
+ for (let day = 0; day < 7; day++) {
132
+ const dateString = createDateString(currentDate);
133
+ week.push(createEmptyContribution(dateString));
134
+ currentDate.setDate(currentDate.getDate() + 1);
135
+ }
136
+ return week;
137
+ };
138
+ function addExtraWeekIfNeeded(weeks, period2) {
139
+ if (weeks.length === 0) return weeks;
140
+ const lastWeek = weeks[weeks.length - 1];
141
+ const lastDate = new Date(lastWeek[6].date);
142
+ const nextWeekStart = new Date(lastDate);
143
+ nextWeekStart.setDate(lastDate.getDate() + 1);
144
+ const extraWeek = createEmptyWeek(nextWeekStart);
145
+ return [...weeks, extraWeek];
146
+ }
147
+ function ContributionHeatmap({
148
+ data: { contribution, period: period2, weeks },
149
+ className = ""
150
+ }) {
151
+ const adjustedWeeks = addExtraWeekIfNeeded(weeks);
152
+ const months = getMonthsForHeader({ weeks: adjustedWeeks, period: period2 });
153
+ return /* @__PURE__ */ jsxs("div", { className: `contribution-heatmap ${className}`, children: [
154
+ /* @__PURE__ */ jsx(Header, { contributions: contribution.filter((d) => d.count > 0).length }),
155
+ /* @__PURE__ */ jsxs("table", { className: "contribution-heatmap__table", children: [
156
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
157
+ /* @__PURE__ */ jsx("th", { className: "contribution-heatmap__day-header" }),
158
+ months.map((month, index) => /* @__PURE__ */ jsx(
159
+ "th",
160
+ {
161
+ className: "contribution-heatmap__month-header",
162
+ colSpan: month.span,
163
+ children: month.name
164
+ },
165
+ index
166
+ ))
167
+ ] }) }),
168
+ /* @__PURE__ */ jsx("tbody", { children: dayNames.map((dayName, dayIndex) => /* @__PURE__ */ jsxs("tr", { children: [
169
+ /* @__PURE__ */ jsx("td", { className: "contribution-heatmap__day-label", children: window.innerWidth > 768 ? dayName : dayName.charAt(0) }),
170
+ adjustedWeeks.map((week, weekIndex) => {
171
+ const contribution2 = week[dayIndex];
172
+ return /* @__PURE__ */ jsx(
173
+ "td",
174
+ {
175
+ className: `contribution-heatmap__day contribution-heatmap__day--level-${contribution2.level}`,
176
+ title: formatTooltip(contribution2, period2),
177
+ "aria-label": formatTooltip(contribution2, period2),
178
+ role: "button",
179
+ tabIndex: 0,
180
+ "data-count": contribution2.count,
181
+ "data-date": contribution2.date,
182
+ style: {
183
+ "--animation-delay": `${(weekIndex * 7 + dayIndex) * 5e-3}s`
184
+ }
185
+ },
186
+ weekIndex
187
+ );
188
+ })
189
+ ] }, dayIndex)) })
190
+ ] }),
191
+ /* @__PURE__ */ jsx(Legend, {})
192
+ ] });
193
+ }
194
+
195
+ // src/ContributionHeatmap/createRealisticMockedCount.ts
196
+ var isSummerMonth = (month) => {
197
+ return month >= 5 && month <= 7;
198
+ };
199
+ function createRealisticMockedCount(date) {
200
+ const dayOfWeek = date.getDay();
201
+ const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
202
+ const month = date.getMonth();
203
+ let baseCount = isWeekend ? 2 : 8;
204
+ if (isSummerMonth(month)) {
205
+ baseCount = Math.max(0, baseCount - 3);
206
+ }
207
+ const randomFactor = Math.random();
208
+ let count = 0;
209
+ if (randomFactor > 0.3) {
210
+ count = Math.floor(Math.random() * baseCount);
211
+ }
212
+ if (randomFactor > 0.95) {
213
+ count += Math.floor(Math.random() * 10);
214
+ }
215
+ return count;
216
+ }
217
+
218
+ // src/ContributionHeatmap/getLevel.ts
219
+ var getLevel = (count) => {
220
+ let level = 0;
221
+ if (count === 0) level = 0;
222
+ else if (count <= 2) level = 1;
223
+ else if (count <= 5) level = 2;
224
+ else if (count <= 8) level = 3;
225
+ else level = 4;
226
+ return level;
227
+ };
228
+
229
+ // src/ContributionHeatmap/generateMockData.tsx
230
+ var getRandomCount = () => Math.floor(Math.random() * 15);
231
+ function getRange(period2) {
232
+ if (period2 !== "lastYear") {
233
+ return period2;
234
+ }
235
+ const end = /* @__PURE__ */ new Date();
236
+ const start = /* @__PURE__ */ new Date();
237
+ start.setFullYear(start.getFullYear() - 1);
238
+ return { start, end };
239
+ }
240
+ function generateMockData({
241
+ period: period2,
242
+ isRealistic
243
+ }) {
244
+ const data = [];
245
+ const { start, end } = getRange(period2);
246
+ for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
247
+ const count = isRealistic ? createRealisticMockedCount(date) : getRandomCount();
248
+ data.push({
249
+ date: createDateString(date),
250
+ count,
251
+ level: getLevel(count)
252
+ });
253
+ }
254
+ return data;
255
+ }
256
+
257
+ // src/ContributionHeatmap/groupByWeeks.ts
258
+ var createEmpyContribution = (date) => ({
259
+ date,
260
+ count: 0,
261
+ level: 0
262
+ });
263
+ var createContributionMap = (contributions) => {
264
+ const contributionMap = /* @__PURE__ */ new Map();
265
+ contributions.forEach((contribution) => {
266
+ contributionMap.set(contribution.date, contribution);
267
+ });
268
+ return contributionMap;
269
+ };
270
+ var getFirstSunday = (firstDate) => {
271
+ const startOfWeek = new Date(firstDate);
272
+ startOfWeek.setDate(firstDate.getDate() - firstDate.getDay());
273
+ return startOfWeek;
274
+ };
275
+ var getLastSaturday = (lastDate) => {
276
+ const endOfWeek = new Date(lastDate);
277
+ endOfWeek.setDate(lastDate.getDate() + (6 - lastDate.getDay()));
278
+ return endOfWeek;
279
+ };
280
+ var createWeek = (startDate, contributionMap) => {
281
+ const week = [];
282
+ const currentDate = new Date(startDate);
283
+ for (let day = 0; day < 7; day++) {
284
+ const dateString = createDateString(currentDate);
285
+ const contribution = contributionMap.get(dateString);
286
+ if (contribution) {
287
+ week.push(contribution);
288
+ } else {
289
+ week.push(createEmpyContribution(dateString));
290
+ }
291
+ currentDate.setDate(currentDate.getDate() + 1);
292
+ }
293
+ return week;
294
+ };
295
+ var groupByWeeks = (contributions) => {
296
+ const weeks = [];
297
+ const contributionMap = createContributionMap(contributions);
298
+ const firstDay = getFirstSunday(new Date(contributions[0].date));
299
+ const lastDate = getLastSaturday(
300
+ new Date(contributions[contributions.length - 1].date)
301
+ );
302
+ const currentDate = new Date(firstDay);
303
+ while (currentDate <= lastDate) {
304
+ weeks.push(createWeek(currentDate, contributionMap));
305
+ currentDate.setDate(currentDate.getDate() + 7);
306
+ }
307
+ return weeks;
308
+ };
309
+ function ControlGroups({
310
+ actions: { theme, setTheme, hasRealisticData, setHasRealisticData }
311
+ }) {
312
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
313
+ /* @__PURE__ */ jsxs("div", { className: "control-group", children: [
314
+ /* @__PURE__ */ jsx("label", { className: "control-group__label", children: "Theme:" }),
315
+ /* @__PURE__ */ jsxs("div", { className: "control-group__buttons", children: [
316
+ /* @__PURE__ */ jsx(
317
+ "button",
318
+ {
319
+ className: `theme-button ${theme === "" ? "theme-button--active" : ""}`,
320
+ onClick: () => setTheme(""),
321
+ children: "GitHub"
322
+ }
323
+ ),
324
+ /* @__PURE__ */ jsx(
325
+ "button",
326
+ {
327
+ className: `theme-button ${theme === "ocean" ? "theme-button--active" : ""}`,
328
+ onClick: () => setTheme("ocean"),
329
+ children: "Ocean"
330
+ }
331
+ ),
332
+ /* @__PURE__ */ jsx(
333
+ "button",
334
+ {
335
+ className: `theme-button ${theme === "sunset" ? "theme-button--active" : ""}`,
336
+ onClick: () => setTheme("sunset"),
337
+ children: "Sunset"
338
+ }
339
+ ),
340
+ /* @__PURE__ */ jsx(
341
+ "button",
342
+ {
343
+ className: `theme-button ${theme === "purple" ? "theme-button--active" : ""}`,
344
+ onClick: () => setTheme("purple"),
345
+ children: "Purple"
346
+ }
347
+ )
348
+ ] })
349
+ ] }),
350
+ /* @__PURE__ */ jsx("div", { className: "control-group", children: /* @__PURE__ */ jsxs("label", { className: "control-group__checkbox", children: [
351
+ /* @__PURE__ */ jsx(
352
+ "input",
353
+ {
354
+ type: "checkbox",
355
+ checked: hasRealisticData,
356
+ onChange: (e) => setHasRealisticData(e.target.checked)
357
+ }
358
+ ),
359
+ "Use realistic sample data"
360
+ ] }) })
361
+ ] });
362
+ }
363
+ var sampleData = generateMockData({ period: "lastYear", isRealistic: true });
364
+ var period = getLastYearPeriod();
365
+ function ContributionHeatmapExample() {
366
+ const [theme, setTheme] = useState("");
367
+ const [hasRealisticData, setHasRealisticData] = useState(false);
368
+ const contribution = hasRealisticData ? sampleData : generateMockData({
369
+ period,
370
+ isRealistic: false
371
+ });
372
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
373
+ /* @__PURE__ */ jsx("div", { className: "example__controls", children: /* @__PURE__ */ jsx(
374
+ ControlGroups,
375
+ {
376
+ actions: { theme, setTheme, hasRealisticData, setHasRealisticData }
377
+ }
378
+ ) }),
379
+ /* @__PURE__ */ jsx("div", { className: "example__heatmap", children: /* @__PURE__ */ jsx(
380
+ ContributionHeatmap,
381
+ {
382
+ className: theme ? `contribution-heatmap--${theme}` : "",
383
+ data: {
384
+ weeks: groupByWeeks(contribution),
385
+ contribution,
386
+ period
387
+ }
388
+ }
389
+ ) })
390
+ ] });
11
391
  }
12
392
 
13
- export { Button };
393
+ export { ContributionHeatmap, ContributionHeatmapExample };
14
394
  //# sourceMappingURL=index.js.map
15
395
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Button.tsx"],"names":[],"mappings":";;;AAQA,SAAS,MAAA,CAAO;AAAA,EACd,QAAA,GAAW,UAAA;AAAA,EACX,SAAA,GAAY,UAAA;AAAA,EACZ,IAAA,GAAO,QAAA;AAAA,EACP,GAAG;AACL,CAAA,EAAgB;AACd,EAAA,uBACE,GAAA,CAAC,YAAO,IAAA,EAAY,YAAA,EAAY,WAAW,QAAA,EAAU,CAAA,EAAI,GAAG,KAAA,EACzD,QAAA,EACH,CAAA;AAEJ","file":"index.js","sourcesContent":["import { type ButtonHTMLAttributes, type ReactNode } from \"react\";\n\ntype ButtonProps = {\n children?: ReactNode;\n ariaLabel?: string;\n type?: \"button\" | \"submit\" | \"reset\";\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\nfunction Button({\n children = \"Click me\",\n ariaLabel = \"Click me\",\n type = \"button\",\n ...props\n}: ButtonProps) {\n return (\n <button type={type} aria-label={ariaLabel} tabIndex={0} {...props}>\n {children}\n </button>\n );\n}\nexport { Button };\n"]}
1
+ {"version":3,"sources":["../src/ContributionHeatmap/models.tsx","../src/ContributionHeatmap/getMonthsForHeader.ts","../src/ContributionHeatmap/Legend.tsx","../src/ContributionHeatmap/Header.tsx","../src/ContributionHeatmap/formatTooltip.tsx","../src/ContributionHeatmap/addExtraWeekIfNeeded.ts","../src/ContributionHeatmap/index.tsx","../src/ContributionHeatmap/createRealisticMockedCount.ts","../src/ContributionHeatmap/getLevel.ts","../src/ContributionHeatmap/generateMockData.tsx","../src/ContributionHeatmap/groupByWeeks.ts","../src/Example/ControlGroups.tsx","../src/Example/index.tsx"],"names":["period","jsxs","jsx","contribution","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["type Theme = '' | 'ocean' | 'sunset' | 'purple';\n\ninterface ContributionData {\n date: string;\n count: number;\n level: 0 | 1 | 2 | 3 | 4;\n}\n\ntype Week = [\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n ContributionData,\n];\n\ntype Period = { start: Date; end: Date };\n\nconst monthNames = [\n 'Jan',\n 'Feb',\n 'Mar',\n 'Apr',\n 'May',\n 'Jun',\n 'Jul',\n 'Aug',\n 'Sep',\n 'Oct',\n 'Nov',\n 'Dec',\n] as const;\nconst dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] as const;\n\nfunction getLastYearPeriod(): Period {\n const start = new Date(\n new Date().setFullYear(\n new Date().getFullYear() - 1,\n new Date().getMonth(),\n new Date().getDate() + 1,\n ),\n );\n const end = new Date();\n return { start, end };\n}\n\nconst createDateString = (date: Date): string =>\n date.toISOString().split('T')[0];\n\nexport type { ContributionData, Week, Period, Theme };\nexport { monthNames, dayNames, createDateString, getLastYearPeriod };\n","import { monthNames, type Period, type Week } from './models';\n\nfunction getMonthsForHeader({\n weeks,\n period,\n}: {\n weeks: Week[];\n period: Period;\n}): {\n name: string;\n span: number;\n}[] {\n const months: { name: string; span: number }[] = [];\n const { start, end } = period;\n let currentMonth = -1;\n let currentSpan = 0;\n\n weeks.forEach((week) => {\n if (week.length > 0) {\n // Find the first day in the week that falls within our actual data range\n let monthToUse = -1;\n const actualStartDate = new Date(start);\n const actualEndDate = new Date(end);\n\n for (const day of week) {\n const dayDate = new Date(day.date);\n if (dayDate >= actualStartDate && dayDate <= actualEndDate) {\n monthToUse = dayDate.getMonth();\n break;\n }\n }\n\n // If no day in the week is in our actual range, use the middle day (Wednesday)\n if (monthToUse === -1) {\n const middleDay = week[3]; // Wednesday (index 3)\n monthToUse = new Date(middleDay.date).getMonth();\n }\n\n if (monthToUse !== currentMonth) {\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan });\n }\n currentMonth = monthToUse;\n currentSpan = 1;\n } else {\n currentSpan++;\n }\n }\n });\n\n if (currentMonth !== -1) {\n months.push({ name: monthNames[currentMonth], span: currentSpan });\n }\n\n // If the last month only has one week, add an extra week for better visual balance\n if (months.length > 0 && months[months.length - 1].span === 1) {\n months[months.length - 1].span = 2;\n }\n\n return months;\n}\n\nexport { getMonthsForHeader };\n","function Legend(): React.ReactNode {\n return (\n <div className=\"contribution-heatmap__legend\">\n <span className=\"contribution-heatmap__legend-text\">Less</span>\n {[0, 1, 2, 3, 4].map((level) => (\n <div\n key={level}\n className={`contribution-heatmap__legend-item contribution-heatmap__legend-item--level-${level}`}\n title={`Level ${level}`}\n ></div>\n ))}\n <span className=\"contribution-heatmap__legend-text\">More</span>\n </div>\n );\n}\n\nexport { Legend };\n","function Header({ contributions }: { contributions: number }): React.ReactNode {\n return (\n <div className=\"contribution-heatmap__header\">\n <h3 className=\"contribution-heatmap__title\">Activity Overview</h3>\n <p className=\"contribution-heatmap__subtitle\">\n {contributions} \"healthy\" days in the last year\n </p>\n </div>\n );\n}\n\nexport { Header };\n","import type { ContributionData, Period } from './models';\n\nfunction isInRange(date: Date, { start, end }: Period): boolean {\n const actualStartDate = new Date(start);\n const actualEndDate = new Date(end);\n const contributionDate = new Date(date);\n\n return contributionDate > actualStartDate && contributionDate < actualEndDate;\n}\n\nconst formatTooltip = (\n contribution: ContributionData,\n { start, end }: Period,\n): string => {\n const date = new Date(contribution.date);\n const formattedDate = date.toLocaleDateString('en-US', {\n weekday: 'short',\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n\n if (\n !isInRange(new Date(contribution.date), { start, end }) ||\n contribution.count === 0\n ) {\n return `${formattedDate}: No contributions`;\n }\n\n return `${formattedDate}: ${contribution.count} contribution${contribution.count !== 1 ? 's' : ''}`;\n};\n\nexport { formatTooltip };\n","import { createDateString, type ContributionData, type Week, type Period } from './models';\n\nconst createEmptyContribution = (date: string): ContributionData => ({\n date,\n count: 0,\n level: 0,\n});\n\nconst createEmptyWeek = (startDate: Date): Week => {\n const week: ContributionData[] = [];\n const currentDate = new Date(startDate);\n \n for (let day = 0; day < 7; day++) {\n const dateString = createDateString(currentDate);\n week.push(createEmptyContribution(dateString));\n currentDate.setDate(currentDate.getDate() + 1);\n }\n\n return week as Week;\n};\n\n/**\n * Adds an extra empty week if the last month only has one week\n * to match the header span and improve visual balance\n */\nexport function addExtraWeekIfNeeded(weeks: Week[], period: Period): Week[] {\n if (weeks.length === 0) return weeks;\n\n // Simple approach: always add an extra week at the end for better balance\n // This ensures the table has an extra column to match the header span\n const lastWeek = weeks[weeks.length - 1];\n const lastDate = new Date(lastWeek[6].date); // Saturday of last week\n const nextWeekStart = new Date(lastDate);\n nextWeekStart.setDate(lastDate.getDate() + 1); // Next Sunday\n \n const extraWeek = createEmptyWeek(nextWeekStart);\n return [...weeks, extraWeek];\n}","import './index.scss';\nimport type { ContributionData, Period, Week as WeekType } from './models';\nimport { getMonthsForHeader } from './getMonthsForHeader';\nimport { Legend } from './Legend';\nimport { Header } from './Header';\nimport { formatTooltip } from './formatTooltip';\nimport { dayNames } from './models';\nimport { addExtraWeekIfNeeded } from './addExtraWeekIfNeeded';\n\ninterface ContributionHeatmapProps {\n data: { contribution: ContributionData[]; period: Period; weeks: WeekType[] };\n className?: string;\n}\n\nfunction ContributionHeatmap({\n data: { contribution, period, weeks },\n className = '',\n}: ContributionHeatmapProps) {\n // Add extra week if needed to match header span\n const adjustedWeeks = addExtraWeekIfNeeded(weeks, period);\n const months = getMonthsForHeader({ weeks: adjustedWeeks, period });\n\n return (\n <div className={`contribution-heatmap ${className}`}>\n <Header contributions={contribution.filter((d) => d.count > 0).length} />\n\n <table className=\"contribution-heatmap__table\">\n <thead>\n <tr>\n <th className=\"contribution-heatmap__day-header\"></th>\n {months.map((month, index) => (\n <th\n key={index}\n className=\"contribution-heatmap__month-header\"\n colSpan={month.span}\n >\n {month.name}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {dayNames.map((dayName, dayIndex) => (\n <tr key={dayIndex}>\n <td className=\"contribution-heatmap__day-label\">\n {window.innerWidth > 768 ? dayName : dayName.charAt(0)}\n </td>\n {adjustedWeeks.map((week, weekIndex) => {\n const contribution = week[dayIndex];\n return (\n <td\n key={weekIndex}\n className={`contribution-heatmap__day contribution-heatmap__day--level-${contribution.level}`}\n title={formatTooltip(contribution, period)}\n aria-label={formatTooltip(contribution, period)}\n role=\"button\"\n tabIndex={0}\n data-count={contribution.count}\n data-date={contribution.date}\n style={{ \n '--animation-delay': `${(weekIndex * 7 + dayIndex) * 0.005}s` \n } as React.CSSProperties}\n ></td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n\n <Legend />\n </div>\n );\n}\n\nexport { ContributionHeatmap };\n","import type { ContributionData } from './models';\n\nconst isSummerMonth = (month: number) => {\n return month >= 5 && month <= 7;\n};\n\nexport function createRealisticMockedCount(\n date: Date,\n): ContributionData['count'] {\n // Create realistic patterns\n const dayOfWeek = date.getDay();\n const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;\n const month = date.getMonth();\n\n let baseCount = isWeekend ? 2 : 8;\n\n // Lower activity in summer months (June, July, August)\n if (isSummerMonth(month)) {\n baseCount = Math.max(0, baseCount - 3);\n }\n\n // Add some randomness\n const randomFactor = Math.random();\n let count = 0;\n\n if (randomFactor > 0.3) {\n count = Math.floor(Math.random() * baseCount);\n }\n\n // Occasional high activity days\n if (randomFactor > 0.95) {\n count += Math.floor(Math.random() * 10);\n }\n\n return count;\n}\n","import type { ContributionData } from './models';\n\nconst getLevel = (count: number): ContributionData['level'] => {\n let level: 0 | 1 | 2 | 3 | 4 = 0;\n if (count === 0) level = 0;\n else if (count <= 2) level = 1;\n else if (count <= 5) level = 2;\n else if (count <= 8) level = 3;\n else level = 4;\n\n return level;\n};\n\nexport { getLevel };\n","import { createDateString, type ContributionData, type Period } from './models';\nimport { createRealisticMockedCount } from './createRealisticMockedCount';\nimport { getLevel } from './getLevel';\n\nconst getRandomCount = () => Math.floor(Math.random() * 15);\n\nfunction getRange(period: Period | 'lastYear') {\n if (period !== 'lastYear') {\n return period;\n }\n const end = new Date(); // Today\n const start = new Date();\n start.setFullYear(start.getFullYear() - 1); // One year ago\n return { start, end };\n}\n\nfunction generateMockData({\n period,\n isRealistic,\n}: {\n period: Period | 'lastYear';\n isRealistic: boolean;\n}): ContributionData[] {\n const data: ContributionData[] = [];\n const { start, end } = getRange(period);\n\n for (\n let date = new Date(start);\n date <= end;\n date.setDate(date.getDate() + 1)\n ) {\n const count = isRealistic\n ? createRealisticMockedCount(date)\n : getRandomCount();\n data.push({\n date: createDateString(date),\n count,\n level: getLevel(count),\n });\n }\n\n return data;\n}\n\nexport { generateMockData };\n","import { createDateString, type ContributionData, type Week } from './models';\n\nconst createEmpyContribution = (date: string): ContributionData => ({\n date,\n count: 0,\n level: 0,\n});\n\nconst createContributionMap = (\n contributions: ContributionData[],\n): Map<string, ContributionData> => {\n const contributionMap = new Map<string, ContributionData>();\n contributions.forEach((contribution) => {\n contributionMap.set(contribution.date, contribution);\n });\n return contributionMap;\n};\n\nconst getFirstSunday: (firstDate: Date) => Date = (firstDate) => {\n const startOfWeek = new Date(firstDate);\n startOfWeek.setDate(firstDate.getDate() - firstDate.getDay());\n return startOfWeek;\n};\n\nconst getLastSaturday: (lastDate: Date) => Date = (lastDate) => {\n const endOfWeek = new Date(lastDate);\n endOfWeek.setDate(lastDate.getDate() + (6 - lastDate.getDay()));\n return endOfWeek;\n};\n\nconst createWeek = (\n startDate: Date,\n contributionMap: Map<string, ContributionData>,\n): Week => {\n const week: ContributionData[] = [];\n const currentDate = new Date(startDate);\n for (let day = 0; day < 7; day++) {\n const dateString = createDateString(currentDate);\n const contribution = contributionMap.get(dateString);\n\n if (contribution) {\n week.push(contribution);\n } else {\n week.push(createEmpyContribution(dateString));\n }\n currentDate.setDate(currentDate.getDate() + 1);\n }\n\n return week as Week;\n};\n\nconst groupByWeeks: (contributions: ContributionData[]) => Week[] = (\n contributions,\n) => {\n const weeks: Week[] = [];\n const contributionMap = createContributionMap(contributions);\n\n const firstDay = getFirstSunday(new Date(contributions[0].date));\n const lastDate = getLastSaturday(\n new Date(contributions[contributions.length - 1].date),\n );\n\n const currentDate = new Date(firstDay);\n while (currentDate <= lastDate) {\n weeks.push(createWeek(currentDate, contributionMap));\n currentDate.setDate(currentDate.getDate() + 7);\n }\n\n return weeks;\n};\n\nexport { groupByWeeks };\n","import './controlGroups.scss';\nimport type { Theme } from '@pearpages/heatmap/ContributionHeatmap/models';\n\nexport function ControlGroups({\n actions: { theme, setTheme, hasRealisticData, setHasRealisticData },\n}: {\n actions: {\n theme: Theme;\n setTheme: (theme: Theme) => void;\n hasRealisticData: boolean;\n setHasRealisticData: (use: boolean) => void;\n };\n}) {\n return (\n <>\n <div className=\"control-group\">\n <label className=\"control-group__label\">Theme:</label>\n <div className=\"control-group__buttons\">\n <button\n className={`theme-button ${theme === '' ? 'theme-button--active' : ''}`}\n onClick={() => setTheme('')}\n >\n GitHub\n </button>\n <button\n className={`theme-button ${theme === 'ocean' ? 'theme-button--active' : ''}`}\n onClick={() => setTheme('ocean')}\n >\n Ocean\n </button>\n <button\n className={`theme-button ${theme === 'sunset' ? 'theme-button--active' : ''}`}\n onClick={() => setTheme('sunset')}\n >\n Sunset\n </button>\n <button\n className={`theme-button ${theme === 'purple' ? 'theme-button--active' : ''}`}\n onClick={() => setTheme('purple')}\n >\n Purple\n </button>\n </div>\n </div>\n <div className=\"control-group\">\n <label className=\"control-group__checkbox\">\n <input\n type=\"checkbox\"\n checked={hasRealisticData}\n onChange={(e) => setHasRealisticData(e.target.checked)}\n />\n Use realistic sample data\n </label>\n </div>\n </>\n );\n}\n","import { useState } from 'react';\nimport { ContributionHeatmap } from '@pearpages/heatmap/ContributionHeatmap';\nimport { generateMockData } from '@pearpages/heatmap/ContributionHeatmap/generateMockData';\nimport { getLastYearPeriod, type Theme } from '@pearpages/heatmap/ContributionHeatmap/models';\nimport { groupByWeeks } from '@pearpages/heatmap/ContributionHeatmap/groupByWeeks';\nimport './index.scss';\nimport { ControlGroups } from './ControlGroups';\n\nconst sampleData = generateMockData({ period: 'lastYear', isRealistic: true });\nconst period = getLastYearPeriod();\n\nfunction ContributionHeatmapExample() {\n const [theme, setTheme] = useState<Theme>('');\n const [hasRealisticData, setHasRealisticData] = useState(false);\n\n const contribution = hasRealisticData\n ? sampleData\n : generateMockData({\n period,\n isRealistic: false,\n });\n\n return (\n <>\n <div className=\"example__controls\">\n <ControlGroups\n actions={{ theme, setTheme, hasRealisticData, setHasRealisticData }}\n />\n </div>\n\n <div className=\"example__heatmap\">\n <ContributionHeatmap\n className={theme ? `contribution-heatmap--${theme}` : ''}\n data={{\n weeks: groupByWeeks(contribution),\n contribution,\n period,\n }}\n />\n </div>\n </>\n );\n}\n\nexport { ContributionHeatmapExample };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pearpages/heatmap",
3
- "version": "0.0.2",
3
+ "version": "0.1.2",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/pearpages/heatmap"
@@ -34,6 +34,8 @@
34
34
  "@testing-library/react": "^16",
35
35
  "@types/react": "^19.1.12",
36
36
  "@types/react-dom": "^19.1.9",
37
+ "esbuild-sass-plugin": "^3.3.1",
38
+ "sass": "^1.92.1",
37
39
  "tsup": "^8",
38
40
  "typescript": "^5",
39
41
  "vitest": "^2"