@vvedantb/ui 0.17.1 → 0.18.1
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/README.md +251 -3
- package/package.json +13 -5
- package/shell/src/SidebarResize.tsx +1 -4
- package/src/ai-elements/__snapshots__/queue.test.tsx.snap +11 -0
- package/src/ai-elements/activity-question-utils.ts +26 -0
- package/src/ai-elements/activity-question.tsx +159 -0
- package/src/ai-elements/activity-shared.tsx +459 -0
- package/src/ai-elements/activity-step-detail.tsx +115 -0
- package/src/ai-elements/activity-step-label.ts +981 -0
- package/src/ai-elements/activity-tasks-utils.ts +199 -0
- package/src/ai-elements/activity-tasks.tsx +770 -0
- package/src/ai-elements/artifact.tsx +162 -52
- package/src/ai-elements/attachments.tsx +62 -32
- package/src/ai-elements/code-block.tsx +76 -22
- package/src/ai-elements/context.tsx +370 -0
- package/src/ai-elements/conversation.tsx +186 -62
- package/src/ai-elements/file-chip.tsx +53 -0
- package/src/ai-elements/message.tsx +161 -50
- package/src/ai-elements/model-picker-content.tsx +505 -0
- package/src/ai-elements/model-picker-types.ts +56 -0
- package/src/ai-elements/model-picker.tsx +301 -0
- package/src/ai-elements/model-selector.tsx +102 -21
- package/src/ai-elements/plan.tsx +130 -0
- package/src/ai-elements/prompt-input-settings.tsx +144 -0
- package/src/ai-elements/prompt-input-speech.tsx +189 -0
- package/src/ai-elements/prompt-input-traits.tsx +281 -0
- package/src/ai-elements/prompt-input.tsx +320 -134
- package/src/ai-elements/provider-icon.tsx +133 -0
- package/src/ai-elements/queue.tsx +293 -9
- package/src/ai-elements/shimmer.tsx +72 -1
- package/src/ai-elements/simple-model-ladder.tsx +108 -0
- package/src/ai-elements/speech-input.tsx +5 -3
- package/src/ai-elements/test-results.tsx +510 -0
- package/src/ai-elements/trace.tsx +243 -0
- package/src/ai-elements/use-streaming-disclosure.ts +50 -0
- package/src/ai-elements/web-preview.tsx +349 -0
- package/src/composer/input-area/ComposerSlashMenu.tsx +1 -1
- package/src/index.ts +66 -12
- package/src/kibo/avatar-stack.tsx +40 -0
- package/src/kibo/contribution-graph.tsx +539 -0
- package/src/kibo/cursor.tsx +72 -0
- package/src/kibo/gantt-features.tsx +439 -0
- package/src/kibo/gantt-header.tsx +181 -0
- package/src/kibo/gantt-provider.tsx +657 -0
- package/src/kibo/gantt-sidebar.tsx +157 -0
- package/src/kibo/gantt-timeline.tsx +154 -0
- package/src/kibo/index.ts +151 -0
- package/src/kibo/kanban.tsx +338 -0
- package/src/kibo/kanbanDropTarget.ts +25 -0
- package/src/kibo/list.tsx +162 -0
- package/src/kibo/theme-switcher.tsx +109 -0
- package/src/kibo/video-player.tsx +237 -0
- package/src/markdown/Markdown.tsx +112 -0
- package/src/markdown/MarkdownBlockquote.tsx +45 -0
- package/src/markdown/MarkdownCodeBlock.tsx +197 -0
- package/src/markdown/MarkdownInlineCode.tsx +40 -0
- package/src/markdown/MarkdownLink.tsx +92 -0
- package/src/markdown/codeFence.ts +57 -0
- package/src/markdown/codeHighlight.ts +138 -0
- package/src/markdown/incrementalBlocks.ts +182 -0
- package/src/markdown/index.ts +3 -0
- package/src/markdown/markdownClipboard.ts +178 -0
- package/src/markdown/mermaid.ts +155 -0
- package/src/markdown/rehypeGithubAlerts.ts +80 -0
- package/src/motion/presets.ts +24 -0
- package/src/ui/__snapshots__/alt-wrappers.test.tsx.snap +19 -0
- package/src/ui/__snapshots__/sonner-dom.test.tsx.snap +5 -0
- package/src/ui/_menu-parts.tsx +29 -0
- package/src/ui/accordion.alt.tsx +46 -53
- package/src/ui/accordion.tsx +59 -10
- package/src/ui/avatar.tsx +56 -0
- package/src/ui/badge.tsx +45 -21
- package/src/ui/border-beam.tsx +97 -0
- package/src/ui/button-group.tsx +50 -21
- package/src/ui/button.tsx +67 -45
- package/src/ui/calendar.tsx +48 -5
- package/src/ui/card.tsx +77 -8
- package/src/ui/carousel.tsx +60 -0
- package/src/ui/centered-spinner.tsx +30 -0
- package/src/ui/checkbox.tsx +34 -16
- package/src/ui/clear-input.alt.tsx +33 -75
- package/src/ui/clear-input.tsx +140 -15
- package/src/ui/collapsible.tsx +40 -2
- package/src/ui/command.tsx +143 -70
- package/src/ui/context-menu.tsx +159 -93
- package/src/ui/crossfade-icon.tsx +123 -0
- package/src/ui/dialog.tsx +154 -70
- package/src/ui/dropdown-menu.tsx +177 -117
- package/src/ui/hover-card.tsx +59 -17
- package/src/ui/input-group.alt.tsx +83 -83
- package/src/ui/input-group.tsx +129 -58
- package/src/ui/input.tsx +13 -4
- package/src/ui/label.tsx +11 -8
- package/src/ui/list-row.tsx +232 -0
- package/src/ui/loading-state.tsx +215 -0
- package/src/ui/popover.tsx +56 -23
- package/src/ui/progress.tsx +28 -16
- package/src/ui/refresh-spin-icon.tsx +41 -0
- package/src/ui/search-input.tsx +66 -15
- package/src/ui/select.tsx +171 -100
- package/src/ui/separator.tsx +23 -14
- package/src/ui/slider.tsx +21 -7
- package/src/ui/sonner.tsx +68 -30
- package/src/ui/spinner.tsx +229 -11
- package/src/ui/status-dot.tsx +84 -0
- package/src/ui/surface.tsx +49 -0
- package/src/ui/switch.tsx +29 -15
- package/src/ui/table.tsx +38 -16
- package/src/ui/tabs.tsx +123 -35
- package/src/ui/tabsSliding.ts +33 -1
- package/src/ui/textarea.tsx +13 -4
- package/src/ui/tooltip.tsx +50 -14
- package/src/ui/ui-config.tsx +149 -0
- package/src/utils/cat-color.ts +123 -0
- package/src/utils/dayjs.ts +6 -0
- package/src/utils/gesture.ts +59 -0
- package/src/utils/runtimeVisibility.ts +167 -0
- package/src/utils/sharedClock.ts +96 -0
- package/src/utils/surface-radius.ts +16 -0
- package/src/utils/useDragSensors.ts +58 -0
- package/tokens/examples/tokens-moved/Fixture.tsx +283 -0
- package/tokens/examples/tokens-moved/README.md +20 -0
- package/tokens/examples/tokens-moved/index.html +12 -0
- package/tokens/examples/tokens-moved/main.tsx +6 -0
- package/tokens/examples/tokens-moved/package.json +14 -0
- package/tokens/examples/tokens-moved/styles.css +12 -0
- package/tokens/examples/tokens-moved/tsconfig.json +14 -0
- package/tokens/examples/tokens-moved/vite.config.ts +11 -0
- package/tokens/src/css/border-beam.css +116 -0
- package/tokens/src/css/markdown.css +318 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import dayjs from "../utils/dayjs";
|
|
4
|
+
import {
|
|
5
|
+
type CSSProperties,
|
|
6
|
+
createContext,
|
|
7
|
+
Fragment,
|
|
8
|
+
type HTMLAttributes,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
useContext,
|
|
11
|
+
useMemo,
|
|
12
|
+
} from "react";
|
|
13
|
+
import { useCn } from "../ui/ui-config";
|
|
14
|
+
|
|
15
|
+
export type ContributionGraphActivity = {
|
|
16
|
+
date: string;
|
|
17
|
+
count: number;
|
|
18
|
+
level: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
22
|
+
|
|
23
|
+
type Week = Array<ContributionGraphActivity | undefined>;
|
|
24
|
+
|
|
25
|
+
export type ContributionGraphLabels = {
|
|
26
|
+
months?: string[];
|
|
27
|
+
weekdays?: string[];
|
|
28
|
+
totalCount?: string;
|
|
29
|
+
legend?: {
|
|
30
|
+
less?: string;
|
|
31
|
+
more?: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type MonthLabel = {
|
|
36
|
+
weekIndex: number;
|
|
37
|
+
label: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const DEFAULT_MONTH_LABELS = [
|
|
41
|
+
"Jan",
|
|
42
|
+
"Feb",
|
|
43
|
+
"Mar",
|
|
44
|
+
"Apr",
|
|
45
|
+
"May",
|
|
46
|
+
"Jun",
|
|
47
|
+
"Jul",
|
|
48
|
+
"Aug",
|
|
49
|
+
"Sep",
|
|
50
|
+
"Oct",
|
|
51
|
+
"Nov",
|
|
52
|
+
"Dec",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const DEFAULT_LABELS: ContributionGraphLabels = {
|
|
56
|
+
months: DEFAULT_MONTH_LABELS,
|
|
57
|
+
weekdays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
58
|
+
totalCount: "{{count}} activities in {{year}}",
|
|
59
|
+
legend: {
|
|
60
|
+
less: "Less",
|
|
61
|
+
more: "More",
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type ContributionGraphContextType = {
|
|
66
|
+
data: ContributionGraphActivity[];
|
|
67
|
+
weeks: Week[];
|
|
68
|
+
blockMargin: number;
|
|
69
|
+
blockRadius: number;
|
|
70
|
+
blockSize: number;
|
|
71
|
+
fontSize: number;
|
|
72
|
+
labels: ContributionGraphLabels;
|
|
73
|
+
labelHeight: number;
|
|
74
|
+
maxLevel: number;
|
|
75
|
+
totalCount: number;
|
|
76
|
+
weekStart: WeekDay;
|
|
77
|
+
year: number;
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const ContributionGraphContext =
|
|
83
|
+
createContext<ContributionGraphContextType | null>(null);
|
|
84
|
+
|
|
85
|
+
const useContributionGraph = () => {
|
|
86
|
+
const context = useContext(ContributionGraphContext);
|
|
87
|
+
|
|
88
|
+
if (!context) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"ContributionGraph components must be used within a ContributionGraph",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return context;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type Dayjs = ReturnType<typeof dayjs>;
|
|
98
|
+
|
|
99
|
+
const eachDayOfInterval = (start: Dayjs, end: Dayjs): Dayjs[] => {
|
|
100
|
+
const days: Dayjs[] = [];
|
|
101
|
+
let current = start.startOf("day");
|
|
102
|
+
const endDay = end.startOf("day");
|
|
103
|
+
while (current.isBefore(endDay) || current.isSame(endDay, "day")) {
|
|
104
|
+
days.push(current);
|
|
105
|
+
current = current.add(1, "day");
|
|
106
|
+
}
|
|
107
|
+
return days;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const fillHoles = (
|
|
111
|
+
activities: ContributionGraphActivity[],
|
|
112
|
+
): ContributionGraphActivity[] => {
|
|
113
|
+
if (activities.length === 0) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const sortedActivities = [...activities].sort((a, b) =>
|
|
118
|
+
a.date.localeCompare(b.date),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const calendar = new Map<string, ContributionGraphActivity>(
|
|
122
|
+
activities.map((a) => [a.date, a]),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const firstActivity = sortedActivities[0];
|
|
126
|
+
const lastActivity = sortedActivities.at(-1);
|
|
127
|
+
|
|
128
|
+
if (!firstActivity || !lastActivity) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return eachDayOfInterval(
|
|
133
|
+
dayjs(firstActivity.date),
|
|
134
|
+
dayjs(lastActivity.date),
|
|
135
|
+
).map((day) => {
|
|
136
|
+
const date = day.format("YYYY-MM-DD");
|
|
137
|
+
|
|
138
|
+
const existing = calendar.get(date);
|
|
139
|
+
if (existing) {
|
|
140
|
+
return existing;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
date,
|
|
145
|
+
count: 0,
|
|
146
|
+
level: 0,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const nextDayOfWeek = (date: Dayjs, targetDay: WeekDay): Dayjs => {
|
|
152
|
+
const currentDay = date.day();
|
|
153
|
+
const daysUntil = (targetDay - currentDay + 7) % 7 || 7;
|
|
154
|
+
return date.add(daysUntil, "day");
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const groupByWeeks = (
|
|
158
|
+
activities: ContributionGraphActivity[],
|
|
159
|
+
weekStart: WeekDay = 0,
|
|
160
|
+
): Week[] => {
|
|
161
|
+
if (activities.length === 0) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const normalizedActivities = fillHoles(activities);
|
|
166
|
+
const firstActivity = normalizedActivities[0];
|
|
167
|
+
if (!firstActivity) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const firstDate = dayjs(firstActivity.date);
|
|
172
|
+
const firstCalendarDate =
|
|
173
|
+
firstDate.day() === weekStart
|
|
174
|
+
? firstDate
|
|
175
|
+
: nextDayOfWeek(firstDate, weekStart).subtract(1, "week");
|
|
176
|
+
|
|
177
|
+
const paddingDays = firstDate.diff(firstCalendarDate, "day");
|
|
178
|
+
const paddedActivities: Array<ContributionGraphActivity | undefined> = [
|
|
179
|
+
...Array.from<undefined>({ length: paddingDays }),
|
|
180
|
+
...normalizedActivities,
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
const numberOfWeeks = Math.ceil(paddedActivities.length / 7);
|
|
184
|
+
|
|
185
|
+
return Array.from({ length: numberOfWeeks }, (_, weekIndex) =>
|
|
186
|
+
paddedActivities.slice(weekIndex * 7, weekIndex * 7 + 7),
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const getMonthLabels = (
|
|
191
|
+
weeks: Week[],
|
|
192
|
+
monthNames: string[] = DEFAULT_MONTH_LABELS,
|
|
193
|
+
): MonthLabel[] => {
|
|
194
|
+
return weeks
|
|
195
|
+
.reduce<MonthLabel[]>((labels, week, weekIndex) => {
|
|
196
|
+
const firstActivity = week.find((activity) => activity !== undefined);
|
|
197
|
+
|
|
198
|
+
if (!firstActivity) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`Unexpected error: Week ${weekIndex + 1} is empty: [${week}].`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const month = monthNames[dayjs(firstActivity.date).month()];
|
|
205
|
+
|
|
206
|
+
if (!month) {
|
|
207
|
+
const monthName = dayjs(firstActivity.date).format("MMM");
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Unexpected error: undefined month label for ${monthName}.`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const prevLabel = labels.at(-1);
|
|
214
|
+
|
|
215
|
+
if (weekIndex === 0 || !prevLabel || prevLabel.label !== month) {
|
|
216
|
+
return labels.concat({ weekIndex, label: month });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return labels;
|
|
220
|
+
}, [])
|
|
221
|
+
.filter(({ weekIndex }, index, labels) => {
|
|
222
|
+
const minWeeks = 3;
|
|
223
|
+
|
|
224
|
+
if (index === 0) {
|
|
225
|
+
return labels[1] && labels[1].weekIndex - weekIndex >= minWeeks;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (index === labels.length - 1) {
|
|
229
|
+
return weeks.slice(weekIndex).length >= minWeeks;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return true;
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export type ContributionGraphProps = HTMLAttributes<HTMLDivElement> & {
|
|
237
|
+
data: ContributionGraphActivity[];
|
|
238
|
+
blockMargin?: number;
|
|
239
|
+
blockRadius?: number;
|
|
240
|
+
blockSize?: number;
|
|
241
|
+
fontSize?: number;
|
|
242
|
+
labels?: ContributionGraphLabels;
|
|
243
|
+
maxLevel?: number;
|
|
244
|
+
style?: CSSProperties;
|
|
245
|
+
totalCount?: number;
|
|
246
|
+
weekStart?: WeekDay;
|
|
247
|
+
children: ReactNode;
|
|
248
|
+
className?: string;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const ContributionGraph = ({
|
|
252
|
+
data,
|
|
253
|
+
blockMargin = 4,
|
|
254
|
+
blockRadius = 2,
|
|
255
|
+
blockSize = 12,
|
|
256
|
+
fontSize = 14,
|
|
257
|
+
labels: labelsProp = undefined,
|
|
258
|
+
maxLevel: maxLevelProp = 4,
|
|
259
|
+
style = {},
|
|
260
|
+
totalCount: totalCountProp = undefined,
|
|
261
|
+
weekStart = 0,
|
|
262
|
+
className,
|
|
263
|
+
children,
|
|
264
|
+
...props
|
|
265
|
+
}: ContributionGraphProps) => {
|
|
266
|
+
const merge = useCn();
|
|
267
|
+
const maxLevel = Math.max(1, maxLevelProp);
|
|
268
|
+
const weeks = useMemo(() => groupByWeeks(data, weekStart), [data, weekStart]);
|
|
269
|
+
const LABEL_MARGIN = 8;
|
|
270
|
+
|
|
271
|
+
const labels = { ...DEFAULT_LABELS, ...labelsProp };
|
|
272
|
+
const labelHeight = fontSize + LABEL_MARGIN;
|
|
273
|
+
|
|
274
|
+
const firstActivity = data[0];
|
|
275
|
+
const year = firstActivity
|
|
276
|
+
? dayjs(firstActivity.date).year()
|
|
277
|
+
: new Date().getFullYear();
|
|
278
|
+
|
|
279
|
+
const totalCount =
|
|
280
|
+
typeof totalCountProp === "number"
|
|
281
|
+
? totalCountProp
|
|
282
|
+
: data.reduce((sum, activity) => sum + activity.count, 0);
|
|
283
|
+
|
|
284
|
+
const width = weeks.length * (blockSize + blockMargin) - blockMargin;
|
|
285
|
+
const height = labelHeight + (blockSize + blockMargin) * 7 - blockMargin;
|
|
286
|
+
|
|
287
|
+
if (data.length === 0) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<ContributionGraphContext.Provider
|
|
293
|
+
value={{
|
|
294
|
+
data,
|
|
295
|
+
weeks,
|
|
296
|
+
blockMargin,
|
|
297
|
+
blockRadius,
|
|
298
|
+
blockSize,
|
|
299
|
+
fontSize,
|
|
300
|
+
labels,
|
|
301
|
+
labelHeight,
|
|
302
|
+
maxLevel,
|
|
303
|
+
totalCount,
|
|
304
|
+
weekStart,
|
|
305
|
+
year,
|
|
306
|
+
width,
|
|
307
|
+
height,
|
|
308
|
+
}}
|
|
309
|
+
>
|
|
310
|
+
<div
|
|
311
|
+
className={merge("flex w-max max-w-full flex-col gap-2", className)}
|
|
312
|
+
style={{ fontSize, ...style }}
|
|
313
|
+
{...props}
|
|
314
|
+
>
|
|
315
|
+
{children}
|
|
316
|
+
</div>
|
|
317
|
+
</ContributionGraphContext.Provider>
|
|
318
|
+
);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export type ContributionGraphBlockProps = HTMLAttributes<SVGRectElement> & {
|
|
322
|
+
activity: ContributionGraphActivity;
|
|
323
|
+
dayIndex: number;
|
|
324
|
+
weekIndex: number;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export const ContributionGraphBlock = ({
|
|
328
|
+
activity,
|
|
329
|
+
dayIndex,
|
|
330
|
+
weekIndex,
|
|
331
|
+
className,
|
|
332
|
+
...props
|
|
333
|
+
}: ContributionGraphBlockProps) => {
|
|
334
|
+
const merge = useCn();
|
|
335
|
+
const { blockSize, blockMargin, blockRadius, labelHeight, maxLevel } =
|
|
336
|
+
useContributionGraph();
|
|
337
|
+
|
|
338
|
+
if (activity.level < 0 || activity.level > maxLevel) {
|
|
339
|
+
throw new RangeError(
|
|
340
|
+
`Provided activity level ${activity.level} for ${activity.date} is out of range. It must be between 0 and ${maxLevel}.`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
<rect
|
|
346
|
+
className={merge(
|
|
347
|
+
'data-[level="0"]:fill-surface-secondary',
|
|
348
|
+
'data-[level="1"]:fill-primary/20',
|
|
349
|
+
'data-[level="2"]:fill-primary/40',
|
|
350
|
+
'data-[level="3"]:fill-primary/60',
|
|
351
|
+
'data-[level="4"]:fill-primary/80',
|
|
352
|
+
className,
|
|
353
|
+
)}
|
|
354
|
+
data-count={activity.count}
|
|
355
|
+
data-date={activity.date}
|
|
356
|
+
data-level={activity.level}
|
|
357
|
+
height={blockSize}
|
|
358
|
+
rx={blockRadius}
|
|
359
|
+
ry={blockRadius}
|
|
360
|
+
width={blockSize}
|
|
361
|
+
x={(blockSize + blockMargin) * weekIndex}
|
|
362
|
+
y={labelHeight + (blockSize + blockMargin) * dayIndex}
|
|
363
|
+
{...props}
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export type ContributionGraphCalendarProps = Omit<
|
|
369
|
+
HTMLAttributes<HTMLDivElement>,
|
|
370
|
+
"children"
|
|
371
|
+
> & {
|
|
372
|
+
hideMonthLabels?: boolean;
|
|
373
|
+
className?: string;
|
|
374
|
+
children: (props: {
|
|
375
|
+
activity: ContributionGraphActivity;
|
|
376
|
+
dayIndex: number;
|
|
377
|
+
weekIndex: number;
|
|
378
|
+
}) => ReactNode;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export const ContributionGraphCalendar = ({
|
|
382
|
+
hideMonthLabels = false,
|
|
383
|
+
className,
|
|
384
|
+
children,
|
|
385
|
+
...props
|
|
386
|
+
}: ContributionGraphCalendarProps) => {
|
|
387
|
+
const merge = useCn();
|
|
388
|
+
const { weeks, width, height, blockSize, blockMargin, labels } =
|
|
389
|
+
useContributionGraph();
|
|
390
|
+
|
|
391
|
+
const monthLabels = useMemo(
|
|
392
|
+
() => getMonthLabels(weeks, labels.months),
|
|
393
|
+
[weeks, labels.months],
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<div
|
|
398
|
+
className={merge("max-w-full overflow-x-auto overflow-y-hidden", className)}
|
|
399
|
+
{...props}
|
|
400
|
+
>
|
|
401
|
+
<svg
|
|
402
|
+
className="block overflow-visible"
|
|
403
|
+
height={height}
|
|
404
|
+
viewBox={`0 0 ${width} ${height}`}
|
|
405
|
+
width={width}
|
|
406
|
+
>
|
|
407
|
+
<title>Contribution Graph</title>
|
|
408
|
+
{!hideMonthLabels && (
|
|
409
|
+
<g className="fill-current">
|
|
410
|
+
{monthLabels.map(({ label, weekIndex }) => (
|
|
411
|
+
<text
|
|
412
|
+
dominantBaseline="hanging"
|
|
413
|
+
key={weekIndex}
|
|
414
|
+
x={(blockSize + blockMargin) * weekIndex}
|
|
415
|
+
>
|
|
416
|
+
{label}
|
|
417
|
+
</text>
|
|
418
|
+
))}
|
|
419
|
+
</g>
|
|
420
|
+
)}
|
|
421
|
+
{weeks.map((week, weekIndex) =>
|
|
422
|
+
week.map((activity, dayIndex) => {
|
|
423
|
+
if (!activity) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return (
|
|
428
|
+
<Fragment key={`${weekIndex}-${dayIndex}`}>
|
|
429
|
+
{children({ activity, dayIndex, weekIndex })}
|
|
430
|
+
</Fragment>
|
|
431
|
+
);
|
|
432
|
+
}),
|
|
433
|
+
)}
|
|
434
|
+
</svg>
|
|
435
|
+
</div>
|
|
436
|
+
);
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
export type ContributionGraphFooterProps = HTMLAttributes<HTMLDivElement>;
|
|
440
|
+
|
|
441
|
+
export const ContributionGraphFooter = ({
|
|
442
|
+
className,
|
|
443
|
+
...props
|
|
444
|
+
}: ContributionGraphFooterProps) => {
|
|
445
|
+
const merge = useCn();
|
|
446
|
+
return (
|
|
447
|
+
<div
|
|
448
|
+
className={merge(
|
|
449
|
+
"flex flex-wrap gap-1 whitespace-nowrap sm:gap-x-4",
|
|
450
|
+
className,
|
|
451
|
+
)}
|
|
452
|
+
{...props}
|
|
453
|
+
/>
|
|
454
|
+
);
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
export type ContributionGraphTotalCountProps = Omit<
|
|
458
|
+
HTMLAttributes<HTMLDivElement>,
|
|
459
|
+
"children"
|
|
460
|
+
> & {
|
|
461
|
+
children?: (props: { totalCount: number; year: number }) => ReactNode;
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
export const ContributionGraphTotalCount = ({
|
|
465
|
+
className,
|
|
466
|
+
children,
|
|
467
|
+
...props
|
|
468
|
+
}: ContributionGraphTotalCountProps) => {
|
|
469
|
+
const merge = useCn();
|
|
470
|
+
const { totalCount, year, labels } = useContributionGraph();
|
|
471
|
+
|
|
472
|
+
if (children) {
|
|
473
|
+
return <>{children({ totalCount, year })}</>;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return (
|
|
477
|
+
<div className={merge("text-muted-foreground", className)} {...props}>
|
|
478
|
+
{labels.totalCount
|
|
479
|
+
? labels.totalCount
|
|
480
|
+
.replace("{{count}}", String(totalCount))
|
|
481
|
+
.replace("{{year}}", String(year))
|
|
482
|
+
: `${totalCount} activities in ${year}`}
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
export type ContributionGraphLegendProps = Omit<
|
|
488
|
+
HTMLAttributes<HTMLDivElement>,
|
|
489
|
+
"children"
|
|
490
|
+
> & {
|
|
491
|
+
children?: (props: { level: number }) => ReactNode;
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
export const ContributionGraphLegend = ({
|
|
495
|
+
className,
|
|
496
|
+
children,
|
|
497
|
+
...props
|
|
498
|
+
}: ContributionGraphLegendProps) => {
|
|
499
|
+
const merge = useCn();
|
|
500
|
+
const { labels, maxLevel, blockSize, blockRadius } = useContributionGraph();
|
|
501
|
+
|
|
502
|
+
return (
|
|
503
|
+
<div
|
|
504
|
+
className={merge("ml-auto flex items-center gap-[3px]", className)}
|
|
505
|
+
{...props}
|
|
506
|
+
>
|
|
507
|
+
<span className="mr-1 text-muted-foreground">
|
|
508
|
+
{labels.legend?.less || "Less"}
|
|
509
|
+
</span>
|
|
510
|
+
{Array.from({ length: maxLevel + 1 }, (_, level) =>
|
|
511
|
+
children ? (
|
|
512
|
+
<Fragment key={level}>{children({ level })}</Fragment>
|
|
513
|
+
) : (
|
|
514
|
+
<svg height={blockSize} key={level} width={blockSize}>
|
|
515
|
+
<title>{`${level} contributions`}</title>
|
|
516
|
+
<rect
|
|
517
|
+
className={merge(
|
|
518
|
+
"stroke-[1px] stroke-border",
|
|
519
|
+
'data-[level="0"]:fill-surface-secondary',
|
|
520
|
+
'data-[level="1"]:fill-primary/20',
|
|
521
|
+
'data-[level="2"]:fill-primary/40',
|
|
522
|
+
'data-[level="3"]:fill-primary/60',
|
|
523
|
+
'data-[level="4"]:fill-primary/80',
|
|
524
|
+
)}
|
|
525
|
+
data-level={level}
|
|
526
|
+
height={blockSize}
|
|
527
|
+
rx={blockRadius}
|
|
528
|
+
ry={blockRadius}
|
|
529
|
+
width={blockSize}
|
|
530
|
+
/>
|
|
531
|
+
</svg>
|
|
532
|
+
),
|
|
533
|
+
)}
|
|
534
|
+
<span className="ml-1 text-muted-foreground">
|
|
535
|
+
{labels.legend?.more || "More"}
|
|
536
|
+
</span>
|
|
537
|
+
</div>
|
|
538
|
+
);
|
|
539
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { HTMLAttributes, SVGProps } from "react";
|
|
2
|
+
import { useCn } from "../ui/ui-config";
|
|
3
|
+
|
|
4
|
+
export type LiveCursorProps = HTMLAttributes<HTMLSpanElement>;
|
|
5
|
+
|
|
6
|
+
export const LiveCursor = ({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
...props
|
|
10
|
+
}: LiveCursorProps) => {
|
|
11
|
+
const merge = useCn();
|
|
12
|
+
return (
|
|
13
|
+
<span
|
|
14
|
+
className={merge("pointer-events-none relative select-none", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
>
|
|
17
|
+
{children}
|
|
18
|
+
</span>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type LiveCursorPointerProps = SVGProps<SVGSVGElement>;
|
|
23
|
+
|
|
24
|
+
export const LiveCursorPointer = ({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: LiveCursorPointerProps) => {
|
|
28
|
+
const merge = useCn();
|
|
29
|
+
return (
|
|
30
|
+
<svg
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
className={merge("size-3.5", className)}
|
|
33
|
+
fill="none"
|
|
34
|
+
focusable="false"
|
|
35
|
+
height="20"
|
|
36
|
+
viewBox="0 0 20 20"
|
|
37
|
+
width="20"
|
|
38
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
<path
|
|
42
|
+
d="M19.438 6.716 1.115.05A.832.832 0 0 0 .05 1.116L6.712 19.45a.834.834 0 0 0 1.557.025l3.198-8 7.995-3.2a.833.833 0 0 0 0-1.559h-.024Z"
|
|
43
|
+
fill="currentColor"
|
|
44
|
+
/>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type LiveCursorBodyProps = HTMLAttributes<HTMLSpanElement>;
|
|
50
|
+
|
|
51
|
+
export const LiveCursorBody = ({
|
|
52
|
+
children,
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: LiveCursorBodyProps) => {
|
|
56
|
+
const merge = useCn();
|
|
57
|
+
return (
|
|
58
|
+
<span
|
|
59
|
+
className={merge(
|
|
60
|
+
"relative ml-3.5 flex flex-col whitespace-nowrap rounded-lg py-1 pr-3 pl-2.5 text-xs",
|
|
61
|
+
className,
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</span>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type LiveCursorNameProps = HTMLAttributes<HTMLSpanElement>;
|
|
71
|
+
|
|
72
|
+
export const LiveCursorName = (props: LiveCursorNameProps) => <span {...props} />;
|