@tulipfarm/sprout 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -40
- package/dist/components/accordion.d.ts +2 -2
- package/dist/components/accordion.js +9 -9
- package/dist/components/agent-fanout.d.ts +2 -2
- package/dist/components/agent-fanout.js +65 -87
- package/dist/components/alert-dialog.d.ts +6 -6
- package/dist/components/alert-dialog.js +20 -20
- package/dist/components/alert.d.ts +6 -6
- package/dist/components/alert.js +13 -13
- package/dist/components/aspect-ratio.d.ts +2 -2
- package/dist/components/aspect-ratio.js +2 -2
- package/dist/components/attachment.d.ts +17 -17
- package/dist/components/attachment.js +82 -115
- package/dist/components/avatar.d.ts +10 -10
- package/dist/components/avatar.js +14 -14
- package/dist/components/badge.d.ts +4 -4
- package/dist/components/badge.js +18 -18
- package/dist/components/board.d.ts +107 -0
- package/dist/components/board.js +640 -0
- package/dist/components/brand-icon.d.ts +6 -6
- package/dist/components/brand-icon.js +55 -63
- package/dist/components/breadcrumb.d.ts +11 -11
- package/dist/components/breadcrumb.js +16 -18
- package/dist/components/button-group.d.ts +6 -6
- package/dist/components/button-group.js +14 -14
- package/dist/components/button.d.ts +11 -11
- package/dist/components/button.js +46 -56
- package/dist/components/calendar.d.ts +5 -5
- package/dist/components/calendar.js +44 -53
- package/dist/components/canvas.d.ts +4 -4
- package/dist/components/canvas.js +39 -46
- package/dist/components/card.d.ts +6 -6
- package/dist/components/card.js +17 -17
- package/dist/components/carousel.d.ts +7 -7
- package/dist/components/carousel.js +57 -75
- package/dist/components/chart.d.ts +5 -112
- package/dist/components/chart.js +5 -172
- package/dist/components/chat-confirmation.d.ts +13 -11
- package/dist/components/chat-confirmation.js +32 -38
- package/dist/components/chat-input.d.ts +4 -4
- package/dist/components/chat-input.js +74 -81
- package/dist/components/chat-plan.d.ts +2 -2
- package/dist/components/chat-plan.js +15 -20
- package/dist/components/chat-sources.d.ts +8 -8
- package/dist/components/chat-sources.js +18 -27
- package/dist/components/checkbox.d.ts +1 -1
- package/dist/components/checkbox.js +9 -9
- package/dist/components/choice-list.d.ts +2 -2
- package/dist/components/choice-list.js +16 -18
- package/dist/components/code-block.d.ts +5 -5
- package/dist/components/code-block.js +26 -26
- package/dist/components/collapsible.d.ts +1 -1
- package/dist/components/collapsible.js +7 -11
- package/dist/components/combobox.d.ts +3 -3
- package/dist/components/combobox.js +24 -24
- package/dist/components/command.d.ts +13 -13
- package/dist/components/command.js +61 -73
- package/dist/components/context-menu.d.ts +5 -5
- package/dist/components/context-menu.js +22 -22
- package/dist/components/dialog.d.ts +6 -6
- package/dist/components/dialog.js +23 -28
- package/dist/components/direction.d.ts +1 -1
- package/dist/components/direction.js +1 -1
- package/dist/components/dropdown-menu.d.ts +5 -5
- package/dist/components/dropdown-menu.js +20 -20
- package/dist/components/field.d.ts +11 -11
- package/dist/components/field.js +41 -61
- package/dist/components/file-explorer.d.ts +2 -2
- package/dist/components/file-explorer.js +89 -105
- package/dist/components/file-viewer.d.ts +10 -10
- package/dist/components/file-viewer.js +112 -140
- package/dist/components/form.d.ts +7 -7
- package/dist/components/form.js +24 -28
- package/dist/components/glass-banner.d.ts +3 -3
- package/dist/components/glass-banner.js +21 -24
- package/dist/components/gradient-banner.d.ts +5 -5
- package/dist/components/gradient-banner.js +13 -22
- package/dist/components/hover-card.d.ts +4 -4
- package/dist/components/hover-card.js +6 -6
- package/dist/components/input-group.d.ts +11 -11
- package/dist/components/input-group.js +22 -36
- package/dist/components/input-otp.d.ts +10 -10
- package/dist/components/input-otp.js +13 -15
- package/dist/components/input.d.ts +1 -1
- package/dist/components/input.js +7 -7
- package/dist/components/kbd.d.ts +3 -3
- package/dist/components/kbd.js +5 -5
- package/dist/components/label.d.ts +2 -2
- package/dist/components/label.js +2 -2
- package/dist/components/mention-list.d.ts +3 -3
- package/dist/components/mention-list.js +10 -10
- package/dist/components/menubar.d.ts +6 -6
- package/dist/components/menubar.js +22 -22
- package/dist/components/message-actions.d.ts +2 -2
- package/dist/components/message-actions.js +29 -31
- package/dist/components/message-response.d.ts +3 -3
- package/dist/components/message-response.js +18 -18
- package/dist/components/message-scroller.d.ts +12 -12
- package/dist/components/message-scroller.js +44 -56
- package/dist/components/message.d.ts +11 -11
- package/dist/components/message.js +25 -25
- package/dist/components/navigation-menu.d.ts +2 -2
- package/dist/components/navigation-menu.js +15 -15
- package/dist/components/notice-banner.d.ts +2 -2
- package/dist/components/notice-banner.js +2 -2
- package/dist/components/page-content.d.ts +2 -2
- package/dist/components/page-content.js +2 -2
- package/dist/components/page-header.d.ts +1 -1
- package/dist/components/page-header.js +16 -16
- package/dist/components/pagination.d.ts +9 -9
- package/dist/components/pagination.js +17 -17
- package/dist/components/plan-outline.d.ts +2 -2
- package/dist/components/plan-outline.js +43 -60
- package/dist/components/popover.d.ts +6 -6
- package/dist/components/popover.js +15 -15
- package/dist/components/presence-cursor.d.ts +2 -2
- package/dist/components/presence-cursor.js +3 -3
- package/dist/components/progress.d.ts +1 -1
- package/dist/components/progress.js +8 -8
- package/dist/components/prompt-list.d.ts +3 -3
- package/dist/components/prompt-list.js +6 -6
- package/dist/components/questionnaire.d.ts +19 -19
- package/dist/components/questionnaire.js +183 -242
- package/dist/components/radio-group.d.ts +2 -2
- package/dist/components/radio-group.js +6 -6
- package/dist/components/reasoning-effort.d.ts +3 -3
- package/dist/components/reasoning-effort.js +9 -12
- package/dist/components/record-list.d.ts +66 -0
- package/dist/components/record-list.js +151 -0
- package/dist/components/resizable.d.ts +5 -5
- package/dist/components/resizable.js +76 -102
- package/dist/components/result-card.d.ts +10 -5
- package/dist/components/result-card.js +42 -53
- package/dist/components/result-notice.d.ts +18 -0
- package/dist/components/result-notice.js +29 -0
- package/dist/components/review-card.d.ts +6 -6
- package/dist/components/review-card.js +18 -18
- package/dist/components/rich-text-editor.d.ts +2 -2
- package/dist/components/rich-text-editor.js +56 -72
- package/dist/components/schema-diagram.d.ts +3 -3
- package/dist/components/schema-diagram.js +39 -53
- package/dist/components/scroll-area.d.ts +1 -1
- package/dist/components/scroll-area.js +10 -14
- package/dist/components/search-dialog.d.ts +6 -6
- package/dist/components/search-dialog.js +16 -16
- package/dist/components/search-field.d.ts +1 -1
- package/dist/components/search-field.js +11 -11
- package/dist/components/select.d.ts +3 -3
- package/dist/components/select.js +20 -20
- package/dist/components/selection-slot.d.ts +2 -2
- package/dist/components/selection-slot.js +6 -9
- package/dist/components/separator.d.ts +15 -15
- package/dist/components/separator.js +22 -27
- package/dist/components/settings-list.d.ts +3 -3
- package/dist/components/settings-list.js +5 -5
- package/dist/components/sheet.d.ts +6 -6
- package/dist/components/sheet.js +27 -27
- package/dist/components/sidebar.d.ts +22 -22
- package/dist/components/sidebar.js +57 -58
- package/dist/components/skeleton.d.ts +5 -5
- package/dist/components/skeleton.js +14 -21
- package/dist/components/slider.d.ts +2 -2
- package/dist/components/slider.js +6 -6
- package/dist/components/spinner.d.ts +5 -5
- package/dist/components/spinner.js +22 -26
- package/dist/components/sql-viewer.d.ts +14 -14
- package/dist/components/sql-viewer.js +99 -128
- package/dist/components/state-panel-samples.d.ts +2 -2
- package/dist/components/state-panel-samples.js +209 -245
- package/dist/components/state-panel.d.ts +2 -35
- package/dist/components/state-panel.js +2 -29
- package/dist/components/state-sample.d.ts +7 -7
- package/dist/components/state-sample.js +18 -31
- package/dist/components/switch.d.ts +1 -1
- package/dist/components/switch.js +7 -7
- package/dist/components/table-nuqs.d.ts +2 -2
- package/dist/components/table-nuqs.js +11 -12
- package/dist/components/table.d.ts +4 -4
- package/dist/components/table.js +166 -235
- package/dist/components/tabs.d.ts +2 -2
- package/dist/components/tabs.js +12 -12
- package/dist/components/textarea.d.ts +2 -2
- package/dist/components/textarea.js +6 -8
- package/dist/components/timeline.d.ts +3 -0
- package/dist/components/timeline.js +3 -0
- package/dist/components/toast.d.ts +2 -2
- package/dist/components/toast.js +19 -25
- package/dist/components/toggle-group.d.ts +2 -2
- package/dist/components/toggle-group.js +18 -20
- package/dist/components/toggle.d.ts +2 -2
- package/dist/components/toggle.js +8 -8
- package/dist/components/tool-activity.d.ts +1 -1
- package/dist/components/tool-activity.js +83 -110
- package/dist/components/toolbar-status.d.ts +2 -2
- package/dist/components/toolbar-status.js +7 -7
- package/dist/components/tooltip.d.ts +5 -5
- package/dist/components/tooltip.js +12 -11
- package/dist/components/typed-text.d.ts +2 -2
- package/dist/components/typed-text.js +10 -12
- package/dist/components/view-switcher.d.ts +51 -0
- package/dist/components/view-switcher.js +114 -0
- package/dist/index.d.ts +98 -93
- package/dist/index.js +98 -93
- package/dist/internal/chart-kit.d.ts +142 -0
- package/dist/internal/chart-kit.js +200 -0
- package/dist/internal/charts/bars.d.ts +61 -0
- package/dist/internal/charts/bars.js +86 -0
- package/dist/internal/charts/lines.d.ts +67 -0
- package/dist/internal/charts/lines.js +70 -0
- package/dist/internal/charts/more.d.ts +77 -0
- package/dist/internal/charts/more.js +68 -0
- package/dist/internal/charts/parts.d.ts +54 -0
- package/dist/internal/charts/parts.js +82 -0
- package/dist/internal/charts/timeline-dates.d.ts +38 -0
- package/dist/internal/charts/timeline-dates.js +113 -0
- package/dist/internal/charts/timeline-window.d.ts +31 -0
- package/dist/internal/charts/timeline-window.js +26 -0
- package/dist/internal/charts/timeline.d.ts +72 -0
- package/dist/internal/charts/timeline.js +287 -0
- package/dist/internal/classes.d.ts +1 -1
- package/dist/internal/classes.js +7 -9
- package/dist/internal/code-tokens.d.ts +2 -2
- package/dist/internal/code-tokens.js +12 -12
- package/dist/internal/fieldset.d.ts +1 -1
- package/dist/internal/fieldset.js +6 -6
- package/dist/internal/file-kind.d.ts +1 -1
- package/dist/internal/file-kind.js +43 -51
- package/dist/internal/flow.d.ts +4 -4
- package/dist/internal/flow.js +20 -23
- package/dist/internal/languages.js +999 -999
- package/dist/internal/message-scroller.d.ts +3 -3
- package/dist/internal/message-scroller.js +64 -112
- package/dist/internal/pressable.d.ts +1 -1
- package/dist/internal/pressable.js +7 -7
- package/dist/internal/state-panel.d.ts +35 -0
- package/dist/internal/state-panel.js +22 -0
- package/dist/internal/syntax.d.ts +2 -2
- package/dist/internal/syntax.js +36 -47
- package/dist/styles/accordion.css +3 -5
- package/dist/styles/agent-fanout.css +64 -60
- package/dist/styles/alert-dialog.css +12 -26
- package/dist/styles/alert.css +10 -15
- package/dist/styles/attachment.css +51 -93
- package/dist/styles/avatar.css +17 -24
- package/dist/styles/badge.css +11 -36
- package/dist/styles/board.css +571 -0
- package/dist/styles/brand-icon.css +7 -8
- package/dist/styles/breadcrumb.css +4 -7
- package/dist/styles/button-group.css +8 -14
- package/dist/styles/button.css +22 -27
- package/dist/styles/calendar.css +24 -36
- package/dist/styles/canvas.css +2 -3
- package/dist/styles/card.css +7 -23
- package/dist/styles/carousel.css +11 -12
- package/dist/styles/chart.css +215 -31
- package/dist/styles/chat-input.css +15 -39
- package/dist/styles/chat-plan.css +3 -4
- package/dist/styles/chat-sources.css +3 -6
- package/dist/styles/checkbox.css +11 -25
- package/dist/styles/choice-list.css +17 -31
- package/dist/styles/code-block.css +11 -25
- package/dist/styles/collapsible.css +10 -15
- package/dist/styles/combobox.css +11 -15
- package/dist/styles/command.css +5 -8
- package/dist/styles/context-menu.css +2 -2
- package/dist/styles/dialog.css +5 -16
- package/dist/styles/dropdown-menu.css +2 -2
- package/dist/styles/field.css +15 -21
- package/dist/styles/file-explorer.css +19 -33
- package/dist/styles/file-viewer.css +13 -17
- package/dist/styles/form.css +3 -3
- package/dist/styles/glass-banner.css +12 -47
- package/dist/styles/gradient-banner.css +17 -35
- package/dist/styles/hover-card.css +9 -15
- package/dist/styles/index.css +116 -108
- package/dist/styles/input-group.css +25 -41
- package/dist/styles/input-otp.css +5 -6
- package/dist/styles/input.css +5 -5
- package/dist/styles/kbd.css +1 -1
- package/dist/styles/label.css +9 -30
- package/dist/styles/mention-list.css +6 -8
- package/dist/styles/menubar.css +5 -6
- package/dist/styles/message-actions.css +1 -1
- package/dist/styles/message-response.css +3 -5
- package/dist/styles/message-scroller.css +14 -21
- package/dist/styles/message.css +23 -60
- package/dist/styles/navigation-menu.css +14 -29
- package/dist/styles/notice-banner.css +1 -2
- package/dist/styles/page-header.css +7 -10
- package/dist/styles/pagination.css +1 -4
- package/dist/styles/plan-outline.css +20 -37
- package/dist/styles/popover.css +10 -19
- package/dist/styles/presence-cursor.css +1 -2
- package/dist/styles/progress.css +2 -4
- package/dist/styles/prompt-list.css +6 -17
- package/dist/styles/questionnaire.css +16 -40
- package/dist/styles/radio-group.css +12 -23
- package/dist/styles/reasoning-effort.css +8 -15
- package/dist/styles/record-list.css +243 -0
- package/dist/styles/resizable.css +10 -11
- package/dist/styles/result-card.css +17 -3
- package/dist/styles/result-notice.css +80 -0
- package/dist/styles/review-card.css +2 -3
- package/dist/styles/rich-text-editor.css +1 -2
- package/dist/styles/schema-diagram.css +12 -17
- package/dist/styles/scroll-area.css +8 -10
- package/dist/styles/search-dialog.css +1 -5
- package/dist/styles/search-field.css +1 -2
- package/dist/styles/select.css +7 -7
- package/dist/styles/selection-slot.css +1 -2
- package/dist/styles/separator.css +7 -8
- package/dist/styles/settings-list.css +1 -2
- package/dist/styles/sheet.css +40 -73
- package/dist/styles/sidebar.css +17 -21
- package/dist/styles/skeleton.css +9 -11
- package/dist/styles/slider.css +11 -21
- package/dist/styles/spinner.css +2 -2
- package/dist/styles/sql-viewer.css +29 -29
- package/dist/styles/state-panel.css +8 -9
- package/dist/styles/state-sample.css +20 -33
- package/dist/styles/switch.css +11 -18
- package/dist/styles/table.css +51 -77
- package/dist/styles/tabs.css +12 -16
- package/dist/styles/textarea.css +3 -3
- package/dist/styles/themes/chatgpt.css +11 -23
- package/dist/styles/themes/claude.css +12 -24
- package/dist/styles/themes/cloudflare.css +11 -23
- package/dist/styles/themes/discord.css +11 -23
- package/dist/styles/themes/github.css +11 -23
- package/dist/styles/themes/grok.css +11 -23
- package/dist/styles/themes/linear.css +12 -25
- package/dist/styles/themes/neutral.css +11 -23
- package/dist/styles/themes/notion.css +11 -23
- package/dist/styles/themes/perplexity.css +11 -23
- package/dist/styles/themes/raycast.css +11 -23
- package/dist/styles/themes/spotify.css +11 -23
- package/dist/styles/themes/stripe.css +11 -23
- package/dist/styles/themes/supabase.css +11 -23
- package/dist/styles/themes/tulipfarm.css +9 -10
- package/dist/styles/themes/vercel.css +12 -25
- package/dist/styles/timeline.css +276 -0
- package/dist/styles/toast.css +13 -20
- package/dist/styles/toggle-group.css +17 -23
- package/dist/styles/toggle.css +8 -8
- package/dist/styles/tool-activity.css +7 -13
- package/dist/styles/toolbar-status.css +2 -7
- package/dist/styles/tooltip.css +15 -21
- package/dist/styles/view-switcher.css +279 -0
- package/package.json +21 -1
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
import { Bar, BarChart, CartesianGrid, Rectangle, ReferenceArea, ReferenceLine, XAxis, YAxis } from 'recharts';
|
|
5
|
+
import { Badge } from '../../components/badge.js';
|
|
6
|
+
import { Button } from '../../components/button.js';
|
|
7
|
+
import { Skeleton } from '../../components/skeleton.js';
|
|
8
|
+
import { StatePanel } from '../state-panel.js';
|
|
9
|
+
import { cn } from '../classes.js';
|
|
10
|
+
import { ChartContainer, ChartLegend, ChartTooltip, ChartTooltipContent, chartColor, configLegend, dayLabel, quietAxis, seriesConfig, tooltipCursor, } from '../chart-kit.js';
|
|
11
|
+
import { DAY, dayTime, isCalendarDate, plainSpaces, wallClock } from './timeline-dates.js';
|
|
12
|
+
const AXIS = 40;
|
|
13
|
+
const read = (value) => {
|
|
14
|
+
if (typeof value === 'number')
|
|
15
|
+
return Number.isFinite(value) ? { time: value, date: false } : null;
|
|
16
|
+
if (typeof value !== 'string' || !value)
|
|
17
|
+
return null;
|
|
18
|
+
if (isCalendarDate(value))
|
|
19
|
+
return { time: dayTime(value), date: true };
|
|
20
|
+
const time = Date.parse(value);
|
|
21
|
+
return Number.isNaN(time) ? null : { time, date: false };
|
|
22
|
+
};
|
|
23
|
+
const clipText = {
|
|
24
|
+
start: 'Starts before window',
|
|
25
|
+
end: 'Ends after window',
|
|
26
|
+
both: 'Runs past window',
|
|
27
|
+
};
|
|
28
|
+
const dateFormat = (locale) => new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC' });
|
|
29
|
+
const timeFormat = (locale, timeZone) => new Intl.DateTimeFormat(locale, {
|
|
30
|
+
day: 'numeric',
|
|
31
|
+
month: 'short',
|
|
32
|
+
year: 'numeric',
|
|
33
|
+
hour: 'numeric',
|
|
34
|
+
minute: '2-digit',
|
|
35
|
+
timeZone,
|
|
36
|
+
});
|
|
37
|
+
/** A mark's dates in words, like "1–9 Oct 2026". */
|
|
38
|
+
function datesText(mark, hasEnd, locale, timeZone) {
|
|
39
|
+
return plainSpaces(rawDatesText(mark, hasEnd, locale, timeZone));
|
|
40
|
+
}
|
|
41
|
+
function rawDatesText(mark, hasEnd, locale, timeZone) {
|
|
42
|
+
const format = mark.from.date ? dateFormat(locale) : timeFormat(locale, timeZone);
|
|
43
|
+
const start = format.format(mark.from.time);
|
|
44
|
+
if (mark.kind === 'reversed' && mark.to)
|
|
45
|
+
return `Starts ${start}, ends ${format.format(mark.to.time)}`;
|
|
46
|
+
if (!mark.to)
|
|
47
|
+
return hasEnd ? `${start}, no end` : start;
|
|
48
|
+
if (mark.to.time === mark.from.time)
|
|
49
|
+
return start;
|
|
50
|
+
return format.formatRange(mark.from.time, mark.to.time);
|
|
51
|
+
}
|
|
52
|
+
/** Saturdays and Sundays in a window of a month or less, as [from, to] times. */
|
|
53
|
+
function weekends(from, to) {
|
|
54
|
+
if (to - from > 31 * DAY)
|
|
55
|
+
return [];
|
|
56
|
+
const bands = [];
|
|
57
|
+
for (let day = from; day < to; day += DAY) {
|
|
58
|
+
const weekday = new Date(day).getUTCDay();
|
|
59
|
+
if (weekday === 6 || (weekday === 0 && day === from))
|
|
60
|
+
bands.push([day, Math.min(to, day + (weekday === 6 ? 2 : 1) * DAY)]);
|
|
61
|
+
}
|
|
62
|
+
return bands;
|
|
63
|
+
}
|
|
64
|
+
const weekLength = (from, to) => to - from <= 7 * DAY;
|
|
65
|
+
/** Tick times for a window: the middle of each day in a week, weekly in a month, twice a month in a quarter. */
|
|
66
|
+
function windowTicks(from, to) {
|
|
67
|
+
if (weekLength(from, to))
|
|
68
|
+
return Array.from({ length: Math.round((to - from) / DAY) }, (_, i) => from + (i + 0.5) * DAY);
|
|
69
|
+
const days = Math.round((to - from) / DAY);
|
|
70
|
+
const ticks = [];
|
|
71
|
+
for (let day = from; day <= to; day += DAY) {
|
|
72
|
+
const date = new Date(day).getUTCDate();
|
|
73
|
+
if (days <= 31 ? (date - 1) % 7 === 0 : date === 1 || date === 15)
|
|
74
|
+
ticks.push(day);
|
|
75
|
+
}
|
|
76
|
+
return ticks;
|
|
77
|
+
}
|
|
78
|
+
/** A week tick, like "Mon 12": weekday first in every locale so the days scan down one edge. */
|
|
79
|
+
function weekdayTick(value, locale) {
|
|
80
|
+
const parts = new Intl.DateTimeFormat(locale, { weekday: 'short', day: 'numeric', timeZone: 'UTC' }).formatToParts(value);
|
|
81
|
+
const part = (type) => parts.find(item => item.type === type)?.value ?? '';
|
|
82
|
+
return `${part('weekday')} ${part('day')}`;
|
|
83
|
+
}
|
|
84
|
+
const diamond = (cx, cy) => `M${cx} ${cy - 6}L${cx + 6} ${cy}L${cx} ${cy + 6}L${cx - 6} ${cy}Z`;
|
|
85
|
+
/** Bars for ranges, cut ends squared with a chevron; diamonds for points. */
|
|
86
|
+
function markShape(props) {
|
|
87
|
+
const mark = props.payload;
|
|
88
|
+
const { x = 0, y = 0, width = 0, height = 0 } = props;
|
|
89
|
+
const cy = y + height / 2;
|
|
90
|
+
if (mark.kind !== 'range')
|
|
91
|
+
return (_jsx("path", { className: "sprout-timeline-point", "data-kind": mark.kind, d: diamond(x, cy), fill: mark.kind === 'reversed' ? 'var(--sprout-surface)' : mark.fill, stroke: mark.kind === 'reversed' ? 'var(--sprout-warning)' : 'var(--sprout-surface)', strokeWidth: mark.kind === 'reversed' ? 2 : 1 }));
|
|
92
|
+
if (!mark.clip)
|
|
93
|
+
return _jsx(Rectangle, { ...props, fill: mark.fill });
|
|
94
|
+
const left = mark.clip !== 'end';
|
|
95
|
+
const right = mark.clip !== 'start';
|
|
96
|
+
const radius = [left ? 0 : 3, right ? 0 : 3, right ? 0 : 3, left ? 0 : 3];
|
|
97
|
+
const room = width >= 16;
|
|
98
|
+
return (_jsxs("g", { className: "sprout-timeline-clipped", "data-clip": mark.clip, children: [_jsx(Rectangle, { ...props, radius: radius, fill: mark.fill }), room && left ? (_jsx("path", { className: "sprout-timeline-chevron", d: `M${x + 8} ${cy - 4}L${x + 4} ${cy}L${x + 8} ${cy + 4}` })) : null, room && right ? (_jsx("path", { className: "sprout-timeline-chevron", d: `M${x + width - 8} ${cy - 4}L${x + width - 4} ${cy}L${x + width - 8} ${cy + 4}` })) : null] }));
|
|
99
|
+
}
|
|
100
|
+
function TodayLabel({ viewBox }) {
|
|
101
|
+
const x = viewBox?.x ?? 0;
|
|
102
|
+
const y = viewBox?.y ?? 0;
|
|
103
|
+
return (_jsxs("g", { className: "sprout-timeline-today", children: [_jsx("rect", { x: x - 21, y: y - AXIS + 2, width: 42, height: 16, rx: 8 }), _jsx("text", { x: x, y: y - AXIS + 10, textAnchor: "middle", dominantBaseline: "central", children: "Today" })] }));
|
|
104
|
+
}
|
|
105
|
+
const plural = (count) => (Number(String(count).replaceAll(',', '')) === 1 ? 'row' : 'rows');
|
|
106
|
+
const focusable = 'a[href], button, input, textarea, [tabindex]';
|
|
107
|
+
/**
|
|
108
|
+
* Gives only the active row's actions a place in the tab order, so the list
|
|
109
|
+
* is one tab stop and Tab from a row reaches its own actions.
|
|
110
|
+
*/
|
|
111
|
+
function syncActions(list, active) {
|
|
112
|
+
list.querySelectorAll('[data-slot="timeline-row"]').forEach((row, index) => {
|
|
113
|
+
for (const element of row.querySelectorAll(`[data-slot="timeline-row-actions"] :is(${focusable})`)) {
|
|
114
|
+
const saved = element.dataset.sproutTabindex;
|
|
115
|
+
if (index === active) {
|
|
116
|
+
if (saved === undefined)
|
|
117
|
+
continue;
|
|
118
|
+
if (saved)
|
|
119
|
+
element.setAttribute('tabindex', saved);
|
|
120
|
+
else
|
|
121
|
+
element.removeAttribute('tabindex');
|
|
122
|
+
delete element.dataset.sproutTabindex;
|
|
123
|
+
}
|
|
124
|
+
else if (saved === undefined) {
|
|
125
|
+
element.dataset.sproutTabindex = element.getAttribute('tabindex') ?? '';
|
|
126
|
+
element.tabIndex = -1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Rows as bars on a time axis, like a project plan. A row with only a start
|
|
133
|
+
* is a diamond. Give it a `window`, `renderLabel`, `renderActions`, or
|
|
134
|
+
* `onRowOpen` and it becomes a record timeline: a list of titles and dates
|
|
135
|
+
* in words beside the chart, in start order, with one tab stop and arrow
|
|
136
|
+
* keys. Without them it is a plain chart with names on the axis, one 32px
|
|
137
|
+
* row per task, colored by `seriesKey`.
|
|
138
|
+
*/
|
|
139
|
+
export function TimelineChart({ rows, category, start, end, seriesKey, series = [], locale, window, today, timeZone, rowKey, renderLabel, renderActions, onRowOpen, undated, notice, status = 'ready', error, onRetry, empty, title = 'Timeline', className, }) {
|
|
140
|
+
const record = Boolean(window || renderLabel || renderActions || onRowOpen);
|
|
141
|
+
const zoned = record || timeZone !== undefined;
|
|
142
|
+
const config = useMemo(() => seriesConfig(series), [series]);
|
|
143
|
+
const domain = useMemo(() => (window ? [dayTime(window.start), dayTime(window.end) + DAY] : undefined), [window]);
|
|
144
|
+
const marks = useMemo(() => {
|
|
145
|
+
const at = (when) => (when.date || !zoned ? when.time : wallClock(when.time, timeZone));
|
|
146
|
+
const list = [];
|
|
147
|
+
rows.forEach((row, index) => {
|
|
148
|
+
const from = read(row[start]);
|
|
149
|
+
if (!from)
|
|
150
|
+
return;
|
|
151
|
+
const to = end ? read(row[end]) : null;
|
|
152
|
+
const key = seriesKey ? String(row[seriesKey] ?? '') : '';
|
|
153
|
+
const a = at(from);
|
|
154
|
+
const kind = !to ? 'point' : at(to) < a ? 'reversed' : 'range';
|
|
155
|
+
let range;
|
|
156
|
+
if (kind === 'range')
|
|
157
|
+
range = [a, at(to) + (record && to.date ? DAY : 0)];
|
|
158
|
+
else {
|
|
159
|
+
const point = a + (record && from.date ? DAY / 2 : 0);
|
|
160
|
+
range = [point, point];
|
|
161
|
+
}
|
|
162
|
+
let clip;
|
|
163
|
+
if (domain) {
|
|
164
|
+
const [low, high] = domain;
|
|
165
|
+
if (kind === 'range' ? range[1] <= low || range[0] >= high : range[0] < low || range[0] >= high)
|
|
166
|
+
return;
|
|
167
|
+
const cutStart = range[0] < low;
|
|
168
|
+
const cutEnd = range[1] > high;
|
|
169
|
+
clip = cutStart && cutEnd ? 'both' : cutStart ? 'start' : cutEnd ? 'end' : undefined;
|
|
170
|
+
range = [Math.max(range[0], low), Math.min(range[1], high)];
|
|
171
|
+
}
|
|
172
|
+
list.push({
|
|
173
|
+
row,
|
|
174
|
+
id: rowKey ? String(row[rowKey] ?? index) : String(index),
|
|
175
|
+
name: String(row[category] ?? ''),
|
|
176
|
+
key,
|
|
177
|
+
fill: series.some(item => item.key === key) ? chartColor(key) : 'var(--sprout-chart-1)',
|
|
178
|
+
kind,
|
|
179
|
+
range,
|
|
180
|
+
clip,
|
|
181
|
+
startsAt: a,
|
|
182
|
+
from,
|
|
183
|
+
to,
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
if (record)
|
|
187
|
+
list.sort((x, y) => x.startsAt - y.startsAt);
|
|
188
|
+
return list;
|
|
189
|
+
}, [rows, category, start, end, seriesKey, series, rowKey, domain, record, zoned, timeZone]);
|
|
190
|
+
const date = (value) => dayLabel(value, locale);
|
|
191
|
+
const todayAt = today && isCalendarDate(today) ? dayTime(today) + DAY / 2 : undefined;
|
|
192
|
+
if (!record)
|
|
193
|
+
return (_jsx(ChartContainer, { "data-variant": "timeline", className: cn('sprout-chart-timeline', className), style: { height: `${marks.length * 2 + (series.length ? 5 : 3)}rem` }, config: config, children: _jsxs(BarChart, { data: marks, accessibilityLayer: true, title: title, layout: "vertical", barCategoryGap: "28%", children: [_jsx(CartesianGrid, { horizontal: false }), _jsx(XAxis, { type: "number", scale: "time", domain: domain ?? ['dataMin', 'dataMax'], tickFormatter: date, ...quietAxis, minTickGap: 24 }), _jsx(YAxis, { type: "category", dataKey: "name", width: "auto", ...quietAxis, interval: 0 }), _jsx(ChartTooltip, { cursor: tooltipCursor, content: props => (_jsx(ChartTooltipContent, { active: props.active, label: props.label, nameKey: "key", payload: props.payload?.map(item => {
|
|
194
|
+
const [from, to] = item.payload.range;
|
|
195
|
+
return {
|
|
196
|
+
...item,
|
|
197
|
+
name: item.payload.key,
|
|
198
|
+
value: from === to ? date(from) : `${date(from)} – ${date(to)}`,
|
|
199
|
+
};
|
|
200
|
+
}) })) }), series.length ? _jsx(ChartLegend, { content: configLegend(config) }) : null, todayAt === undefined ? null : (_jsx(ReferenceLine, { x: todayAt, stroke: "var(--sprout-chart-1)", strokeDasharray: "3 3" })), _jsx(Bar, { dataKey: "range", radius: 3, shape: markShape, isAnimationActive: false })] }) }));
|
|
201
|
+
return (_jsx(RecordTimeline, { marks: marks, config: config, series: series, hasEnd: Boolean(end), domain: domain, window: window, todayAt: todayAt, locale: locale, timeZone: timeZone, renderLabel: renderLabel, renderActions: renderActions, onRowOpen: onRowOpen, undated: undated, notice: notice, status: status, error: error, onRetry: onRetry, empty: empty, title: title, className: className }));
|
|
202
|
+
}
|
|
203
|
+
function RecordTimeline({ marks, config, series, hasEnd, domain, window, todayAt, locale, timeZone, renderLabel, renderActions, onRowOpen, undated, notice, status, error, onRetry, empty, title, className, }) {
|
|
204
|
+
const id = useId();
|
|
205
|
+
const listRef = useRef(null);
|
|
206
|
+
const [activeId, setActiveId] = useState(null);
|
|
207
|
+
const [hot, setHot] = useState(null);
|
|
208
|
+
const found = marks.findIndex(mark => mark.id === activeId);
|
|
209
|
+
const active = found === -1 ? 0 : found;
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
if (listRef.current)
|
|
212
|
+
syncActions(listRef.current, active);
|
|
213
|
+
});
|
|
214
|
+
const date = (value) => dayLabel(value, locale);
|
|
215
|
+
const dates = marks.map(mark => datesText(mark, hasEnd, locale, timeZone));
|
|
216
|
+
const loading = status === 'loading';
|
|
217
|
+
const style = { '--sprout-timeline-count': Math.max(marks.length, 1) };
|
|
218
|
+
const focusRow = (index) => {
|
|
219
|
+
const next = marks[index];
|
|
220
|
+
if (!next)
|
|
221
|
+
return;
|
|
222
|
+
setActiveId(next.id);
|
|
223
|
+
listRef.current?.querySelectorAll('[data-slot="timeline-row"]')[index]?.focus();
|
|
224
|
+
};
|
|
225
|
+
const onKeyDown = (event, index) => {
|
|
226
|
+
if (event.target !== event.currentTarget)
|
|
227
|
+
return;
|
|
228
|
+
const moves = {
|
|
229
|
+
ArrowDown: index + 1,
|
|
230
|
+
ArrowUp: index - 1,
|
|
231
|
+
Home: 0,
|
|
232
|
+
End: marks.length - 1,
|
|
233
|
+
};
|
|
234
|
+
const next = moves[event.key];
|
|
235
|
+
if (next !== undefined) {
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
focusRow(Math.min(Math.max(next, 0), marks.length - 1));
|
|
238
|
+
}
|
|
239
|
+
else if (event.key === 'Enter' && onRowOpen) {
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
onRowOpen(marks[index].row);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const onRowClick = (event, index) => {
|
|
245
|
+
if (event.target.closest('[data-slot="timeline-row-actions"]'))
|
|
246
|
+
return;
|
|
247
|
+
onRowOpen?.(marks[index].row);
|
|
248
|
+
};
|
|
249
|
+
const windowText = () => {
|
|
250
|
+
if (!window)
|
|
251
|
+
return 'No rows to show.';
|
|
252
|
+
const format = dateFormat(locale);
|
|
253
|
+
return `Nothing between ${format.format(dayTime(window.start))} and ${format.format(dayTime(window.end))}.`;
|
|
254
|
+
};
|
|
255
|
+
let body;
|
|
256
|
+
if (status === 'error')
|
|
257
|
+
body = (_jsx(StatePanel, { kind: "error", heading: "h3", title: "Couldn't load the timeline", description: error ?? 'Something went wrong while reading these rows.', className: "sprout-timeline-state", children: onRetry ? (_jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: "Try again" })) : null }));
|
|
258
|
+
else if (!marks.length && loading)
|
|
259
|
+
body = (_jsxs("div", { role: "status", "aria-label": "Loading timeline", className: "sprout-timeline-grid", "data-skeleton": "", children: [_jsx("div", { className: "sprout-timeline-labels", "aria-hidden": "true", children: [0, 1, 2, 3, 4].map(index => (_jsx("div", { className: "sprout-timeline-row", children: _jsxs("div", { className: "sprout-timeline-label", children: [_jsx(Skeleton, { style: { width: `${70 - index * 7}%` } }), _jsx(Skeleton, { style: { width: '45%', height: 10 } })] }) }, index))) }), _jsx("div", { className: "sprout-timeline-plot", "aria-hidden": "true", children: [0, 1, 2, 3, 4].map(index => (_jsx("div", { className: "sprout-timeline-skeleton-band", children: _jsx(Skeleton, { style: { marginLeft: `${(index * 17) % 60}%`, width: `${20 + ((index * 23) % 30)}%` } }) }, index))) })] }));
|
|
260
|
+
else if (!marks.length)
|
|
261
|
+
body = empty ?? (_jsx(StatePanel, { kind: "empty", heading: "h3", title: "Nothing in this window", description: windowText(), className: "sprout-timeline-state" }));
|
|
262
|
+
else
|
|
263
|
+
body = (_jsxs("div", { className: "sprout-timeline-grid", onMouseLeave: () => setHot(null), children: [_jsx("ol", { ref: listRef, "aria-label": title, className: "sprout-timeline-labels", onBlur: event => {
|
|
264
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
265
|
+
setHot(null);
|
|
266
|
+
}, children: marks.map((mark, index) => (_jsxs("li", { "data-slot": "timeline-row", "data-active": index === active || undefined, "data-hot": index === hot || undefined, "data-openable": onRowOpen ? '' : undefined, tabIndex: index === active ? 0 : -1, "aria-labelledby": `${id}-${index}`, className: "sprout-timeline-row", onKeyDown: event => onKeyDown(event, index), onClick: event => onRowClick(event, index), onFocus: () => {
|
|
267
|
+
setActiveId(mark.id);
|
|
268
|
+
setHot(index);
|
|
269
|
+
}, onMouseEnter: () => setHot(index), children: [_jsxs("div", { className: "sprout-timeline-label", id: `${id}-${index}`, children: [_jsxs("span", { className: "sprout-timeline-label-head", children: [_jsx("span", { className: "sprout-timeline-label-title", children: renderLabel ? renderLabel(mark.row) : mark.name }), mark.kind === 'reversed' ? (_jsx(Badge, { variant: "warning", size: "chip", className: "sprout-timeline-label-marker", children: "End before start" })) : null] }), _jsxs("span", { className: "sprout-timeline-label-dates", children: [mark.kind === 'reversed' ? (_jsx("span", { className: "sprout-timeline-label-warning", children: "End before start" })) : null, _jsx("span", { className: "sprout-timeline-label-text", children: dates[index] }), mark.clip ? _jsx("span", { className: "sprout-timeline-label-clip", children: clipText[mark.clip] }) : null] })] }), renderActions ? (_jsx("div", { "data-slot": "timeline-row-actions", className: "sprout-timeline-row-actions", children: renderActions(mark.row) })) : null] }, mark.id))) }), _jsxs("div", { className: "sprout-timeline-plot", "aria-hidden": "true", children: [hot === null ? null : (_jsx("div", { className: "sprout-timeline-band", style: { '--sprout-timeline-index': hot } })), _jsx(ChartContainer, { "data-variant": "timeline", className: "sprout-chart-timeline sprout-timeline-chart", config: config, initialDimension: { width: 640, height: AXIS + marks.length * 44 }, children: _jsxs(BarChart, { data: marks, layout: "vertical", margin: { top: 0, right: 16, bottom: 0, left: 16 }, barSize: 16, accessibilityLayer: false, onMouseMove: state => {
|
|
270
|
+
const index = Number(state.activeTooltipIndex);
|
|
271
|
+
setHot(Number.isInteger(index) && state.activeTooltipIndex != null ? index : null);
|
|
272
|
+
}, children: [_jsx(CartesianGrid, { horizontal: false, vertical: !(domain && weekLength(...domain)) }), domain
|
|
273
|
+
? weekends(...domain).map(([x1, x2]) => (_jsx(ReferenceArea, { x1: x1, x2: x2, ifOverflow: "hidden", className: "sprout-timeline-weekend", fill: "var(--sprout-hover)", fillOpacity: 0.4, stroke: "none" }, x1)))
|
|
274
|
+
: null, _jsx(XAxis, { type: "number", scale: "time", orientation: "top", height: AXIS, domain: domain ?? ['dataMin', 'dataMax'], allowDataOverflow: true, ticks: domain ? windowTicks(...domain) : undefined, tickFormatter: domain && weekLength(...domain) ? (value) => weekdayTick(value, locale) : date, interval: "equidistantPreserveStart", ...quietAxis, tickMargin: 6, minTickGap: 32 }), _jsx(YAxis, { type: "category", dataKey: "id", hide: true }), _jsx(ChartTooltip, { cursor: false, content: props => (_jsx(ChartTooltipContent, { active: props.active, label: props.payload?.[0]?.payload?.name, nameKey: "key", hideIndicator: !series.length, payload: props.payload?.map(item => {
|
|
275
|
+
const mark = item.payload;
|
|
276
|
+
return {
|
|
277
|
+
...item,
|
|
278
|
+
name: mark.key,
|
|
279
|
+
value: dates[marks.indexOf(mark)] ?? '',
|
|
280
|
+
};
|
|
281
|
+
}) })) }), todayAt === undefined ? null : (_jsx(ReferenceLine, { x: todayAt, stroke: "var(--sprout-chart-1)", strokeWidth: 1.5, ifOverflow: "discard", label: _jsx(TodayLabel, {}) })), _jsx(Bar, { dataKey: "range", radius: 3, shape: markShape, isAnimationActive: false, onClick: (_, index) => {
|
|
282
|
+
const mark = marks[index];
|
|
283
|
+
if (mark)
|
|
284
|
+
onRowOpen?.(mark.row);
|
|
285
|
+
} })] }) })] })] }));
|
|
286
|
+
return (_jsxs("div", { "data-slot": "timeline", "data-status": status, "aria-busy": loading || undefined, className: cn('sprout-timeline', className), style: style, children: [notice ? _jsx("div", { className: "sprout-timeline-notice", children: notice }) : null, body, series.length && marks.length && status !== 'error' ? (_jsx("div", { "data-slot": "chart-legend", className: "sprout-chart-legend sprout-timeline-legend", children: series.map(item => (_jsxs("div", { className: "sprout-chart-legend-item", children: [_jsx("span", { "aria-hidden": "true", className: "sprout-chart-legend-swatch", style: { backgroundColor: item.color } }), item.label] }, item.key))) })) : null, undated && status !== 'error' ? (_jsxs("div", { "data-slot": "timeline-undated", className: "sprout-timeline-undated", children: [_jsx("span", { children: `${undated.count} ${plural(undated.count)} without ${undated.field}` }), undated.action] })) : null] }));
|
|
287
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { type ClassValue } from
|
|
1
|
+
import { type ClassValue } from 'clsx';
|
|
2
2
|
export declare function cn(...values: ClassValue[]): string;
|
|
3
3
|
export declare function stateClassName<State>(base: string, className: string | ((state: State) => string | undefined) | undefined): string | ((state: State) => string);
|
package/dist/internal/classes.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { clsx } from
|
|
2
|
-
import { extendTailwindMerge } from
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { extendTailwindMerge } from 'tailwind-merge';
|
|
3
3
|
const merge = extendTailwindMerge({
|
|
4
4
|
extend: {
|
|
5
5
|
theme: {
|
|
6
|
-
text: [
|
|
7
|
-
font: [
|
|
8
|
-
radius: [
|
|
9
|
-
shadow: [
|
|
6
|
+
text: ['sprout-xs', 'sprout-sm', 'sprout-base'],
|
|
7
|
+
font: ['sprout'],
|
|
8
|
+
radius: ['sprout-control', 'sprout-panel'],
|
|
9
|
+
shadow: ['sprout-control', 'sprout-panel', 'sprout-overlay'],
|
|
10
10
|
},
|
|
11
11
|
},
|
|
12
12
|
});
|
|
@@ -14,7 +14,5 @@ export function cn(...values) {
|
|
|
14
14
|
return merge(clsx(values));
|
|
15
15
|
}
|
|
16
16
|
export function stateClassName(base, className) {
|
|
17
|
-
return typeof className ===
|
|
18
|
-
? (state) => cn(base, className(state))
|
|
19
|
-
: cn(base, className);
|
|
17
|
+
return typeof className === 'function' ? (state) => cn(base, className(state)) : cn(base, className);
|
|
20
18
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ThemedToken } from
|
|
2
|
-
import type { CodeLanguage } from
|
|
1
|
+
import type { ThemedToken } from 'shiki/core';
|
|
2
|
+
import type { CodeLanguage } from './languages.js';
|
|
3
3
|
export declare function useCodeTokens(code: string, language: CodeLanguage | null): ThemedToken[][] | null;
|
|
4
4
|
export declare function tokenClass(token: ThemedToken): string | undefined;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from
|
|
2
|
-
import { codeTokens, embeddedLanguages, isLanguageReady, loadCodeLanguage, maxHighlightedLength
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { codeTokens, embeddedLanguages, isLanguageReady, loadCodeLanguage, maxHighlightedLength } from './syntax.js';
|
|
3
3
|
export function useCodeTokens(code, language) {
|
|
4
4
|
const [loaded, setLoaded] = useState(0);
|
|
5
5
|
const highlightable = language !== null && code.length <= maxHighlightedLength;
|
|
6
|
-
const needed = useMemo(() => highlightable ? [language, ...embeddedLanguages(code, language)] : [], [code, language, highlightable]);
|
|
7
|
-
const missing = needed.filter(
|
|
8
|
-
const key = missing.join(
|
|
6
|
+
const needed = useMemo(() => (highlightable ? [language, ...embeddedLanguages(code, language)] : []), [code, language, highlightable]);
|
|
7
|
+
const missing = needed.filter(name => !isLanguageReady(name));
|
|
8
|
+
const key = missing.join(' ');
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
if (!key)
|
|
11
11
|
return;
|
|
12
12
|
let live = true;
|
|
13
|
-
void Promise.all(key.split(
|
|
13
|
+
void Promise.all(key.split(' ').map(name => loadCodeLanguage(name))).then(() => live && setLoaded(value => value + 1), () => undefined);
|
|
14
14
|
return () => {
|
|
15
15
|
live = false;
|
|
16
16
|
};
|
|
@@ -20,15 +20,15 @@ export function useCodeTokens(code, language) {
|
|
|
20
20
|
[code, language, highlightable, loaded, key]);
|
|
21
21
|
}
|
|
22
22
|
const fontStyleClasses = [
|
|
23
|
-
[1,
|
|
24
|
-
[2,
|
|
25
|
-
[4,
|
|
23
|
+
[1, 'sprout-syntax-italic'],
|
|
24
|
+
[2, 'sprout-syntax-bold'],
|
|
25
|
+
[4, 'sprout-syntax-underline'],
|
|
26
26
|
];
|
|
27
27
|
export function tokenClass(token) {
|
|
28
|
-
const role = /--sprout-syntax-(?:token-)?([a-z-]+)/.exec(token.color ??
|
|
29
|
-
const classes = role && role !==
|
|
28
|
+
const role = /--sprout-syntax-(?:token-)?([a-z-]+)/.exec(token.color ?? '')?.[1];
|
|
29
|
+
const classes = role && role !== 'foreground' ? [`sprout-syntax-${role}`] : [];
|
|
30
30
|
for (const [flag, name] of fontStyleClasses)
|
|
31
31
|
if ((token.fontStyle ?? 0) & flag)
|
|
32
32
|
classes.push(name);
|
|
33
|
-
return classes.length ? classes.join(
|
|
33
|
+
return classes.length ? classes.join(' ') : undefined;
|
|
34
34
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useState } from
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
2
|
/**
|
|
3
3
|
* Base UI roots are spans, which `<fieldset disabled>` does not disable. Track the
|
|
4
4
|
* enclosing fieldsets so the control can pass a real `disabled` to Base UI.
|
|
@@ -6,29 +6,29 @@ import { useCallback, useState } from "react";
|
|
|
6
6
|
export function useFieldsetDisabled(ref) {
|
|
7
7
|
const [disabled, setDisabled] = useState(false);
|
|
8
8
|
const mergedRef = useCallback((node) => {
|
|
9
|
-
if (typeof ref ===
|
|
9
|
+
if (typeof ref === 'function')
|
|
10
10
|
ref(node);
|
|
11
11
|
else if (ref)
|
|
12
12
|
ref.current = node;
|
|
13
13
|
if (!node)
|
|
14
14
|
return;
|
|
15
15
|
const fieldsets = [];
|
|
16
|
-
for (let fieldset = node.closest(
|
|
16
|
+
for (let fieldset = node.closest('fieldset'); fieldset; fieldset = fieldset.parentElement?.closest('fieldset') ?? null) {
|
|
17
17
|
fieldsets.push(fieldset);
|
|
18
18
|
}
|
|
19
19
|
if (fieldsets.length === 0)
|
|
20
20
|
return;
|
|
21
|
-
const sync = () => setDisabled(node.closest(
|
|
21
|
+
const sync = () => setDisabled(node.closest('fieldset:disabled') !== null);
|
|
22
22
|
sync();
|
|
23
23
|
const observer = new MutationObserver(sync);
|
|
24
24
|
for (const fieldset of fieldsets)
|
|
25
25
|
observer.observe(fieldset, {
|
|
26
26
|
attributes: true,
|
|
27
|
-
attributeFilter: [
|
|
27
|
+
attributeFilter: ['disabled'],
|
|
28
28
|
});
|
|
29
29
|
return () => {
|
|
30
30
|
observer.disconnect();
|
|
31
|
-
if (typeof ref ===
|
|
31
|
+
if (typeof ref === 'function')
|
|
32
32
|
ref(null);
|
|
33
33
|
else if (ref)
|
|
34
34
|
ref.current = null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type FileKind =
|
|
1
|
+
export type FileKind = 'image' | 'pdf' | 'json' | 'text' | 'markdown' | 'code' | 'sheet' | 'document' | 'slides' | 'google' | 'unsupported';
|
|
2
2
|
export declare const defaultFileAccept: string;
|
|
3
3
|
export declare function fileKind(name: string, mediaType: string): FileKind;
|
|
4
4
|
export declare function formatFileSize(bytes: number): string;
|
|
@@ -1,62 +1,54 @@
|
|
|
1
|
-
import { languageForFilename } from
|
|
2
|
-
export const defaultFileAccept =
|
|
3
|
-
|
|
1
|
+
import { languageForFilename } from './syntax.js';
|
|
2
|
+
export const defaultFileAccept = '.docx,.xlsx,.xls,.csv,.tsv,.pptx,.ppt,.pdf,.json,.txt,.md,.markdown,.gdoc,.gsheet,.gslides,image/*,' +
|
|
3
|
+
'.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.kt,.swift,.c,.h,.cpp,.cs,.php,.sh,.sql,.yaml,.yml,.toml,.xml,.html,.css,.scss,.log';
|
|
4
4
|
const imageExtensions = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
'png',
|
|
6
|
+
'jpg',
|
|
7
|
+
'jpeg',
|
|
8
|
+
'gif',
|
|
9
|
+
'webp',
|
|
10
|
+
'avif',
|
|
11
|
+
'svg',
|
|
12
|
+
'bmp',
|
|
13
|
+
'ico',
|
|
14
|
+
'heic',
|
|
15
|
+
'heif',
|
|
16
|
+
'tif',
|
|
17
|
+
'tiff',
|
|
18
18
|
];
|
|
19
19
|
const sheetTypes = [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
'application/vnd.ms-excel',
|
|
21
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
22
|
+
'text/csv',
|
|
23
23
|
];
|
|
24
24
|
export function fileKind(name, mediaType) {
|
|
25
|
-
const extension = name.toLowerCase().split(
|
|
26
|
-
if (mediaType.startsWith(
|
|
27
|
-
return
|
|
28
|
-
if (mediaType.startsWith(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
if (extension ===
|
|
41
|
-
|
|
42
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document")
|
|
43
|
-
return "document";
|
|
44
|
-
if (extension === "pptx" ||
|
|
45
|
-
mediaType ===
|
|
46
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation")
|
|
47
|
-
return "slides";
|
|
25
|
+
const extension = name.toLowerCase().split('.').at(-1) ?? '';
|
|
26
|
+
if (mediaType.startsWith('image/') || imageExtensions.includes(extension))
|
|
27
|
+
return 'image';
|
|
28
|
+
if (mediaType.startsWith('application/vnd.google-apps.') || ['gdoc', 'gsheet', 'gslides'].includes(extension))
|
|
29
|
+
return 'google';
|
|
30
|
+
if (extension === 'pdf' || mediaType === 'application/pdf')
|
|
31
|
+
return 'pdf';
|
|
32
|
+
if (extension === 'json' || mediaType === 'application/json')
|
|
33
|
+
return 'json';
|
|
34
|
+
if (['md', 'markdown'].includes(extension) || mediaType === 'text/markdown')
|
|
35
|
+
return 'markdown';
|
|
36
|
+
if (['xls', 'xlsx', 'csv', 'tsv'].includes(extension) || sheetTypes.includes(mediaType))
|
|
37
|
+
return 'sheet';
|
|
38
|
+
if (extension === 'docx' || mediaType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
|
|
39
|
+
return 'document';
|
|
40
|
+
if (extension === 'pptx' || mediaType === 'application/vnd.openxmlformats-officedocument.presentationml.presentation')
|
|
41
|
+
return 'slides';
|
|
48
42
|
if (languageForFilename(name))
|
|
49
|
-
return
|
|
50
|
-
if (mediaType.startsWith(
|
|
51
|
-
return
|
|
52
|
-
return
|
|
43
|
+
return 'code';
|
|
44
|
+
if (mediaType.startsWith('text/') || extension === 'txt')
|
|
45
|
+
return 'text';
|
|
46
|
+
return 'unsupported';
|
|
53
47
|
}
|
|
54
48
|
export function formatFileSize(bytes) {
|
|
55
49
|
if (bytes === 0)
|
|
56
|
-
return
|
|
50
|
+
return '0 bytes';
|
|
57
51
|
if (bytes >= 1024 ** 3)
|
|
58
|
-
return `${(bytes / 1024 ** 3).toFixed(1).replace(/\.0$/,
|
|
59
|
-
return bytes < 1024 * 1024
|
|
60
|
-
? `${Math.max(1, Math.round(bytes / 1024))} KB`
|
|
61
|
-
: `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
52
|
+
return `${(bytes / 1024 ** 3).toFixed(1).replace(/\.0$/, '')} GB`;
|
|
53
|
+
return bytes < 1024 * 1024 ? `${Math.max(1, Math.round(bytes / 1024))} KB` : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
62
54
|
}
|
package/dist/internal/flow.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type FitViewOptions, type Node } from
|
|
2
|
-
import { type KeyboardEvent } from
|
|
1
|
+
import { type FitViewOptions, type Node } from '@xyflow/react';
|
|
2
|
+
import { type KeyboardEvent } from 'react';
|
|
3
3
|
export declare const flowMinZoom = 0.2;
|
|
4
4
|
export declare const flowMaxZoom = 1.5;
|
|
5
5
|
export declare function useViewportMobile(): boolean;
|
|
@@ -8,12 +8,12 @@ export declare const motion: (ms: number) => number;
|
|
|
8
8
|
* Refits whenever the pane resizes or the nodes change. The canvas panel
|
|
9
9
|
* animates open, so the first fit sees a narrower pane than the final one.
|
|
10
10
|
*/
|
|
11
|
-
export declare function FitOnResize({ nodes, fit
|
|
11
|
+
export declare function FitOnResize({ nodes, fit }: {
|
|
12
12
|
nodes: readonly Node[];
|
|
13
13
|
fit: FitViewOptions;
|
|
14
14
|
}): null;
|
|
15
15
|
export declare function ZoomControls({ position, fit, className, slot, }: {
|
|
16
|
-
position:
|
|
16
|
+
position: 'top-right' | 'bottom-right';
|
|
17
17
|
fit: FitViewOptions;
|
|
18
18
|
className: string;
|
|
19
19
|
slot: string;
|