@siyahi/ui 0.0.0-stage → 0.1.0-next.4
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,330 @@
|
|
|
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 { format } from "date-fns";
|
|
5
|
+
//#region src/components/registry/heatmap/status-heatmap.tsx
|
|
6
|
+
const DEFAULT_COLORS = {
|
|
7
|
+
0: "var(--color-secondary)",
|
|
8
|
+
1: "var(--negative)",
|
|
9
|
+
2: "var(--warning)",
|
|
10
|
+
3: "var(--positive)"
|
|
11
|
+
};
|
|
12
|
+
const SEMANTIC_KEYS = {
|
|
13
|
+
0: "noData",
|
|
14
|
+
1: "critical",
|
|
15
|
+
2: "degraded",
|
|
16
|
+
3: "healthy"
|
|
17
|
+
};
|
|
18
|
+
const resolveColor = (value, colors) => {
|
|
19
|
+
const semanticKey = SEMANTIC_KEYS[value];
|
|
20
|
+
if (colors) {
|
|
21
|
+
const numeric = colors[value];
|
|
22
|
+
if (typeof numeric === "string") return numeric;
|
|
23
|
+
if (semanticKey) {
|
|
24
|
+
const semantic = colors[semanticKey];
|
|
25
|
+
if (typeof semantic === "string") return semantic;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return DEFAULT_COLORS[value] ?? DEFAULT_COLORS[0];
|
|
29
|
+
};
|
|
30
|
+
const resolveStatusLabel = (value, labels) => {
|
|
31
|
+
if (labels) {
|
|
32
|
+
const numeric = labels[value];
|
|
33
|
+
if (typeof numeric === "string") return numeric;
|
|
34
|
+
const semanticKey = SEMANTIC_KEYS[value];
|
|
35
|
+
if (semanticKey) {
|
|
36
|
+
const semantic = labels[semanticKey];
|
|
37
|
+
if (typeof semantic === "string") return semantic;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
switch (value) {
|
|
41
|
+
case 0: return "No Data";
|
|
42
|
+
case 1: return "Critical";
|
|
43
|
+
case 2: return "Degraded";
|
|
44
|
+
case 3: return "Healthy";
|
|
45
|
+
default: return `Status ${value}`;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const getStatusFill = (value, colors, highlighted = false) => {
|
|
49
|
+
const base = resolveColor(value, colors);
|
|
50
|
+
if (!highlighted) return base;
|
|
51
|
+
return `color-mix(in oklch, ${base} 60%, transparent)`;
|
|
52
|
+
};
|
|
53
|
+
const EMPTY_STYLE = {};
|
|
54
|
+
const StatusHeatmapContext = createContext(null);
|
|
55
|
+
const useStatusHeatmap = () => {
|
|
56
|
+
const context = use(StatusHeatmapContext);
|
|
57
|
+
if (!context) throw new Error("StatusHeatmap components must be used within a StatusHeatmap");
|
|
58
|
+
return context;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Status Heatmap
|
|
62
|
+
*
|
|
63
|
+
* A timeline indicator showing daily status over a period (e.g., 90 days).
|
|
64
|
+
* Similar to Atlassian Statuspage - each day is represented by a vertical bar.
|
|
65
|
+
* Supports 4 status values: 0=no-data, 1=critical, 2=degraded, 3=healthy
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* <StatusHeatmap data={data} blockAspectRatio={0.2}>
|
|
70
|
+
* <StatusHeatmapBody>
|
|
71
|
+
* {({ activity, dayIndex }) => (
|
|
72
|
+
* <StatusHeatmapBlock
|
|
73
|
+
* activity={activity}
|
|
74
|
+
* dayIndex={dayIndex}
|
|
75
|
+
* />
|
|
76
|
+
* )}
|
|
77
|
+
* </StatusHeatmapBody>
|
|
78
|
+
* <StatusHeatmapFooter>
|
|
79
|
+
* <StatusHeatmapStat />
|
|
80
|
+
* <StatusHeatmapLegend />
|
|
81
|
+
* </StatusHeatmapFooter>
|
|
82
|
+
* </StatusHeatmap>
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @param data - Array of activities with date (YYYY-MM-DD) and status value (0-3)
|
|
86
|
+
* @param dateFormat - Date format string for tooltips. Default: "MMM d"
|
|
87
|
+
* @param blockAspectRatio - Width/height ratio of blocks. Default: 0.2 (narrow vertical bars)
|
|
88
|
+
* @param colors - Custom colors for critical, degraded, and healthy states
|
|
89
|
+
*/
|
|
90
|
+
const StatusHeatmap = ({ data, dateFormat = "MMM d", blockSize = 40, blockMargin = 2, blockRadius = 2, blockAspectRatio = .2, colors, locale, labels: labelsProp, fontSize = 14, emptyState, statusValues = [
|
|
91
|
+
0,
|
|
92
|
+
1,
|
|
93
|
+
2,
|
|
94
|
+
3
|
|
95
|
+
], healthyValue = 3, style = EMPTY_STYLE, className, children, ...props }) => {
|
|
96
|
+
const labels = useMemo(() => ({
|
|
97
|
+
statuses: {
|
|
98
|
+
noData: "No Data",
|
|
99
|
+
critical: "Critical",
|
|
100
|
+
degraded: "Degraded",
|
|
101
|
+
healthy: "Healthy"
|
|
102
|
+
},
|
|
103
|
+
cellLabel: "{{date}}: {{status}}",
|
|
104
|
+
heatmapLabel: "Status heatmap",
|
|
105
|
+
legendLabel: "Status legend",
|
|
106
|
+
...labelsProp
|
|
107
|
+
}), [labelsProp]);
|
|
108
|
+
const dates = useMemo(() => {
|
|
109
|
+
const uniqueDates = /* @__PURE__ */ new Set();
|
|
110
|
+
data.forEach((activity) => {
|
|
111
|
+
uniqueDates.add(activity.date);
|
|
112
|
+
});
|
|
113
|
+
return Array.from(uniqueDates).sort();
|
|
114
|
+
}, [data]);
|
|
115
|
+
const blockWidth = blockSize * blockAspectRatio;
|
|
116
|
+
const width = dates.length * (blockWidth + blockMargin) - blockMargin;
|
|
117
|
+
const height = blockSize;
|
|
118
|
+
const contextValue = useMemo(() => ({
|
|
119
|
+
data,
|
|
120
|
+
dates,
|
|
121
|
+
blockMargin,
|
|
122
|
+
blockRadius,
|
|
123
|
+
blockSize,
|
|
124
|
+
blockAspectRatio,
|
|
125
|
+
blockWidth,
|
|
126
|
+
fontSize,
|
|
127
|
+
labels,
|
|
128
|
+
statusValues,
|
|
129
|
+
healthyValue,
|
|
130
|
+
width,
|
|
131
|
+
height,
|
|
132
|
+
dateFormat,
|
|
133
|
+
locale,
|
|
134
|
+
colors
|
|
135
|
+
}), [
|
|
136
|
+
data,
|
|
137
|
+
dates,
|
|
138
|
+
blockMargin,
|
|
139
|
+
blockRadius,
|
|
140
|
+
blockSize,
|
|
141
|
+
blockAspectRatio,
|
|
142
|
+
blockWidth,
|
|
143
|
+
fontSize,
|
|
144
|
+
labels,
|
|
145
|
+
statusValues,
|
|
146
|
+
healthyValue,
|
|
147
|
+
width,
|
|
148
|
+
height,
|
|
149
|
+
dateFormat,
|
|
150
|
+
locale,
|
|
151
|
+
colors
|
|
152
|
+
]);
|
|
153
|
+
if (data.length === 0 || dates.length === 0) return emptyState ? emptyState : null;
|
|
154
|
+
return /* @__PURE__ */ jsx(StatusHeatmapContext, {
|
|
155
|
+
value: contextValue,
|
|
156
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
157
|
+
"data-slot": "status-heatmap",
|
|
158
|
+
className: cn("flex w-max max-w-full flex-col gap-2 p-4", className),
|
|
159
|
+
style: {
|
|
160
|
+
fontSize,
|
|
161
|
+
...style
|
|
162
|
+
},
|
|
163
|
+
...props,
|
|
164
|
+
children
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const StatusHeatmapBlock = ({ ref, activity, dayIndex, highlighted = false, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }) => {
|
|
169
|
+
const { blockSize, blockWidth, blockMargin, blockRadius, labels, colors } = useStatusHeatmap();
|
|
170
|
+
const statusText = resolveStatusLabel(activity.value, labels.statuses);
|
|
171
|
+
const ariaLabel = (labels.cellLabel ?? "{{date}}: {{status}}").replace("{{date}}", activity.date).replace("{{status}}", statusText);
|
|
172
|
+
const xPosition = (blockWidth + blockMargin) * dayIndex;
|
|
173
|
+
return /* @__PURE__ */ jsx("rect", {
|
|
174
|
+
ref,
|
|
175
|
+
"data-slot": "status-heatmap-block",
|
|
176
|
+
role: onCellClick ? "button" : "img",
|
|
177
|
+
tabIndex: onCellClick ? 0 : -1,
|
|
178
|
+
"aria-label": ariaLabel,
|
|
179
|
+
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),
|
|
180
|
+
"data-value": activity.value,
|
|
181
|
+
"data-date": activity.date,
|
|
182
|
+
"data-highlighted": highlighted || void 0,
|
|
183
|
+
height: blockSize,
|
|
184
|
+
rx: blockRadius,
|
|
185
|
+
ry: blockRadius,
|
|
186
|
+
width: blockWidth,
|
|
187
|
+
x: xPosition,
|
|
188
|
+
y: 0,
|
|
189
|
+
style: {
|
|
190
|
+
fill: getStatusFill(activity.value, colors, highlighted),
|
|
191
|
+
...styleProp
|
|
192
|
+
},
|
|
193
|
+
onClick: (event) => {
|
|
194
|
+
onCellClick?.(activity);
|
|
195
|
+
onClick?.(event);
|
|
196
|
+
},
|
|
197
|
+
onKeyDown: (event) => {
|
|
198
|
+
if (onCellClick && (event.key === "Enter" || event.key === " ")) {
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
onCellClick(activity);
|
|
201
|
+
}
|
|
202
|
+
onKeyDown?.(event);
|
|
203
|
+
},
|
|
204
|
+
onMouseEnter: (event) => {
|
|
205
|
+
onCellHover?.(activity);
|
|
206
|
+
onMouseEnter?.(event);
|
|
207
|
+
},
|
|
208
|
+
onMouseLeave: (event) => {
|
|
209
|
+
onCellHover?.(null);
|
|
210
|
+
onMouseLeave?.(event);
|
|
211
|
+
},
|
|
212
|
+
...props
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
StatusHeatmapBlock.displayName = "StatusHeatmapBlock";
|
|
216
|
+
const StatusHeatmapBody = ({ hideDateLabels = false, labelInterval = 30, className, labelClassName, children, ...props }) => {
|
|
217
|
+
const { data, dates, width, height, blockWidth, blockMargin, fontSize, dateFormat, locale, labels } = useStatusHeatmap();
|
|
218
|
+
const activityMap = useMemo(() => {
|
|
219
|
+
const map = /* @__PURE__ */ new Map();
|
|
220
|
+
data.forEach((activity) => {
|
|
221
|
+
map.set(activity.date, activity);
|
|
222
|
+
});
|
|
223
|
+
return map;
|
|
224
|
+
}, [data]);
|
|
225
|
+
const allActivities = useMemo(() => {
|
|
226
|
+
return dates.map((date) => {
|
|
227
|
+
return activityMap.get(date) || {
|
|
228
|
+
date,
|
|
229
|
+
value: 0
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}, [dates, activityMap]);
|
|
233
|
+
const labelFontSize = fontSize * .75;
|
|
234
|
+
const labelHeight = hideDateLabels ? 0 : labelFontSize * 2;
|
|
235
|
+
return /* @__PURE__ */ jsx("div", {
|
|
236
|
+
"data-slot": "status-heatmap-body",
|
|
237
|
+
className: cn("max-w-full overflow-x-auto overflow-y-hidden", className),
|
|
238
|
+
...props,
|
|
239
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
240
|
+
role: "img",
|
|
241
|
+
"aria-label": labels.heatmapLabel ?? "Status heatmap",
|
|
242
|
+
className: "block overflow-visible rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
243
|
+
height: height + labelHeight + 40,
|
|
244
|
+
viewBox: `-20 -20 ${width + 40} ${height + labelHeight + 40}`,
|
|
245
|
+
width: width + 40,
|
|
246
|
+
children: [!hideDateLabels && /* @__PURE__ */ jsx("g", {
|
|
247
|
+
className: cn("fill-current font-mono", labelClassName),
|
|
248
|
+
children: dates.map((date, dayIndex) => {
|
|
249
|
+
const isFirst = dayIndex === 0;
|
|
250
|
+
const isLast = dayIndex === dates.length - 1;
|
|
251
|
+
if (dayIndex % labelInterval !== 0 && !isLast) return null;
|
|
252
|
+
const blockLeft = (blockWidth + blockMargin) * dayIndex;
|
|
253
|
+
const xPosition = isFirst ? blockLeft : isLast ? blockLeft + blockWidth : blockLeft + blockWidth / 2;
|
|
254
|
+
const anchor = isFirst ? "start" : isLast ? "end" : "middle";
|
|
255
|
+
return /* @__PURE__ */ jsx("text", {
|
|
256
|
+
x: xPosition,
|
|
257
|
+
y: height + labelFontSize,
|
|
258
|
+
dominantBaseline: "hanging",
|
|
259
|
+
textAnchor: anchor,
|
|
260
|
+
style: { fontSize: `${labelFontSize}px` },
|
|
261
|
+
children: format(new Date(date), dateFormat, { locale })
|
|
262
|
+
}, `date-${date}`);
|
|
263
|
+
})
|
|
264
|
+
}), allActivities.map((activity, dayIndex) => {
|
|
265
|
+
const key = activity.date;
|
|
266
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children({
|
|
267
|
+
activity,
|
|
268
|
+
dayIndex
|
|
269
|
+
}) }, key);
|
|
270
|
+
})]
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const StatusHeatmapFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
275
|
+
"data-slot": "status-heatmap-footer",
|
|
276
|
+
className: cn("flex flex-wrap gap-1 whitespace-nowrap sm:gap-x-4", className),
|
|
277
|
+
...props
|
|
278
|
+
});
|
|
279
|
+
const StatusHeatmapStat = ({ compute, label, className, children, ...props }) => {
|
|
280
|
+
const { data, healthyValue, labels } = useStatusHeatmap();
|
|
281
|
+
const value = compute ? compute(data) : data.filter((a) => a.value === healthyValue).length;
|
|
282
|
+
if (children) return /* @__PURE__ */ jsx(Fragment$1, { children: children({
|
|
283
|
+
value,
|
|
284
|
+
data
|
|
285
|
+
}) });
|
|
286
|
+
const template = label ?? labels.stat ?? "{{value}} days healthy";
|
|
287
|
+
return /* @__PURE__ */ jsx("div", {
|
|
288
|
+
"data-slot": "status-heatmap-stat",
|
|
289
|
+
className: cn("text-muted-foreground tabular-nums", className),
|
|
290
|
+
...props,
|
|
291
|
+
children: template.replace("{{value}}", String(value))
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
const StatusHeatmapLegend = ({ labels: labelsProp, className, children, ...props }) => {
|
|
295
|
+
const { labels, statusValues, blockSize, blockWidth, blockRadius, colors } = useStatusHeatmap();
|
|
296
|
+
const statuses = statusValues.map((value) => ({
|
|
297
|
+
value,
|
|
298
|
+
label: resolveStatusLabel(value, labelsProp ?? labels.statuses)
|
|
299
|
+
}));
|
|
300
|
+
return /* @__PURE__ */ jsx("fieldset", {
|
|
301
|
+
"data-slot": "status-heatmap-legend",
|
|
302
|
+
"aria-label": labels.legendLabel ?? "Status legend",
|
|
303
|
+
className: cn("ml-auto flex items-center gap-1", className),
|
|
304
|
+
...props,
|
|
305
|
+
children: statuses.map((status) => children ? /* @__PURE__ */ jsx(Fragment, { children: children({
|
|
306
|
+
value: status.value,
|
|
307
|
+
label: status.label
|
|
308
|
+
}) }, `status-${status.value}`) : /* @__PURE__ */ jsxs("div", {
|
|
309
|
+
className: "flex items-center gap-1",
|
|
310
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
311
|
+
"aria-hidden": "true",
|
|
312
|
+
height: blockSize / 2,
|
|
313
|
+
width: blockWidth,
|
|
314
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
315
|
+
"data-value": status.value,
|
|
316
|
+
height: blockSize / 2,
|
|
317
|
+
rx: blockRadius,
|
|
318
|
+
ry: blockRadius,
|
|
319
|
+
width: blockWidth,
|
|
320
|
+
style: { fill: getStatusFill(status.value, colors) }
|
|
321
|
+
})
|
|
322
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
323
|
+
className: "font-medium text-muted-foreground text-xs",
|
|
324
|
+
children: status.label
|
|
325
|
+
})]
|
|
326
|
+
}, `status-${status.value}`))
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
//#endregion
|
|
330
|
+
export { StatusHeatmap, StatusHeatmapBlock, StatusHeatmapBody, StatusHeatmapFooter, StatusHeatmapLegend, StatusHeatmapStat };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { CSSProperties, HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { Day, Locale } from "date-fns";
|
|
3
|
+
//#region src/components/registry/heatmap/weekday-heatmap.d.ts
|
|
4
|
+
export type WeekdayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
5
|
+
export type WeekdayHour = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23;
|
|
6
|
+
export type WeekdayHourlyActivity = {
|
|
7
|
+
weekday: number;
|
|
8
|
+
hour: number;
|
|
9
|
+
value: number;
|
|
10
|
+
};
|
|
11
|
+
type WeekdayHourlyActivityWithLevel = WeekdayHourlyActivity & {
|
|
12
|
+
level: number;
|
|
13
|
+
};
|
|
14
|
+
export type WeekdayExtraRowSpec = {
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
compute: (data: WeekdayHourlyActivity[]) => number[];
|
|
17
|
+
};
|
|
18
|
+
export type WeekdayExtraColumnSpec = {
|
|
19
|
+
label: ReactNode;
|
|
20
|
+
compute: (data: WeekdayHourlyActivity[]) => number[];
|
|
21
|
+
};
|
|
22
|
+
export type WeekdayHeatmapLabels = {
|
|
23
|
+
hours?: string[];
|
|
24
|
+
endHour?: string | null;
|
|
25
|
+
weekdays?: string[];
|
|
26
|
+
stat?: string;
|
|
27
|
+
cellLabel?: string;
|
|
28
|
+
heatmapLabel?: string;
|
|
29
|
+
legendLabel?: string;
|
|
30
|
+
legendLevelLabel?: string;
|
|
31
|
+
};
|
|
32
|
+
export type ColorConfig = {
|
|
33
|
+
empty?: string;
|
|
34
|
+
scale?: string;
|
|
35
|
+
};
|
|
36
|
+
export type WeekdayHeatmapProps = HTMLAttributes<HTMLDivElement> & {
|
|
37
|
+
data: WeekdayHourlyActivity[];
|
|
38
|
+
weekStart?: Day;
|
|
39
|
+
use12Hour?: boolean;
|
|
40
|
+
blockSize?: number;
|
|
41
|
+
blockMargin?: number;
|
|
42
|
+
blockRadius?: number;
|
|
43
|
+
blockAspectRatio?: number;
|
|
44
|
+
levels?: number;
|
|
45
|
+
isNormalized?: boolean;
|
|
46
|
+
colors?: ColorConfig;
|
|
47
|
+
locale?: Locale;
|
|
48
|
+
labels?: WeekdayHeatmapLabels;
|
|
49
|
+
fontSize?: number;
|
|
50
|
+
emptyState?: ReactNode;
|
|
51
|
+
totalCount?: number;
|
|
52
|
+
extraRow?: WeekdayExtraRowSpec;
|
|
53
|
+
extraColumn?: WeekdayExtraColumnSpec;
|
|
54
|
+
style?: CSSProperties;
|
|
55
|
+
className?: string;
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Weekday Heatmap
|
|
60
|
+
*
|
|
61
|
+
* A GitHub punch card style heatmap showing activity distribution by weekday (rows) and hour (columns).
|
|
62
|
+
* Renders a 7 × 24 grid with optional extra row (aggregated per hour) and extra column (aggregated per weekday).
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* <WeekdayHeatmap data={data} weekStart={1} use12Hour>
|
|
67
|
+
* <WeekdayHeatmapBody>
|
|
68
|
+
* {({ activity }) => (
|
|
69
|
+
* <WeekdayHeatmapBlock activity={activity} />
|
|
70
|
+
* )}
|
|
71
|
+
* </WeekdayHeatmapBody>
|
|
72
|
+
* <WeekdayHeatmapFooter>
|
|
73
|
+
* <WeekdayHeatmapStat />
|
|
74
|
+
* <WeekdayHeatmapLegend />
|
|
75
|
+
* </WeekdayHeatmapFooter>
|
|
76
|
+
* </WeekdayHeatmap>
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @param data - Array of activities with weekday (0-6 for Sun-Sat), hour (0-23), and numeric value
|
|
80
|
+
* @param weekStart - First day of week (0=Sunday, 1=Monday). Default: 0
|
|
81
|
+
* @param use12Hour - Use 12-hour format for hour labels. Default: false
|
|
82
|
+
* @param blockSize - Block height in pixels. Default: 24
|
|
83
|
+
* @param blockAspectRatio - Block width/height ratio. Default: 1 (square)
|
|
84
|
+
* @param levels - Total number of legend cells (including empty when not normalized). Default: 5
|
|
85
|
+
* @param isNormalized - When true, uses min-max normalization across the dataset (suitable for signed values like temperature). When false (default), treats 0 as empty and scales from 0 to max.
|
|
86
|
+
* @param extraRow - Appends an aggregated row below the grid. `compute` receives all activities and must return 24 values (one per hour). Rendered through `renderExtraRow` on the body, or falls back to `children`.
|
|
87
|
+
* @param extraColumn - Appends an aggregated column to the right of the grid. `compute` receives all activities and must return 7 values indexed by weekday 0–6. Rendered through `renderExtraColumn` on the body, or falls back to `children`.
|
|
88
|
+
*/
|
|
89
|
+
export declare const WeekdayHeatmap: ({ data, weekStart, use12Hour, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style, className, children, ...props }: WeekdayHeatmapProps) => 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;
|
|
90
|
+
export type WeekdayHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
91
|
+
activity: WeekdayHourlyActivityWithLevel;
|
|
92
|
+
extra?: "row" | "column";
|
|
93
|
+
highlighted?: boolean;
|
|
94
|
+
onCellClick?: (activity: WeekdayHourlyActivityWithLevel) => void;
|
|
95
|
+
onCellHover?: (activity: WeekdayHourlyActivityWithLevel | null) => void;
|
|
96
|
+
className?: string;
|
|
97
|
+
};
|
|
98
|
+
export declare function WeekdayHeatmapBlock({ ref, activity, extra, highlighted, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: WeekdayHeatmapBlockProps & {
|
|
99
|
+
ref?: React.RefObject<SVGRectElement | null>;
|
|
100
|
+
}): import("react").JSX.Element;
|
|
101
|
+
export declare namespace WeekdayHeatmapBlock {
|
|
102
|
+
var displayName: string;
|
|
103
|
+
}
|
|
104
|
+
export type WeekdayHeatmapBodyProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
105
|
+
hideHourLabels?: boolean;
|
|
106
|
+
hideWeekdayLabels?: boolean;
|
|
107
|
+
className?: string;
|
|
108
|
+
labelClassName?: string;
|
|
109
|
+
children: (props: {
|
|
110
|
+
activity: WeekdayHourlyActivityWithLevel;
|
|
111
|
+
weekdayIndex: number;
|
|
112
|
+
}) => ReactNode;
|
|
113
|
+
renderExtraRow?: (props: {
|
|
114
|
+
activity: WeekdayHourlyActivityWithLevel;
|
|
115
|
+
weekdayIndex: number;
|
|
116
|
+
}) => ReactNode;
|
|
117
|
+
renderExtraColumn?: (props: {
|
|
118
|
+
activity: WeekdayHourlyActivityWithLevel;
|
|
119
|
+
weekdayIndex: number;
|
|
120
|
+
}) => ReactNode;
|
|
121
|
+
};
|
|
122
|
+
export declare const WeekdayHeatmapBody: ({ hideHourLabels, hideWeekdayLabels, className, labelClassName, children, renderExtraRow, renderExtraColumn, ...props }: WeekdayHeatmapBodyProps) => import("react").JSX.Element;
|
|
123
|
+
export type WeekdayHeatmapFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
124
|
+
export declare const WeekdayHeatmapFooter: ({ className, ...props }: WeekdayHeatmapFooterProps) => import("react").JSX.Element;
|
|
125
|
+
export type WeekdayHeatmapStatProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
126
|
+
compute?: (data: WeekdayHourlyActivityWithLevel[]) => number | string;
|
|
127
|
+
label?: string;
|
|
128
|
+
children?: (result: {
|
|
129
|
+
value: number | string;
|
|
130
|
+
data: WeekdayHourlyActivityWithLevel[];
|
|
131
|
+
}) => ReactNode;
|
|
132
|
+
};
|
|
133
|
+
export declare const WeekdayHeatmapStat: ({ compute, label, className, children, ...props }: WeekdayHeatmapStatProps) => import("react").JSX.Element;
|
|
134
|
+
export type WeekdayHeatmapLegendProps = Omit<HTMLAttributes<HTMLFieldSetElement>, "children"> & {
|
|
135
|
+
labels?: {
|
|
136
|
+
less?: string;
|
|
137
|
+
more?: string;
|
|
138
|
+
};
|
|
139
|
+
children?: (props: {
|
|
140
|
+
level: number;
|
|
141
|
+
}) => ReactNode;
|
|
142
|
+
};
|
|
143
|
+
export declare const WeekdayHeatmapLegend: ({ labels: labelsProp, className, children, ...props }: WeekdayHeatmapLegendProps) => import("react").JSX.Element;
|
|
144
|
+
//#endregion
|
|
145
|
+
export {};
|