@pearpages/heatmap 0.1.5 → 0.2.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.
- package/dist/index.css +22 -134
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +94 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.css
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* src/ContributionHeatmap/index.scss */
|
|
2
2
|
.contribution-heatmap {
|
|
3
|
+
color: var(--color-text-muted);
|
|
4
|
+
font-size: 8px;
|
|
3
5
|
--day-size: 12px;
|
|
4
6
|
--day-gap: 2px;
|
|
5
7
|
--border-radius: 6px;
|
|
@@ -38,8 +40,23 @@
|
|
|
38
40
|
max-width: 100%;
|
|
39
41
|
overflow-x: auto;
|
|
40
42
|
}
|
|
43
|
+
.contribution-heatmap__table {
|
|
44
|
+
table-layout: fixed;
|
|
45
|
+
width: 100%;
|
|
46
|
+
border-collapse: separate;
|
|
47
|
+
border-spacing: var(--day-gap);
|
|
48
|
+
}
|
|
41
49
|
.contribution-heatmap__month-header {
|
|
42
|
-
|
|
50
|
+
font-weight: 500;
|
|
51
|
+
text-align: left;
|
|
52
|
+
padding: 0;
|
|
53
|
+
border: none;
|
|
54
|
+
background: none;
|
|
55
|
+
}
|
|
56
|
+
.contribution-heatmap__day-header {
|
|
57
|
+
padding: 0;
|
|
58
|
+
border: none;
|
|
59
|
+
background: none;
|
|
43
60
|
}
|
|
44
61
|
.contribution-heatmap:hover {
|
|
45
62
|
box-shadow: 0 4px 16px var(--shadow-color);
|
|
@@ -70,139 +87,6 @@
|
|
|
70
87
|
margin-bottom: 12px;
|
|
71
88
|
}
|
|
72
89
|
}
|
|
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
90
|
.contribution-heatmap__day {
|
|
207
91
|
width: var(--day-size);
|
|
208
92
|
height: var(--day-size);
|
|
@@ -211,6 +95,10 @@
|
|
|
211
95
|
transition: var(--transition);
|
|
212
96
|
position: relative;
|
|
213
97
|
border: 1px solid transparent;
|
|
98
|
+
padding: 0;
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
min-width: var(--day-size);
|
|
101
|
+
max-width: var(--day-size);
|
|
214
102
|
}
|
|
215
103
|
.contribution-heatmap__day--level-0 {
|
|
216
104
|
background-color: var(--color-level-0);
|
package/dist/index.css.map
CHANGED
|
@@ -1 +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":[]}
|
|
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 color: var(--color-text-muted);\n font-size: 8px;\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 // Table-specific styles\n &__table {\n table-layout: fixed;\n width: 100%;\n border-collapse: separate;\n border-spacing: var(--day-gap);\n }\n\n &__month-header {\n font-weight: 500;\n text-align: left;\n padding: 0;\n border: none;\n background: none;\n }\n\n &__day-header {\n padding: 0;\n border: none;\n background: none;\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 \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 \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 // 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 padding: 0;\n box-sizing: border-box;\n\n // Ensure consistent sizing in table\n min-width: var(--day-size);\n max-width: var(--day-size);\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 {\n background-color: var(--color-level-0);\n }\n &--level-1 {\n background-color: var(--color-level-1);\n }\n &--level-2 {\n background-color: var(--color-level-2);\n }\n &--level-3 {\n background-color: var(--color-level-3);\n }\n &--level-4 {\n background-color: var(--color-level-4);\n }\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
|
@@ -26,8 +26,9 @@ interface ContributionHeatmapProps {
|
|
|
26
26
|
weeks: Week[];
|
|
27
27
|
};
|
|
28
28
|
className?: string;
|
|
29
|
+
isReverse?: boolean;
|
|
29
30
|
}
|
|
30
|
-
declare function ContributionHeatmap({ data: { period, weeks }, className, }: ContributionHeatmapProps): react_jsx_runtime.JSX.Element;
|
|
31
|
+
declare function ContributionHeatmap({ data: { period, weeks }, className, isReverse, }: ContributionHeatmapProps): react_jsx_runtime.JSX.Element;
|
|
31
32
|
|
|
32
33
|
declare function ContributionHeatmapExample(): react_jsx_runtime.JSX.Element;
|
|
33
34
|
|
package/dist/index.js
CHANGED
|
@@ -28,18 +28,24 @@ function getLastYearPeriod() {
|
|
|
28
28
|
const end = /* @__PURE__ */ new Date();
|
|
29
29
|
return { start, end };
|
|
30
30
|
}
|
|
31
|
+
function getLastMonthPeriod() {
|
|
32
|
+
const now = /* @__PURE__ */ new Date();
|
|
33
|
+
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
34
|
+
const start = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate() + 1);
|
|
35
|
+
return { start, end };
|
|
36
|
+
}
|
|
31
37
|
var createDateString = (date) => date.toISOString().split("T")[0];
|
|
32
38
|
|
|
33
39
|
// src/ContributionHeatmap/getMonthsForHeader.ts
|
|
34
40
|
function getMonthsForHeader({
|
|
35
41
|
weeks,
|
|
36
|
-
period
|
|
42
|
+
period
|
|
37
43
|
}) {
|
|
38
44
|
const months = [];
|
|
39
|
-
const { start, end } =
|
|
45
|
+
const { start, end } = period;
|
|
40
46
|
let currentMonth = -1;
|
|
41
47
|
let currentSpan = 0;
|
|
42
|
-
weeks.forEach((week) => {
|
|
48
|
+
weeks.forEach((week, weekIndex) => {
|
|
43
49
|
if (week.length > 0) {
|
|
44
50
|
let monthToUse = -1;
|
|
45
51
|
const actualStartDate = new Date(start);
|
|
@@ -57,7 +63,7 @@ function getMonthsForHeader({
|
|
|
57
63
|
}
|
|
58
64
|
if (monthToUse !== currentMonth) {
|
|
59
65
|
if (currentMonth !== -1) {
|
|
60
|
-
months.push({ name: monthNames[currentMonth], span: currentSpan });
|
|
66
|
+
months.push({ name: monthNames[currentMonth], span: currentSpan, start: weekIndex - currentSpan });
|
|
61
67
|
}
|
|
62
68
|
currentMonth = monthToUse;
|
|
63
69
|
currentSpan = 1;
|
|
@@ -67,7 +73,7 @@ function getMonthsForHeader({
|
|
|
67
73
|
}
|
|
68
74
|
});
|
|
69
75
|
if (currentMonth !== -1) {
|
|
70
|
-
months.push({ name: monthNames[currentMonth], span: currentSpan });
|
|
76
|
+
months.push({ name: monthNames[currentMonth], span: currentSpan, start: weeks.length - currentSpan });
|
|
71
77
|
}
|
|
72
78
|
if (months.length > 0 && months[months.length - 1].span === 1) {
|
|
73
79
|
months[months.length - 1].span = 2;
|
|
@@ -109,40 +115,49 @@ function Legend() {
|
|
|
109
115
|
/* @__PURE__ */ jsx("span", { className: "contribution-heatmap__legend-text", children: "More" })
|
|
110
116
|
] });
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
// src/ContributionHeatmap/addExtraWeekIfNeeded.ts
|
|
114
|
-
var createEmptyContribution = (date) => ({
|
|
115
|
-
date,
|
|
116
|
-
count: 0,
|
|
117
|
-
level: 0
|
|
118
|
-
});
|
|
119
|
-
var createEmptyWeek = (startDate) => {
|
|
120
|
-
const week = [];
|
|
121
|
-
const currentDate = new Date(startDate);
|
|
122
|
-
for (let day = 0; day < 7; day++) {
|
|
123
|
-
const dateString = createDateString(currentDate);
|
|
124
|
-
week.push(createEmptyContribution(dateString));
|
|
125
|
-
currentDate.setDate(currentDate.getDate() + 1);
|
|
126
|
-
}
|
|
127
|
-
return week;
|
|
128
|
-
};
|
|
129
|
-
function addExtraWeekIfNeeded(weeks) {
|
|
130
|
-
if (weeks.length === 0) return weeks;
|
|
131
|
-
const lastWeek = weeks[weeks.length - 1];
|
|
132
|
-
const lastDate = new Date(lastWeek[6].date);
|
|
133
|
-
const nextWeekStart = new Date(lastDate);
|
|
134
|
-
nextWeekStart.setDate(lastDate.getDate() + 1);
|
|
135
|
-
const extraWeek = createEmptyWeek(nextWeekStart);
|
|
136
|
-
return [...weeks, extraWeek];
|
|
137
|
-
}
|
|
138
118
|
function ContributionHeatmap({
|
|
139
|
-
data: { period
|
|
140
|
-
className = ""
|
|
119
|
+
data: { period, weeks },
|
|
120
|
+
className = "",
|
|
121
|
+
isReverse = false
|
|
141
122
|
}) {
|
|
142
|
-
const
|
|
143
|
-
const months = getMonthsForHeader({ weeks: adjustedWeeks, period: period2 });
|
|
123
|
+
const months = getMonthsForHeader({ weeks, period });
|
|
144
124
|
return /* @__PURE__ */ jsxs("div", { className: `contribution-heatmap ${className}`, children: [
|
|
145
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ jsx("table", { className: "contribution-heatmap__table", children: isReverse ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
126
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
127
|
+
/* @__PURE__ */ jsx("th", { className: "contribution-heatmap__month-header" }),
|
|
128
|
+
dayNames.map((dayName, dayIndex) => /* @__PURE__ */ jsx("th", { className: "contribution-heatmap__day-header", children: dayName }, dayIndex))
|
|
129
|
+
] }) }),
|
|
130
|
+
/* @__PURE__ */ jsx("tbody", { children: weeks.map((week, weekIndex) => {
|
|
131
|
+
const month = months.find((m) => weekIndex === m.start);
|
|
132
|
+
const showMonthLabel = !!month;
|
|
133
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
134
|
+
showMonthLabel && /* @__PURE__ */ jsx(
|
|
135
|
+
"td",
|
|
136
|
+
{
|
|
137
|
+
className: "contribution-heatmap__month-label",
|
|
138
|
+
rowSpan: month.span,
|
|
139
|
+
children: month.name
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
week.map((contribution, dayIndex) => /* @__PURE__ */ jsx(
|
|
143
|
+
"td",
|
|
144
|
+
{
|
|
145
|
+
className: `contribution-heatmap__day contribution-heatmap__day--level-${contribution.level}`,
|
|
146
|
+
title: formatTooltip(contribution, period),
|
|
147
|
+
"aria-label": formatTooltip(contribution, period),
|
|
148
|
+
role: "button",
|
|
149
|
+
tabIndex: 0,
|
|
150
|
+
"data-count": contribution.count,
|
|
151
|
+
"data-date": contribution.date,
|
|
152
|
+
style: {
|
|
153
|
+
"--animation-delay": `${(weekIndex * 7 + dayIndex) * 5e-3}s`
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
dayIndex
|
|
157
|
+
))
|
|
158
|
+
] }, weekIndex);
|
|
159
|
+
}) })
|
|
160
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
146
161
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
147
162
|
/* @__PURE__ */ jsx("th", { className: "contribution-heatmap__day-header" }),
|
|
148
163
|
months.map((month, index) => /* @__PURE__ */ jsx(
|
|
@@ -156,15 +171,15 @@ function ContributionHeatmap({
|
|
|
156
171
|
))
|
|
157
172
|
] }) }),
|
|
158
173
|
/* @__PURE__ */ jsx("tbody", { children: dayNames.map((dayName, dayIndex) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
159
|
-
/* @__PURE__ */ jsx("td", { className: "contribution-heatmap__day-label", children:
|
|
160
|
-
|
|
174
|
+
/* @__PURE__ */ jsx("td", { className: "contribution-heatmap__day-label", children: dayName }),
|
|
175
|
+
weeks.map((week, weekIndex) => {
|
|
161
176
|
const contribution = week[dayIndex];
|
|
162
177
|
return /* @__PURE__ */ jsx(
|
|
163
178
|
"td",
|
|
164
179
|
{
|
|
165
180
|
className: `contribution-heatmap__day contribution-heatmap__day--level-${contribution.level}`,
|
|
166
|
-
title: formatTooltip(contribution,
|
|
167
|
-
"aria-label": formatTooltip(contribution,
|
|
181
|
+
title: formatTooltip(contribution, period),
|
|
182
|
+
"aria-label": formatTooltip(contribution, period),
|
|
168
183
|
role: "button",
|
|
169
184
|
tabIndex: 0,
|
|
170
185
|
"data-count": contribution.count,
|
|
@@ -177,22 +192,11 @@ function ContributionHeatmap({
|
|
|
177
192
|
);
|
|
178
193
|
})
|
|
179
194
|
] }, dayIndex)) })
|
|
180
|
-
] }),
|
|
195
|
+
] }) }),
|
|
181
196
|
/* @__PURE__ */ jsx(Legend, {})
|
|
182
197
|
] });
|
|
183
198
|
}
|
|
184
199
|
|
|
185
|
-
// src/ContributionHeatmap/getLevel.ts
|
|
186
|
-
var getLevel = (count) => {
|
|
187
|
-
let level = 0;
|
|
188
|
-
if (count === 0) level = 0;
|
|
189
|
-
else if (count <= 2) level = 1;
|
|
190
|
-
else if (count <= 5) level = 2;
|
|
191
|
-
else if (count <= 8) level = 3;
|
|
192
|
-
else level = 4;
|
|
193
|
-
return level;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
200
|
// src/mocks/createRealisticMockedCount.ts
|
|
197
201
|
var isSummerMonth = (month) => {
|
|
198
202
|
return month >= 5 && month <= 7;
|
|
@@ -216,11 +220,20 @@ function createRealisticMockedCount(date) {
|
|
|
216
220
|
return count;
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
// src/mocks/
|
|
223
|
+
// src/mocks/index.tsx
|
|
220
224
|
var getRandomCount = () => Math.floor(Math.random() * 15);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
var getLevel = (count) => {
|
|
226
|
+
let level = 0;
|
|
227
|
+
if (count === 0) level = 0;
|
|
228
|
+
else if (count <= 2) level = 1;
|
|
229
|
+
else if (count <= 5) level = 2;
|
|
230
|
+
else if (count <= 8) level = 3;
|
|
231
|
+
else level = 4;
|
|
232
|
+
return level;
|
|
233
|
+
};
|
|
234
|
+
function getRange(period) {
|
|
235
|
+
if (period !== "lastYear") {
|
|
236
|
+
return period;
|
|
224
237
|
}
|
|
225
238
|
const end = /* @__PURE__ */ new Date();
|
|
226
239
|
const start = /* @__PURE__ */ new Date();
|
|
@@ -228,11 +241,11 @@ function getRange(period2) {
|
|
|
228
241
|
return { start, end };
|
|
229
242
|
}
|
|
230
243
|
function generateMockData({
|
|
231
|
-
period
|
|
244
|
+
period,
|
|
232
245
|
isRealistic
|
|
233
246
|
}) {
|
|
234
247
|
const data = [];
|
|
235
|
-
const { start, end } = getRange(
|
|
248
|
+
const { start, end } = getRange(period);
|
|
236
249
|
for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
|
|
237
250
|
const count = isRealistic ? createRealisticMockedCount(date) : getRandomCount();
|
|
238
251
|
data.push({
|
|
@@ -350,13 +363,17 @@ function ControlGroups({
|
|
|
350
363
|
] }) })
|
|
351
364
|
] });
|
|
352
365
|
}
|
|
353
|
-
var
|
|
354
|
-
var
|
|
366
|
+
var lastYearPeriod = getLastYearPeriod();
|
|
367
|
+
var lastMonthPeriod = getLastMonthPeriod();
|
|
355
368
|
function ContributionHeatmapExample() {
|
|
356
369
|
const [theme, setTheme] = useState("");
|
|
357
370
|
const [hasRealisticData, setHasRealisticData] = useState(false);
|
|
358
|
-
const
|
|
359
|
-
period,
|
|
371
|
+
const lastYearContribution = hasRealisticData ? generateMockData({ period: lastYearPeriod, isRealistic: true }) : generateMockData({
|
|
372
|
+
period: lastYearPeriod,
|
|
373
|
+
isRealistic: false
|
|
374
|
+
});
|
|
375
|
+
const lastMonthContribution = hasRealisticData ? generateMockData({ period: lastMonthPeriod, isRealistic: true }) : generateMockData({
|
|
376
|
+
period: lastMonthPeriod,
|
|
360
377
|
isRealistic: false
|
|
361
378
|
});
|
|
362
379
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -371,9 +388,21 @@ function ContributionHeatmapExample() {
|
|
|
371
388
|
{
|
|
372
389
|
className: theme ? `contribution-heatmap--${theme}` : "",
|
|
373
390
|
data: {
|
|
374
|
-
weeks: groupByWeeks(
|
|
375
|
-
contribution,
|
|
376
|
-
period
|
|
391
|
+
weeks: groupByWeeks(lastYearContribution),
|
|
392
|
+
contribution: lastYearContribution,
|
|
393
|
+
period: lastYearPeriod
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
) }),
|
|
397
|
+
/* @__PURE__ */ jsx("div", { className: "example__heatmap", children: /* @__PURE__ */ jsx(
|
|
398
|
+
ContributionHeatmap,
|
|
399
|
+
{
|
|
400
|
+
isReverse: true,
|
|
401
|
+
className: theme ? `contribution-heatmap--${theme}` : "",
|
|
402
|
+
data: {
|
|
403
|
+
weeks: groupByWeeks(lastMonthContribution),
|
|
404
|
+
contribution: lastMonthContribution,
|
|
405
|
+
period: lastMonthPeriod
|
|
377
406
|
}
|
|
378
407
|
}
|
|
379
408
|
) })
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/models.tsx","../src/ContributionHeatmap/getMonthsForHeader.ts","../src/shared/formatTooltip.tsx","../src/shared/Legend.tsx","../src/ContributionHeatmap/addExtraWeekIfNeeded.ts","../src/ContributionHeatmap/index.tsx","../src/ContributionHeatmap/getLevel.ts","../src/mocks/createRealisticMockedCount.ts","../src/mocks/generateMockData.tsx","../src/shared/groupByWeeks.ts","../src/Example/ControlGroups.tsx","../src/Example/index.tsx"],"names":["period","jsxs","jsx","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 '@/shared/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","import type { ContributionData, Period } from '@/shared/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","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","import { createDateString, type ContributionData, type Week } from '@/shared/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[]): 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 '../shared/models';\nimport { getMonthsForHeader } from './getMonthsForHeader';\nimport { formatTooltip } from '@/shared/formatTooltip';\nimport { dayNames } from '@/shared/models';\nimport { Legend } from '@/shared/Legend';\nimport { addExtraWeekIfNeeded } from './addExtraWeekIfNeeded';\n\ninterface ContributionHeatmapProps {\n data: { contribution: ContributionData[]; period: Period; weeks: WeekType[] };\n className?: string;\n}\n\nfunction ContributionHeatmap({\n data: { period, weeks },\n className = '',\n}: ContributionHeatmapProps) {\n // Add extra week if needed to match header span\n const adjustedWeeks = addExtraWeekIfNeeded(weeks);\n const months = getMonthsForHeader({ weeks: adjustedWeeks, period });\n\n return (\n <div className={`contribution-heatmap ${className}`}>\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 '@/shared/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 type { ContributionData } from '@/shared/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 { createDateString, type ContributionData, type Period } from '@/shared/models';\nimport { getLevel } from '@/ContributionHeatmap/getLevel';\nimport { createRealisticMockedCount } from './createRealisticMockedCount';\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 '@/shared/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 '@/shared/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 '@/ContributionHeatmap';\nimport { generateMockData } from '@/mocks/generateMockData';\nimport { getLastYearPeriod, type Theme } from '@/shared/models';\nimport { groupByWeeks } from '@/shared/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"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/models.tsx","../src/ContributionHeatmap/getMonthsForHeader.ts","../src/shared/formatTooltip.tsx","../src/shared/Legend.tsx","../src/ContributionHeatmap/index.tsx","../src/mocks/createRealisticMockedCount.ts","../src/mocks/index.tsx","../src/shared/groupByWeeks.ts","../src/Example/ControlGroups.tsx","../src/Example/index.tsx"],"names":["jsxs","jsx","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\nfunction getLastMonthPeriod(): Period {\n const now = new Date();\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const start = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate() + 1);\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, getLastMonthPeriod };\n","import { monthNames, type Period, type Week } from '@/shared/models';\n\nfunction getMonthsForHeader({\n weeks,\n period,\n}: {\n weeks: Week[];\n period: Period;\n}): {\n name: string;\n span: number;\n start: number;\n}[] {\n const months: { name: string; span: number; start: number }[] = [];\n const { start, end } = period;\n let currentMonth = -1;\n let currentSpan = 0;\n\n weeks.forEach((week, weekIndex) => {\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, start: weekIndex - 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, start: weeks.length - 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","import type { ContributionData, Period } from '@/shared/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","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","import './index.scss';\nimport type { ContributionData, Period, Week as WeekType } from '../shared/models';\nimport { getMonthsForHeader } from './getMonthsForHeader';\nimport { formatTooltip } from '@/shared/formatTooltip';\nimport { dayNames } from '@/shared/models';\nimport { Legend } from '@/shared/Legend';\n\n\ninterface ContributionHeatmapProps {\n data: { contribution: ContributionData[]; period: Period; weeks: WeekType[] };\n className?: string;\n isReverse?: boolean;\n}\n\nfunction ContributionHeatmap({\n data: { period, weeks },\n className = '',\n isReverse = false,\n}: ContributionHeatmapProps) {\n // Add extra week if needed to match header span\n \n const months = getMonthsForHeader({ weeks, period });\n\n return (\n <div className={`contribution-heatmap ${className}`}>\n <table className=\"contribution-heatmap__table\">\n {isReverse ? (\n <>\n <thead>\n <tr>\n <th className=\"contribution-heatmap__month-header\"></th>\n {dayNames.map((dayName, dayIndex) => (\n <th key={dayIndex} className=\"contribution-heatmap__day-header\">\n {dayName}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {weeks.map((week, weekIndex) => {\n // Find month info for this week\n const month = months.find(m => weekIndex === m.start);\n const showMonthLabel = !!month;\n return (\n <tr key={weekIndex}>\n {showMonthLabel && (\n <td\n className=\"contribution-heatmap__month-label\"\n rowSpan={month.span}\n >\n {month.name}\n </td>\n )}\n {week.map((contribution, dayIndex) => (\n <td\n key={dayIndex}\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 </tr>\n );\n })}\n </tbody>\n </>\n ) : (\n <>\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 {dayName}\n </td>\n {weeks.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 </>\n )}\n </table>\n <Legend />\n </div>\n );\n}\n\nexport { ContributionHeatmap };\n","import type { ContributionData } from '@/shared/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 { createDateString, type ContributionData, type Period } from '@/shared/models';\nimport { createRealisticMockedCount } from './createRealisticMockedCount';\n\nconst getRandomCount = () => Math.floor(Math.random() * 15);\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\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 '@/shared/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 '@/shared/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 '@/ContributionHeatmap';\nimport { generateMockData } from '@/mocks';\nimport { getLastMonthPeriod, getLastYearPeriod, type Theme } from '@/shared/models';\nimport { groupByWeeks } from '@/shared/groupByWeeks';\nimport './index.scss';\nimport { ControlGroups } from './ControlGroups';\n\nconst lastYearPeriod = getLastYearPeriod();\nconst lastMonthPeriod = getLastMonthPeriod();\n\nfunction ContributionHeatmapExample() {\n const [theme, setTheme] = useState<Theme>('');\n const [hasRealisticData, setHasRealisticData] = useState(false);\n\n const lastYearContribution = hasRealisticData\n ? generateMockData({ period: lastYearPeriod, isRealistic: true })\n : generateMockData({\n period: lastYearPeriod,\n isRealistic: false,\n });\n\n const lastMonthContribution = hasRealisticData\n ? generateMockData({ period: lastMonthPeriod, isRealistic: true })\n : generateMockData({\n period: lastMonthPeriod,\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(lastYearContribution),\n contribution: lastYearContribution,\n period: lastYearPeriod,\n }}\n />\n </div>\n\n <div className=\"example__heatmap\">\n <ContributionHeatmap\n isReverse\n className={theme ? `contribution-heatmap--${theme}` : ''}\n data={{\n weeks: groupByWeeks(lastMonthContribution),\n contribution: lastMonthContribution,\n period: lastMonthPeriod,\n }}\n />\n </div>\n </>\n \n );\n}\n\nexport { ContributionHeatmapExample };\n"]}
|