@siyahi/ui 0.0.0-stage → 0.1.0-next.3
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/LICENSE +21 -0
- package/README.md +174 -2
- package/THIRD_PARTY_NOTICES.md +51 -0
- package/dist/components/registry/dashboard-01/section-cards.d.ts +17 -0
- package/dist/components/registry/dashboard-01/section-cards.js +39 -0
- package/dist/components/registry/dashboardcn/bar-list.d.ts +24 -0
- package/dist/components/registry/dashboardcn/bar-list.js +71 -0
- package/dist/components/registry/heatmap/calendar-heatmap.d.ts +134 -0
- package/dist/components/registry/heatmap/calendar-heatmap.js +497 -0
- package/dist/components/registry/heatmap/status-heatmap.d.ts +120 -0
- package/dist/components/registry/heatmap/status-heatmap.js +330 -0
- package/dist/components/registry/heatmap/weekday-heatmap.d.ts +145 -0
- package/dist/components/registry/heatmap/weekday-heatmap.js +489 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.d.ts +26 -0
- package/dist/components/registry/niko-table/components/data-table-column-actions.js +32 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.d.ts +13 -0
- package/dist/components/registry/niko-table/components/data-table-column-filter.js +17 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.d.ts +30 -0
- package/dist/components/registry/niko-table/components/data-table-column-header.js +35 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.d.ts +21 -0
- package/dist/components/registry/niko-table/components/data-table-column-sort.js +36 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.d.ts +12 -0
- package/dist/components/registry/niko-table/components/data-table-column-title.js +18 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.d.ts +108 -0
- package/dist/components/registry/niko-table/components/data-table-empty-state.js +148 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.d.ts +46 -0
- package/dist/components/registry/niko-table/components/data-table-faceted-filter.js +133 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.d.ts +111 -0
- package/dist/components/registry/niko-table/components/data-table-grouped-rows.js +77 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.d.ts +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu-slot.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.d.ts +50 -0
- package/dist/components/registry/niko-table/components/data-table-row-context-menu.js +42 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.d.ts +51 -0
- package/dist/components/registry/niko-table/components/data-table-row-menu.js +65 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.d.ts +7 -0
- package/dist/components/registry/niko-table/components/data-table-toolbar-section.js +63 -0
- package/dist/components/registry/niko-table/config/data-table.d.ts +156 -0
- package/dist/components/registry/niko-table/config/data-table.js +279 -0
- package/dist/components/registry/niko-table/config/feature-detection.d.ts +39 -0
- package/dist/components/registry/niko-table/config/feature-detection.js +174 -0
- package/dist/components/registry/niko-table/core/data-table-context.d.ts +157 -0
- package/dist/components/registry/niko-table/core/data-table-context.js +326 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.d.ts +85 -0
- package/dist/components/registry/niko-table/core/data-table-error-boundary.js +116 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.d.ts +49 -0
- package/dist/components/registry/niko-table/core/data-table-grouped-row.js +112 -0
- package/dist/components/registry/niko-table/core/data-table-root.d.ts +62 -0
- package/dist/components/registry/niko-table/core/data-table-root.js +406 -0
- package/dist/components/registry/niko-table/core/data-table-structure.d.ts +153 -0
- package/dist/components/registry/niko-table/core/data-table-structure.js +370 -0
- package/dist/components/registry/niko-table/core/data-table.d.ts +30 -0
- package/dist/components/registry/niko-table/core/data-table.js +126 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.d.ts +49 -0
- package/dist/components/registry/niko-table/filters/table-column-actions.js +48 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.d.ts +51 -0
- package/dist/components/registry/niko-table/filters/table-column-sort.js +184 -0
- package/dist/components/registry/niko-table/filters/table-column-title.d.ts +17 -0
- package/dist/components/registry/niko-table/filters/table-column-title.js +20 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.d.ts +40 -0
- package/dist/components/registry/niko-table/filters/table-faceted-filter.js +210 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.d.ts +29 -0
- package/dist/components/registry/niko-table/hooks/use-derived-column-title.js +40 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.d.ts +49 -0
- package/dist/components/registry/niko-table/hooks/use-generated-options.js +144 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.d.ts +59 -0
- package/dist/components/registry/niko-table/lib/build-faceted-options.js +82 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/column-resize-handle.js +175 -0
- package/dist/components/registry/niko-table/lib/constants.d.ts +128 -0
- package/dist/components/registry/niko-table/lib/constants.js +123 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.d.ts +35 -0
- package/dist/components/registry/niko-table/lib/create-scroll-handler.js +63 -0
- package/dist/components/registry/niko-table/lib/data-table-features.d.ts +218 -0
- package/dist/components/registry/niko-table/lib/data-table-features.js +127 -0
- package/dist/components/registry/niko-table/lib/filter-functions.d.ts +47 -0
- package/dist/components/registry/niko-table/lib/filter-functions.js +309 -0
- package/dist/components/registry/niko-table/lib/filter-rows.d.ts +14 -0
- package/dist/components/registry/niko-table/lib/filter-rows.js +28 -0
- package/dist/components/registry/niko-table/lib/flex-columns.d.ts +48 -0
- package/dist/components/registry/niko-table/lib/flex-columns.js +65 -0
- package/dist/components/registry/niko-table/lib/format.d.ts +33 -0
- package/dist/components/registry/niko-table/lib/format.js +99 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/render-cell-content.js +65 -0
- package/dist/components/registry/niko-table/lib/row-click.d.ts +67 -0
- package/dist/components/registry/niko-table/lib/row-click.js +28 -0
- package/dist/components/registry/niko-table/lib/styles.d.ts +6 -0
- package/dist/components/registry/niko-table/lib/styles.js +24 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.d.ts +17 -0
- package/dist/components/registry/niko-table/lib/use-header-min-widths.js +82 -0
- package/dist/components/registry/niko-table/types/index.d.ts +141 -0
- package/dist/components/registry/niko-table/types/index.js +2 -0
- package/dist/components/registry/niko-table/ui/table.d.ts +13 -0
- package/dist/components/registry/niko-table/ui/table.js +74 -0
- package/dist/components/registry/sidebar-07/app-sidebar.d.ts +18 -0
- package/dist/components/registry/sidebar-07/app-sidebar.js +26 -0
- package/dist/components/registry/sidebar-07/nav-main.d.ts +15 -0
- package/dist/components/registry/sidebar-07/nav-main.js +25 -0
- package/dist/components/registry/sidebar-07/nav-projects.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-projects.js +43 -0
- package/dist/components/registry/sidebar-07/nav-user.d.ts +9 -0
- package/dist/components/registry/sidebar-07/nav-user.js +70 -0
- package/dist/components/registry/sidebar-07/team-switcher.d.ts +10 -0
- package/dist/components/registry/sidebar-07/team-switcher.js +70 -0
- package/dist/components/siyahi/delta.d.ts +10 -0
- package/dist/components/siyahi/delta.js +27 -0
- package/dist/components/siyahi/local-time.d.ts +7 -0
- package/dist/components/siyahi/local-time.js +22 -0
- package/dist/components/siyahi/price-chart.d.ts +12 -0
- package/dist/components/siyahi/price-chart.js +80 -0
- package/dist/components/siyahi/siyahi-provider.d.ts +12 -0
- package/dist/components/siyahi/siyahi-provider.js +31 -0
- package/dist/components/siyahi/treemap.d.ts +14 -0
- package/dist/components/siyahi/treemap.js +58 -0
- package/dist/components/ui/alert.d.ts +12 -0
- package/dist/components/ui/alert.js +43 -0
- package/dist/components/ui/avatar.d.ts +13 -0
- package/dist/components/ui/avatar.js +50 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/badge.js +29 -0
- package/dist/components/ui/breadcrumb.d.ts +12 -0
- package/dist/components/ui/breadcrumb.js +72 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +44 -0
- package/dist/components/ui/calendar.d.ts +12 -0
- package/dist/components/ui/calendar.js +107 -0
- package/dist/components/ui/card.d.ts +13 -0
- package/dist/components/ui/card.js +56 -0
- package/dist/components/ui/chart.d.ts +46 -0
- package/dist/components/ui/chart.js +149 -0
- package/dist/components/ui/collapsible.d.ts +7 -0
- package/dist/components/ui/collapsible.js +24 -0
- package/dist/components/ui/command.d.ts +21 -0
- package/dist/components/ui/command.js +88 -0
- package/dist/components/ui/context-menu.d.ts +31 -0
- package/dist/components/ui/context-menu.js +132 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dialog.js +91 -0
- package/dist/components/ui/dropdown-menu.d.ts +31 -0
- package/dist/components/ui/dropdown-menu.js +136 -0
- package/dist/components/ui/input-group.d.ts +20 -0
- package/dist/components/ui/input-group.js +79 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/input.js +15 -0
- package/dist/components/ui/popover.d.ts +11 -0
- package/dist/components/ui/popover.js +55 -0
- package/dist/components/ui/progress.d.ts +9 -0
- package/dist/components/ui/progress.js +43 -0
- package/dist/components/ui/scroll-area.d.ts +7 -0
- package/dist/components/ui/scroll-area.js +36 -0
- package/dist/components/ui/select.d.ts +17 -0
- package/dist/components/ui/select.js +97 -0
- package/dist/components/ui/separator.d.ts +5 -0
- package/dist/components/ui/separator.js +15 -0
- package/dist/components/ui/sheet.d.ts +16 -0
- package/dist/components/ui/sheet.js +89 -0
- package/dist/components/ui/sidebar.d.ts +65 -0
- package/dist/components/ui/sidebar.js +385 -0
- package/dist/components/ui/skeleton.d.ts +4 -0
- package/dist/components/ui/skeleton.js +12 -0
- package/dist/components/ui/slider.d.ts +5 -0
- package/dist/components/ui/slider.js +33 -0
- package/dist/components/ui/table.d.ts +12 -0
- package/dist/components/ui/table.js +66 -0
- package/dist/components/ui/tabs.d.ts +12 -0
- package/dist/components/ui/tabs.js +45 -0
- package/dist/components/ui/textarea.d.ts +5 -0
- package/dist/components/ui/textarea.js +13 -0
- package/dist/components/ui/toggle-group.d.ts +13 -0
- package/dist/components/ui/toggle-group.js +52 -0
- package/dist/components/ui/toggle.d.ts +10 -0
- package/dist/components/ui/toggle.js +36 -0
- package/dist/components/ui/tooltip.d.ts +8 -0
- package/dist/components/ui/tooltip.js +41 -0
- package/dist/hooks/use-mobile.d.ts +3 -0
- package/dist/hooks/use-mobile.js +18 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +71 -0
- package/dist/lib/format.d.ts +24 -0
- package/dist/lib/format.js +58 -0
- package/dist/lib/locale.d.ts +12 -0
- package/dist/lib/locale.js +8 -0
- package/dist/lib/recharts.d.ts +2 -0
- package/dist/lib/recharts.js +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +1 -0
- package/package.json +78 -3
- package/registry.lock.json +413 -0
- package/report.css +129 -0
- package/styles/shadcn.css +643 -0
- package/styles/siyahi.css +189 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { Fragment, createContext, use, useMemo } from "react";
|
|
3
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { differenceInCalendarDays, eachDayOfInterval, format, formatISO, getDay, getMonth, getYear, nextDay, parseISO, subWeeks } from "date-fns";
|
|
5
|
+
//#region src/components/registry/heatmap/calendar-heatmap.tsx
|
|
6
|
+
const colorStepCount = (levels, isNormalized) => Math.max(1, isNormalized ? levels : levels - 1);
|
|
7
|
+
const getLevelFill = (level, levels, isNormalized, highlighted = false, colors) => {
|
|
8
|
+
const emptyColor = colors?.empty ?? "var(--color-secondary)";
|
|
9
|
+
const scaleColor = colors?.scale ?? "var(--color-chart-1)";
|
|
10
|
+
if (level === 0) return emptyColor;
|
|
11
|
+
const steps = colorStepCount(levels, isNormalized);
|
|
12
|
+
const opacity = steps === 1 ? 100 : Math.round(20 + (level - 1) * 80 / (steps - 1));
|
|
13
|
+
return `color-mix(in oklch, ${scaleColor} ${highlighted ? Math.round(opacity * .6) : opacity}%, transparent)`;
|
|
14
|
+
};
|
|
15
|
+
const calculateLevel = (value, minValue, maxValue, levels, isNormalized) => {
|
|
16
|
+
const steps = colorStepCount(levels, isNormalized);
|
|
17
|
+
if (!Number.isFinite(value)) return isNormalized ? 1 : 0;
|
|
18
|
+
if (isNormalized) {
|
|
19
|
+
if (maxValue <= minValue) return 1;
|
|
20
|
+
const percentage = (value - minValue) / (maxValue - minValue);
|
|
21
|
+
return Math.max(1, Math.min(steps, Math.ceil(percentage * steps)));
|
|
22
|
+
}
|
|
23
|
+
if (value <= 0 || maxValue <= 0) return 0;
|
|
24
|
+
const percentage = value / maxValue;
|
|
25
|
+
return Math.max(1, Math.min(steps, Math.ceil(percentage * steps)));
|
|
26
|
+
};
|
|
27
|
+
const generateMonthLabels = (locale) => {
|
|
28
|
+
return Array.from({ length: 12 }, (_, i) => {
|
|
29
|
+
const date = new Date(2e3, i, 1);
|
|
30
|
+
if (locale) return format(date, "LLL", { locale });
|
|
31
|
+
return date.toLocaleString("en-US", { month: "short" });
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const generateWeekdayLabels = (locale) => {
|
|
35
|
+
return Array.from({ length: 7 }, (_, i) => {
|
|
36
|
+
const date = new Date(2e3, 0, 2 + i);
|
|
37
|
+
if (locale) return format(date, "EEE", { locale });
|
|
38
|
+
return date.toLocaleString("en-US", { weekday: "short" });
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const EMPTY_STYLE = {};
|
|
42
|
+
const LABEL_MARGIN = 8;
|
|
43
|
+
const CalendarHeatmapContext = createContext(null);
|
|
44
|
+
const useCalendarHeatmap = () => {
|
|
45
|
+
const context = use(CalendarHeatmapContext);
|
|
46
|
+
if (!context) throw new Error("CalendarHeatmap components must be used within a CalendarHeatmap");
|
|
47
|
+
return context;
|
|
48
|
+
};
|
|
49
|
+
const fillHoles = (activities) => {
|
|
50
|
+
if (activities.length === 0) return [];
|
|
51
|
+
const sortedActivities = [...activities].sort((a, b) => a.date.localeCompare(b.date));
|
|
52
|
+
const calendar = new Map(activities.map((a) => [a.date, a]));
|
|
53
|
+
const firstActivity = sortedActivities[0];
|
|
54
|
+
const lastActivity = sortedActivities.at(-1);
|
|
55
|
+
if (!lastActivity) return [];
|
|
56
|
+
return eachDayOfInterval({
|
|
57
|
+
start: parseISO(firstActivity.date),
|
|
58
|
+
end: parseISO(lastActivity.date)
|
|
59
|
+
}).map((day) => {
|
|
60
|
+
const date = formatISO(day, { representation: "date" });
|
|
61
|
+
if (calendar.has(date)) return calendar.get(date);
|
|
62
|
+
return {
|
|
63
|
+
date,
|
|
64
|
+
value: 0,
|
|
65
|
+
level: 0
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const groupByYearAndMonth = (activities, weekStart = 0, hasEmptyColumn = false) => {
|
|
70
|
+
if (activities.length === 0) return [];
|
|
71
|
+
const normalizedActivities = fillHoles(activities);
|
|
72
|
+
const activitiesByYearMonth = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const activity of normalizedActivities) {
|
|
74
|
+
const date = parseISO(activity.date);
|
|
75
|
+
const year = getYear(date);
|
|
76
|
+
const month = getMonth(date);
|
|
77
|
+
const key = `${year}-${String(month).padStart(2, "0")}`;
|
|
78
|
+
if (!activitiesByYearMonth.has(key)) activitiesByYearMonth.set(key, []);
|
|
79
|
+
activitiesByYearMonth.get(key)?.push(activity);
|
|
80
|
+
}
|
|
81
|
+
const sortedKeys = Array.from(activitiesByYearMonth.keys()).sort();
|
|
82
|
+
if (sortedKeys.length === 0) return [];
|
|
83
|
+
const [firstYearStr, firstMonthStr] = sortedKeys[0].split("-");
|
|
84
|
+
const startYear = parseInt(firstYearStr, 10);
|
|
85
|
+
const startMonth = parseInt(firstMonthStr, 10);
|
|
86
|
+
const yearRows = [];
|
|
87
|
+
let currentYear = startYear;
|
|
88
|
+
let currentMonth = startMonth;
|
|
89
|
+
while (true) {
|
|
90
|
+
const rowWeeks = [];
|
|
91
|
+
let hasData = false;
|
|
92
|
+
for (let monthOffset = 0; monthOffset < 12; monthOffset++) {
|
|
93
|
+
const monthIndex = (currentMonth + monthOffset) % 12;
|
|
94
|
+
const yearOffset = Math.floor((currentMonth + monthOffset) / 12);
|
|
95
|
+
const key = `${currentYear + yearOffset}-${String(monthIndex).padStart(2, "0")}`;
|
|
96
|
+
const monthActivities = activitiesByYearMonth.get(key) || [];
|
|
97
|
+
if (monthActivities.length > 0) {
|
|
98
|
+
hasData = true;
|
|
99
|
+
const firstActivity = monthActivities[0];
|
|
100
|
+
const firstDate = parseISO(firstActivity.date);
|
|
101
|
+
const firstCalendarDate = getDay(firstDate) === weekStart ? firstDate : subWeeks(nextDay(firstDate, weekStart), 1);
|
|
102
|
+
const paddedActivities = [...new Array(differenceInCalendarDays(firstDate, firstCalendarDate)).fill(void 0), ...monthActivities];
|
|
103
|
+
const numberOfWeeks = Math.ceil(paddedActivities.length / 7);
|
|
104
|
+
const monthWeeks = new Array(numberOfWeeks).fill(void 0).map((_, weekIndex) => paddedActivities.slice(weekIndex * 7, weekIndex * 7 + 7));
|
|
105
|
+
if (hasEmptyColumn && rowWeeks.length > 0) rowWeeks.push(new Array(7).fill(void 0));
|
|
106
|
+
rowWeeks.push(...monthWeeks);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (!hasData) break;
|
|
110
|
+
yearRows.push({
|
|
111
|
+
year: currentYear,
|
|
112
|
+
startMonth: currentMonth,
|
|
113
|
+
weeks: rowWeeks
|
|
114
|
+
});
|
|
115
|
+
currentYear++;
|
|
116
|
+
currentMonth = startMonth;
|
|
117
|
+
if (!sortedKeys.some((key) => {
|
|
118
|
+
const [yearStr] = key.split("-");
|
|
119
|
+
return parseInt(yearStr, 10) >= currentYear;
|
|
120
|
+
})) break;
|
|
121
|
+
}
|
|
122
|
+
return yearRows;
|
|
123
|
+
};
|
|
124
|
+
const groupContinuous = (activities, weekStart = 0) => {
|
|
125
|
+
if (activities.length === 0) return [];
|
|
126
|
+
const normalizedActivities = fillHoles(activities);
|
|
127
|
+
const first = normalizedActivities[0];
|
|
128
|
+
const firstDate = parseISO(first.date);
|
|
129
|
+
const firstCalendarDate = getDay(firstDate) === weekStart ? firstDate : subWeeks(nextDay(firstDate, weekStart), 1);
|
|
130
|
+
const leadingPad = differenceInCalendarDays(firstDate, firstCalendarDate);
|
|
131
|
+
const padded = [...new Array(leadingPad).fill(void 0), ...normalizedActivities];
|
|
132
|
+
const numberOfWeeks = Math.ceil(padded.length / 7);
|
|
133
|
+
const allWeeks = new Array(numberOfWeeks).fill(void 0).map((_, i) => padded.slice(i * 7, i * 7 + 7));
|
|
134
|
+
const yearMap = /* @__PURE__ */ new Map();
|
|
135
|
+
for (const week of allWeeks) {
|
|
136
|
+
const firstActivity = week.find((a) => a !== void 0);
|
|
137
|
+
const year = firstActivity ? getYear(parseISO(firstActivity.date)) : void 0;
|
|
138
|
+
if (year !== void 0) {
|
|
139
|
+
if (!yearMap.has(year)) yearMap.set(year, []);
|
|
140
|
+
yearMap.get(year)?.push(week);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return Array.from(yearMap.entries()).sort(([a], [b]) => a - b).map(([year, weeks]) => ({
|
|
144
|
+
year,
|
|
145
|
+
startMonth: 0,
|
|
146
|
+
weeks
|
|
147
|
+
}));
|
|
148
|
+
};
|
|
149
|
+
const getMonthLabels = (weeks, monthNames = generateMonthLabels()) => {
|
|
150
|
+
return weeks.reduce((labels, week, weekIndex) => {
|
|
151
|
+
const firstActivity = week.find((activity) => activity !== void 0);
|
|
152
|
+
if (!firstActivity) return labels;
|
|
153
|
+
const month = monthNames[getMonth(parseISO(firstActivity.date))];
|
|
154
|
+
if (!month) {
|
|
155
|
+
const monthName = new Date(firstActivity.date).toLocaleString("en-US", { month: "short" });
|
|
156
|
+
throw new Error(`Unexpected error: undefined month label for ${monthName}.`);
|
|
157
|
+
}
|
|
158
|
+
const prevLabel = labels.at(-1);
|
|
159
|
+
if (weekIndex === 0 || !prevLabel || prevLabel.label !== month) return labels.concat({
|
|
160
|
+
weekIndex,
|
|
161
|
+
label: month
|
|
162
|
+
});
|
|
163
|
+
return labels;
|
|
164
|
+
}, []).filter(({ weekIndex }, index, labels) => {
|
|
165
|
+
const minWeeks = 3;
|
|
166
|
+
if (index === 0) return labels[1] && labels[1].weekIndex - weekIndex >= minWeeks;
|
|
167
|
+
if (index === labels.length - 1) return weeks.slice(weekIndex).length >= minWeeks;
|
|
168
|
+
return true;
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Calendar Heatmap
|
|
173
|
+
*
|
|
174
|
+
* A GitHub-style contribution calendar showing daily activity over months and years.
|
|
175
|
+
* Each cell represents one day, arranged in weeks (rows) and months (columns).
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```tsx
|
|
179
|
+
* <CalendarHeatmap data={data} weekStart={1} continuousMonths>
|
|
180
|
+
* <CalendarHeatmapBody>
|
|
181
|
+
* {({ activity, dayIndex, weekIndex }) => (
|
|
182
|
+
* <CalendarHeatmapBlock
|
|
183
|
+
* activity={activity}
|
|
184
|
+
* dayIndex={dayIndex}
|
|
185
|
+
* weekIndex={weekIndex}
|
|
186
|
+
* />
|
|
187
|
+
* )}
|
|
188
|
+
* </CalendarHeatmapBody>
|
|
189
|
+
* <CalendarHeatmapFooter>
|
|
190
|
+
* <CalendarHeatmapStat />
|
|
191
|
+
* <CalendarHeatmapLegend />
|
|
192
|
+
* </CalendarHeatmapFooter>
|
|
193
|
+
* </CalendarHeatmap>
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* @param data - Array of activities with date (YYYY-MM-DD) and value
|
|
197
|
+
* @param weekStart - First day of week (0=Sunday, 1=Monday). Default: 0
|
|
198
|
+
* @param continuousMonths - Display months continuously vs. grouped by year. Default: true
|
|
199
|
+
* @param hasEmptyColumn - Add empty column between months. Default: false
|
|
200
|
+
* @param blockAspectRatio - Width/height ratio of blocks. Default: 1
|
|
201
|
+
* @param levels - Total number of legend cells (including empty when not normalized). Default: 5
|
|
202
|
+
* @param isNormalized - When true, uses min-max normalization across the dataset (suitable for signed values). When false (default), treats 0 as empty and scales from 0 to max.
|
|
203
|
+
*/
|
|
204
|
+
const CalendarHeatmap = ({ data, weekStart = 0, continuousMonths = true, hasEmptyColumn = false, blockSize = 12, blockMargin = 4, blockRadius = 2, blockAspectRatio = 1, levels: levelsProp = 5, isNormalized = false, colors, locale, labels: labelsProp, fontSize = 14, emptyState, totalCount: totalCountProp, style = EMPTY_STYLE, className, children, ...props }) => {
|
|
205
|
+
const levels = Math.max(1, levelsProp);
|
|
206
|
+
const dataWithLevels = useMemo(() => {
|
|
207
|
+
if (data.length === 0) return [];
|
|
208
|
+
const maxCount = data.reduce((max, d) => Math.max(max, d.value), 1);
|
|
209
|
+
const minCount = isNormalized ? data.reduce((min, d) => Math.min(min, d.value), Infinity) : 0;
|
|
210
|
+
return data.map((activity) => ({
|
|
211
|
+
...activity,
|
|
212
|
+
level: calculateLevel(activity.value, minCount, maxCount, levels, isNormalized)
|
|
213
|
+
}));
|
|
214
|
+
}, [
|
|
215
|
+
data,
|
|
216
|
+
levels,
|
|
217
|
+
isNormalized
|
|
218
|
+
]);
|
|
219
|
+
const yearRows = useMemo(() => continuousMonths ? groupContinuous(dataWithLevels, weekStart) : groupByYearAndMonth(dataWithLevels, weekStart, hasEmptyColumn), [
|
|
220
|
+
dataWithLevels,
|
|
221
|
+
weekStart,
|
|
222
|
+
hasEmptyColumn,
|
|
223
|
+
continuousMonths
|
|
224
|
+
]);
|
|
225
|
+
const weeks = useMemo(() => yearRows.flatMap((r) => r.weeks), [yearRows]);
|
|
226
|
+
const labels = useMemo(() => ({
|
|
227
|
+
months: generateMonthLabels(locale),
|
|
228
|
+
weekdays: generateWeekdayLabels(locale),
|
|
229
|
+
cellLabel: "{{date}}: {{value}} contributions",
|
|
230
|
+
heatmapLabel: "Contribution heatmap for {{year}}",
|
|
231
|
+
legendLabel: "Activity intensity legend",
|
|
232
|
+
legendLevelLabel: "{{level}} contributions",
|
|
233
|
+
...labelsProp
|
|
234
|
+
}), [locale, labelsProp]);
|
|
235
|
+
const labelHeight = fontSize + LABEL_MARGIN;
|
|
236
|
+
const year = data.length > 0 ? getYear(parseISO(data[0].date)) : (/* @__PURE__ */ new Date()).getFullYear();
|
|
237
|
+
const totalCount = typeof totalCountProp === "number" ? totalCountProp : dataWithLevels.reduce((sum, activity) => sum + activity.value, 0);
|
|
238
|
+
const blockWidth = blockSize * blockAspectRatio;
|
|
239
|
+
const width = weeks.length * (blockWidth + blockMargin) - blockMargin;
|
|
240
|
+
const height = labelHeight + (blockSize + blockMargin) * 7 - blockMargin;
|
|
241
|
+
const contextValue = useMemo(() => ({
|
|
242
|
+
data: dataWithLevels,
|
|
243
|
+
weeks,
|
|
244
|
+
yearRows,
|
|
245
|
+
blockMargin,
|
|
246
|
+
blockRadius,
|
|
247
|
+
blockSize,
|
|
248
|
+
blockAspectRatio,
|
|
249
|
+
blockWidth,
|
|
250
|
+
fontSize,
|
|
251
|
+
labels,
|
|
252
|
+
labelHeight,
|
|
253
|
+
levels,
|
|
254
|
+
isNormalized,
|
|
255
|
+
totalCount,
|
|
256
|
+
weekStart,
|
|
257
|
+
year,
|
|
258
|
+
width,
|
|
259
|
+
height,
|
|
260
|
+
hasEmptyColumn,
|
|
261
|
+
continuousMonths,
|
|
262
|
+
locale,
|
|
263
|
+
colors
|
|
264
|
+
}), [
|
|
265
|
+
dataWithLevels,
|
|
266
|
+
weeks,
|
|
267
|
+
yearRows,
|
|
268
|
+
blockMargin,
|
|
269
|
+
blockRadius,
|
|
270
|
+
blockSize,
|
|
271
|
+
blockAspectRatio,
|
|
272
|
+
blockWidth,
|
|
273
|
+
fontSize,
|
|
274
|
+
labels,
|
|
275
|
+
labelHeight,
|
|
276
|
+
levels,
|
|
277
|
+
isNormalized,
|
|
278
|
+
totalCount,
|
|
279
|
+
weekStart,
|
|
280
|
+
year,
|
|
281
|
+
width,
|
|
282
|
+
height,
|
|
283
|
+
hasEmptyColumn,
|
|
284
|
+
continuousMonths,
|
|
285
|
+
locale,
|
|
286
|
+
colors
|
|
287
|
+
]);
|
|
288
|
+
if (data.length === 0) return emptyState ? emptyState : null;
|
|
289
|
+
return /* @__PURE__ */ jsx(CalendarHeatmapContext, {
|
|
290
|
+
value: contextValue,
|
|
291
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
292
|
+
"data-slot": "calendar-heatmap",
|
|
293
|
+
className: cn("flex w-max max-w-full flex-col gap-2 p-4", className),
|
|
294
|
+
style: {
|
|
295
|
+
fontSize,
|
|
296
|
+
...style
|
|
297
|
+
},
|
|
298
|
+
...props,
|
|
299
|
+
children
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
const CalendarHeatmapBlock = ({ ref, activity, dayIndex, weekIndex, highlighted = false, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }) => {
|
|
304
|
+
const { blockSize, blockWidth, blockMargin, blockRadius, labelHeight, labels, levels, isNormalized, colors } = useCalendarHeatmap();
|
|
305
|
+
const level = Math.max(0, Math.min(colorStepCount(levels, isNormalized), activity.level));
|
|
306
|
+
const ariaLabel = (labels.cellLabel ?? "{{date}}: {{value}} contributions").replace("{{date}}", activity.date).replace("{{value}}", String(activity.value));
|
|
307
|
+
return /* @__PURE__ */ jsx("rect", {
|
|
308
|
+
ref,
|
|
309
|
+
"data-slot": "calendar-heatmap-block",
|
|
310
|
+
role: onCellClick ? "button" : "img",
|
|
311
|
+
tabIndex: onCellClick ? 0 : -1,
|
|
312
|
+
"aria-label": ariaLabel,
|
|
313
|
+
className: cn("motion-safe:transition-opacity motion-safe:hover:opacity-70", onCellClick && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", className),
|
|
314
|
+
"data-value": activity.value,
|
|
315
|
+
"data-date": activity.date,
|
|
316
|
+
"data-level": level,
|
|
317
|
+
"data-highlighted": highlighted || void 0,
|
|
318
|
+
height: blockSize,
|
|
319
|
+
rx: blockRadius,
|
|
320
|
+
ry: blockRadius,
|
|
321
|
+
width: blockWidth,
|
|
322
|
+
x: (blockWidth + blockMargin) * weekIndex,
|
|
323
|
+
y: labelHeight + (blockSize + blockMargin) * dayIndex,
|
|
324
|
+
style: {
|
|
325
|
+
fill: getLevelFill(level, levels, isNormalized, highlighted, colors),
|
|
326
|
+
...styleProp
|
|
327
|
+
},
|
|
328
|
+
onClick: (event) => {
|
|
329
|
+
onCellClick?.(activity);
|
|
330
|
+
onClick?.(event);
|
|
331
|
+
},
|
|
332
|
+
onKeyDown: (event) => {
|
|
333
|
+
if (onCellClick && (event.key === "Enter" || event.key === " ")) {
|
|
334
|
+
event.preventDefault();
|
|
335
|
+
onCellClick(activity);
|
|
336
|
+
}
|
|
337
|
+
onKeyDown?.(event);
|
|
338
|
+
},
|
|
339
|
+
onMouseEnter: (event) => {
|
|
340
|
+
onCellHover?.(activity);
|
|
341
|
+
onMouseEnter?.(event);
|
|
342
|
+
},
|
|
343
|
+
onMouseLeave: (event) => {
|
|
344
|
+
onCellHover?.(null);
|
|
345
|
+
onMouseLeave?.(event);
|
|
346
|
+
},
|
|
347
|
+
...props
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
CalendarHeatmapBlock.displayName = "CalendarHeatmapBlock";
|
|
351
|
+
const CalendarHeatmapBody = ({ hideMonthLabels = false, hideWeekdayLabels = false, hideYearLabels = false, className, labelClassName, yearClassName, children, renderYearFooter, ...props }) => {
|
|
352
|
+
const { yearRows, blockSize, blockWidth, blockMargin, labels, labelHeight, fontSize, weekStart, data } = useCalendarHeatmap();
|
|
353
|
+
const weekdayLabelWidth = hideWeekdayLabels ? 0 : 40;
|
|
354
|
+
const strokePadding = 3;
|
|
355
|
+
const rowData = useMemo(() => {
|
|
356
|
+
return yearRows.map((yearRow) => {
|
|
357
|
+
return {
|
|
358
|
+
yearRow,
|
|
359
|
+
width: yearRow.weeks.length * (blockWidth + blockMargin) - blockMargin,
|
|
360
|
+
height: labelHeight + (blockSize + blockMargin) * 7 - blockMargin,
|
|
361
|
+
monthLabels: getMonthLabels(yearRow.weeks, labels.months),
|
|
362
|
+
yearTotalCount: data.filter((activity) => getYear(parseISO(activity.date)) === yearRow.year).reduce((sum, activity) => sum + activity.value, 0)
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
}, [
|
|
366
|
+
yearRows,
|
|
367
|
+
blockSize,
|
|
368
|
+
blockWidth,
|
|
369
|
+
blockMargin,
|
|
370
|
+
labelHeight,
|
|
371
|
+
labels.months,
|
|
372
|
+
data
|
|
373
|
+
]);
|
|
374
|
+
const totalWidth = weekdayLabelWidth + Math.max(...rowData.map((r) => r.width)) + 6;
|
|
375
|
+
return /* @__PURE__ */ jsx("div", {
|
|
376
|
+
"data-slot": "calendar-heatmap-body",
|
|
377
|
+
className: cn("flex max-w-full flex-col gap-6 overflow-x-auto overflow-y-hidden py-4", className),
|
|
378
|
+
...props,
|
|
379
|
+
children: rowData.map(({ yearRow, height, monthLabels, yearTotalCount }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
380
|
+
!hideYearLabels && /* @__PURE__ */ jsx("div", {
|
|
381
|
+
className: cn("mb-2 text-muted-foreground", yearClassName),
|
|
382
|
+
children: yearRow.year
|
|
383
|
+
}),
|
|
384
|
+
/* @__PURE__ */ jsx("svg", {
|
|
385
|
+
role: "img",
|
|
386
|
+
"aria-label": (labels.heatmapLabel ?? "Contribution heatmap for {{year}}").replace("{{year}}", String(yearRow.year)),
|
|
387
|
+
className: "block overflow-visible rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
388
|
+
height: height + 6,
|
|
389
|
+
viewBox: `0 0 ${totalWidth} ${height + 6}`,
|
|
390
|
+
width: totalWidth,
|
|
391
|
+
children: /* @__PURE__ */ jsxs("g", {
|
|
392
|
+
transform: `translate(0, ${strokePadding})`,
|
|
393
|
+
children: [
|
|
394
|
+
!hideMonthLabels && /* @__PURE__ */ jsx("g", {
|
|
395
|
+
className: cn("fill-current font-mono", labelClassName),
|
|
396
|
+
children: monthLabels.map(({ label, weekIndex }) => /* @__PURE__ */ jsx("text", {
|
|
397
|
+
dominantBaseline: "hanging",
|
|
398
|
+
x: weekdayLabelWidth + strokePadding + (blockWidth + blockMargin) * weekIndex,
|
|
399
|
+
style: { fontSize: `${fontSize * .75}px` },
|
|
400
|
+
children: label
|
|
401
|
+
}, `${yearRow.year}-${weekIndex}`))
|
|
402
|
+
}),
|
|
403
|
+
!hideWeekdayLabels && /* @__PURE__ */ jsx("g", {
|
|
404
|
+
className: cn("fill-current font-mono text-xs", labelClassName),
|
|
405
|
+
children: labels.weekdays?.map((label, dayIndex) => {
|
|
406
|
+
const adjustedIndex = (dayIndex + weekStart) % 7;
|
|
407
|
+
const adjustedLabel = labels.weekdays?.[adjustedIndex] || label;
|
|
408
|
+
return /* @__PURE__ */ jsx("text", {
|
|
409
|
+
x: 0,
|
|
410
|
+
y: labelHeight + (blockSize + blockMargin) * dayIndex + blockSize / 2,
|
|
411
|
+
dominantBaseline: "middle",
|
|
412
|
+
textAnchor: "start",
|
|
413
|
+
style: { fontSize: `${fontSize * .75}px` },
|
|
414
|
+
children: adjustedLabel
|
|
415
|
+
}, `weekday-${yearRow.year}-${label}`);
|
|
416
|
+
})
|
|
417
|
+
}),
|
|
418
|
+
/* @__PURE__ */ jsx("g", {
|
|
419
|
+
transform: `translate(${weekdayLabelWidth + strokePadding}, 0)`,
|
|
420
|
+
children: yearRow.weeks.map((week, weekIndex) => week.map((activity, dayIndex) => {
|
|
421
|
+
if (!activity) return null;
|
|
422
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children({
|
|
423
|
+
activity,
|
|
424
|
+
dayIndex,
|
|
425
|
+
weekIndex
|
|
426
|
+
}) }, `${yearRow.year}-${activity.date}`);
|
|
427
|
+
}))
|
|
428
|
+
})
|
|
429
|
+
]
|
|
430
|
+
})
|
|
431
|
+
}),
|
|
432
|
+
renderYearFooter?.({
|
|
433
|
+
year: yearRow.year,
|
|
434
|
+
totalCount: yearTotalCount
|
|
435
|
+
})
|
|
436
|
+
] }, `year-row-${yearRow.year}`))
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
const CalendarHeatmapFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
440
|
+
"data-slot": "calendar-heatmap-footer",
|
|
441
|
+
className: cn("flex flex-wrap gap-1 whitespace-nowrap sm:gap-x-4", className),
|
|
442
|
+
...props
|
|
443
|
+
});
|
|
444
|
+
const CalendarHeatmapStat = ({ compute, label, className, children, ...props }) => {
|
|
445
|
+
const { data, totalCount, year, labels } = useCalendarHeatmap();
|
|
446
|
+
const value = compute ? compute(data) : totalCount;
|
|
447
|
+
if (children) return /* @__PURE__ */ jsx(Fragment$1, { children: children({
|
|
448
|
+
value,
|
|
449
|
+
data,
|
|
450
|
+
year
|
|
451
|
+
}) });
|
|
452
|
+
const template = label ?? labels.stat ?? "{{value}} contributions in {{year}}";
|
|
453
|
+
return /* @__PURE__ */ jsx("div", {
|
|
454
|
+
"data-slot": "calendar-heatmap-stat",
|
|
455
|
+
className: cn("text-muted-foreground tabular-nums", className),
|
|
456
|
+
...props,
|
|
457
|
+
children: template.replace("{{value}}", String(value)).replace("{{year}}", String(year))
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
const CalendarHeatmapLegend = ({ labels: labelsProp, className, children, ...props }) => {
|
|
461
|
+
const { levels, isNormalized, blockSize, blockWidth, blockRadius, colors, labels } = useCalendarHeatmap();
|
|
462
|
+
const lessLabel = labelsProp?.less ?? "Less";
|
|
463
|
+
const moreLabel = labelsProp?.more ?? "More";
|
|
464
|
+
const legendLevels = Array.from({ length: levels }, (_, i) => isNormalized ? i + 1 : i);
|
|
465
|
+
return /* @__PURE__ */ jsxs("fieldset", {
|
|
466
|
+
"data-slot": "calendar-heatmap-legend",
|
|
467
|
+
"aria-label": labels.legendLabel ?? "Activity intensity legend",
|
|
468
|
+
className: cn("ml-auto flex items-center gap-1 text-muted-foreground", className),
|
|
469
|
+
...props,
|
|
470
|
+
children: [
|
|
471
|
+
/* @__PURE__ */ jsx("span", {
|
|
472
|
+
className: "mr-1 font-medium text-xs",
|
|
473
|
+
children: lessLabel
|
|
474
|
+
}),
|
|
475
|
+
legendLevels.map((level) => children ? /* @__PURE__ */ jsx(Fragment, { children: children({ level }) }, `legend-level-${level}`) : /* @__PURE__ */ jsx("svg", {
|
|
476
|
+
role: "img",
|
|
477
|
+
"aria-label": (labels.legendLevelLabel ?? "{{level}} contributions").replace("{{level}}", String(level)),
|
|
478
|
+
height: blockSize,
|
|
479
|
+
width: blockWidth,
|
|
480
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
481
|
+
"data-level": level,
|
|
482
|
+
height: blockSize,
|
|
483
|
+
rx: blockRadius,
|
|
484
|
+
ry: blockRadius,
|
|
485
|
+
width: blockWidth,
|
|
486
|
+
style: { fill: getLevelFill(level, levels, isNormalized, false, colors) }
|
|
487
|
+
})
|
|
488
|
+
}, `legend-level-${level}`)),
|
|
489
|
+
/* @__PURE__ */ jsx("span", {
|
|
490
|
+
className: "ml-1 font-medium text-xs",
|
|
491
|
+
children: moreLabel
|
|
492
|
+
})
|
|
493
|
+
]
|
|
494
|
+
});
|
|
495
|
+
};
|
|
496
|
+
//#endregion
|
|
497
|
+
export { CalendarHeatmap, CalendarHeatmapBlock, CalendarHeatmapBody, CalendarHeatmapFooter, CalendarHeatmapLegend, CalendarHeatmapStat };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { Locale } from "date-fns";
|
|
3
|
+
//#region src/components/registry/heatmap/status-heatmap.d.ts
|
|
4
|
+
export type StatusValue = number;
|
|
5
|
+
export type StatusActivity = {
|
|
6
|
+
date: string;
|
|
7
|
+
value: StatusValue;
|
|
8
|
+
};
|
|
9
|
+
export type StatusHeatmapLabels = {
|
|
10
|
+
statuses?: Record<number, string> & {
|
|
11
|
+
noData?: string;
|
|
12
|
+
critical?: string;
|
|
13
|
+
degraded?: string;
|
|
14
|
+
healthy?: string;
|
|
15
|
+
};
|
|
16
|
+
stat?: string;
|
|
17
|
+
cellLabel?: string;
|
|
18
|
+
heatmapLabel?: string;
|
|
19
|
+
legendLabel?: string;
|
|
20
|
+
};
|
|
21
|
+
export type StatusColorConfig = Record<number, string> & {
|
|
22
|
+
noData?: string;
|
|
23
|
+
critical?: string;
|
|
24
|
+
degraded?: string;
|
|
25
|
+
healthy?: string;
|
|
26
|
+
};
|
|
27
|
+
export type StatusHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
28
|
+
data: StatusActivity[];
|
|
29
|
+
dateFormat?: string;
|
|
30
|
+
blockSize?: number;
|
|
31
|
+
blockMargin?: number;
|
|
32
|
+
blockRadius?: number;
|
|
33
|
+
blockAspectRatio?: number;
|
|
34
|
+
colors?: StatusColorConfig;
|
|
35
|
+
locale?: Locale;
|
|
36
|
+
labels?: StatusHeatmapLabels;
|
|
37
|
+
fontSize?: number;
|
|
38
|
+
emptyState?: ReactNode;
|
|
39
|
+
statusValues?: StatusValue[];
|
|
40
|
+
healthyValue?: StatusValue;
|
|
41
|
+
style?: CSSProperties;
|
|
42
|
+
className?: string;
|
|
43
|
+
children: ReactNode;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Status Heatmap
|
|
47
|
+
*
|
|
48
|
+
* A timeline indicator showing daily status over a period (e.g., 90 days).
|
|
49
|
+
* Similar to Atlassian Statuspage - each day is represented by a vertical bar.
|
|
50
|
+
* Supports 4 status values: 0=no-data, 1=critical, 2=degraded, 3=healthy
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* <StatusHeatmap data={data} blockAspectRatio={0.2}>
|
|
55
|
+
* <StatusHeatmapBody>
|
|
56
|
+
* {({ activity, dayIndex }) => (
|
|
57
|
+
* <StatusHeatmapBlock
|
|
58
|
+
* activity={activity}
|
|
59
|
+
* dayIndex={dayIndex}
|
|
60
|
+
* />
|
|
61
|
+
* )}
|
|
62
|
+
* </StatusHeatmapBody>
|
|
63
|
+
* <StatusHeatmapFooter>
|
|
64
|
+
* <StatusHeatmapStat />
|
|
65
|
+
* <StatusHeatmapLegend />
|
|
66
|
+
* </StatusHeatmapFooter>
|
|
67
|
+
* </StatusHeatmap>
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @param data - Array of activities with date (YYYY-MM-DD) and status value (0-3)
|
|
71
|
+
* @param dateFormat - Date format string for tooltips. Default: "MMM d"
|
|
72
|
+
* @param blockAspectRatio - Width/height ratio of blocks. Default: 0.2 (narrow vertical bars)
|
|
73
|
+
* @param colors - Custom colors for critical, degraded, and healthy states
|
|
74
|
+
*/
|
|
75
|
+
export declare const StatusHeatmap: ({ data, dateFormat, blockSize, blockMargin, blockRadius, blockAspectRatio, colors, locale, labels: labelsProp, fontSize, emptyState, statusValues, healthyValue, style, className, children, ...props }: StatusHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
|
|
76
|
+
export type StatusHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
77
|
+
activity: StatusActivity;
|
|
78
|
+
dayIndex: number;
|
|
79
|
+
highlighted?: boolean;
|
|
80
|
+
onCellClick?: (activity: StatusActivity) => void;
|
|
81
|
+
onCellHover?: (activity: StatusActivity | null) => void;
|
|
82
|
+
className?: string;
|
|
83
|
+
};
|
|
84
|
+
export declare function StatusHeatmapBlock({ ref, activity, dayIndex, highlighted, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: StatusHeatmapBlockProps & {
|
|
85
|
+
ref?: React.RefObject<SVGRectElement | null>;
|
|
86
|
+
}): import("react").JSX.Element;
|
|
87
|
+
export declare namespace StatusHeatmapBlock {
|
|
88
|
+
var displayName: string;
|
|
89
|
+
}
|
|
90
|
+
export type StatusHeatmapBodyProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
91
|
+
hideDateLabels?: boolean;
|
|
92
|
+
labelInterval?: number;
|
|
93
|
+
className?: string;
|
|
94
|
+
labelClassName?: string;
|
|
95
|
+
children: (props: {
|
|
96
|
+
activity: StatusActivity;
|
|
97
|
+
dayIndex: number;
|
|
98
|
+
}) => ReactNode;
|
|
99
|
+
};
|
|
100
|
+
export declare const StatusHeatmapBody: ({ hideDateLabels, labelInterval, className, labelClassName, children, ...props }: StatusHeatmapBodyProps) => import("react").JSX.Element;
|
|
101
|
+
export type StatusHeatmapFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
102
|
+
export declare const StatusHeatmapFooter: ({ className, ...props }: StatusHeatmapFooterProps) => import("react").JSX.Element;
|
|
103
|
+
export type StatusHeatmapStatProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
104
|
+
compute?: (data: StatusActivity[]) => number | string;
|
|
105
|
+
label?: string;
|
|
106
|
+
children?: (result: {
|
|
107
|
+
value: number | string;
|
|
108
|
+
data: StatusActivity[];
|
|
109
|
+
}) => ReactNode;
|
|
110
|
+
};
|
|
111
|
+
export declare const StatusHeatmapStat: ({ compute, label, className, children, ...props }: StatusHeatmapStatProps) => import("react").JSX.Element;
|
|
112
|
+
export type StatusHeatmapLegendProps = Omit<HTMLAttributes<HTMLFieldSetElement>, "children"> & {
|
|
113
|
+
labels?: StatusHeatmapLabels["statuses"];
|
|
114
|
+
children?: (props: {
|
|
115
|
+
value: StatusValue;
|
|
116
|
+
label: string;
|
|
117
|
+
}) => ReactNode;
|
|
118
|
+
};
|
|
119
|
+
export declare const StatusHeatmapLegend: ({ labels: labelsProp, className, children, ...props }: StatusHeatmapLegendProps) => import("react").JSX.Element;
|
|
120
|
+
//#endregion
|