@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
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Background, getSmoothStepPath, Handle, Panel, Position, ReactFlow, ReactFlowProvider, useInternalNode, useNodesState, } from
|
|
4
|
-
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState, } from
|
|
5
|
-
import { Fingerprint } from
|
|
6
|
-
import { Key } from
|
|
7
|
-
import { Layers } from
|
|
8
|
-
import { Link } from
|
|
9
|
-
import { cn } from
|
|
10
|
-
import { FitOnResize, flowMaxZoom, flowMinZoom, useKeyboardView, useViewportMobile, ZoomControls, } from
|
|
11
|
-
import { Button } from
|
|
12
|
-
export const sqlViewerLayoutNames = [
|
|
13
|
-
const moreId =
|
|
3
|
+
import { Background, getSmoothStepPath, Handle, Panel, Position, ReactFlow, ReactFlowProvider, useInternalNode, useNodesState, } from '@xyflow/react';
|
|
4
|
+
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState, } from 'react';
|
|
5
|
+
import { Fingerprint } from 'reicon-react/icons/Fingerprint';
|
|
6
|
+
import { Key } from 'reicon-react/icons/Key';
|
|
7
|
+
import { Layers } from 'reicon-react/icons/Layers';
|
|
8
|
+
import { Link } from 'reicon-react/icons/Link';
|
|
9
|
+
import { cn } from '../internal/classes.js';
|
|
10
|
+
import { FitOnResize, flowMaxZoom, flowMinZoom, useKeyboardView, useViewportMobile, ZoomControls, } from '../internal/flow.js';
|
|
11
|
+
import { Button } from './button.js';
|
|
12
|
+
export const sqlViewerLayoutNames = ['auto', 'horizontal', 'vertical'];
|
|
13
|
+
const moreId = '~more';
|
|
14
14
|
const groupId = (key) => `~group:${key}`;
|
|
15
15
|
// Card geometry. The CSS uses the same numbers so layout never waits for a measure.
|
|
16
16
|
const cardWidth = 272;
|
|
@@ -19,7 +19,6 @@ const headerHeight = 40;
|
|
|
19
19
|
const rowHeight = 28;
|
|
20
20
|
const keysChrome = 36;
|
|
21
21
|
const keyHeight = 26;
|
|
22
|
-
const moreHeight = 132;
|
|
23
22
|
const colGap = 104;
|
|
24
23
|
const rowGap = 32;
|
|
25
24
|
const groupPad = 20;
|
|
@@ -28,38 +27,33 @@ const groupGap = 56;
|
|
|
28
27
|
const wrapWidth = 1400;
|
|
29
28
|
const tones = 5;
|
|
30
29
|
const fitAll = { padding: 0.12, maxZoom: 1 };
|
|
31
|
-
const compositeKeys = (table) => (table.uniqueKeys ?? []).filter(
|
|
30
|
+
const compositeKeys = (table) => (table.uniqueKeys ?? []).filter(key => key.fieldIds.length > 1);
|
|
32
31
|
const cardHeight = (table) => {
|
|
33
32
|
const keys = compositeKeys(table).length;
|
|
34
|
-
return
|
|
35
|
-
headerHeight +
|
|
36
|
-
(table.fields.length + 1) * rowHeight +
|
|
37
|
-
(keys ? keysChrome + keys * keyHeight : 0));
|
|
33
|
+
return cardChrome + headerHeight + (table.fields.length + 1) * rowHeight + (keys ? keysChrome + keys * keyHeight : 0);
|
|
38
34
|
};
|
|
39
35
|
function columnType(field) {
|
|
40
36
|
switch (field.kind) {
|
|
41
|
-
case
|
|
37
|
+
case 'text':
|
|
42
38
|
return `varchar(${field.maxLength})`;
|
|
43
|
-
case
|
|
39
|
+
case 'numeric':
|
|
44
40
|
return `numeric(${field.precision},${field.scale})`;
|
|
45
|
-
case
|
|
46
|
-
return
|
|
47
|
-
case
|
|
48
|
-
return
|
|
49
|
-
case
|
|
50
|
-
return
|
|
51
|
-
case
|
|
52
|
-
return field.cardinality ===
|
|
53
|
-
case
|
|
54
|
-
return field.cardinality ===
|
|
41
|
+
case 'timestamp':
|
|
42
|
+
return 'timestamptz';
|
|
43
|
+
case 'single-select':
|
|
44
|
+
return 'enum';
|
|
45
|
+
case 'multi-select':
|
|
46
|
+
return 'enum[]';
|
|
47
|
+
case 'relationship':
|
|
48
|
+
return field.cardinality === 'one' ? 'uuid' : 'uuid[]';
|
|
49
|
+
case 'file':
|
|
50
|
+
return field.cardinality === 'one' ? 'file' : 'file[]';
|
|
55
51
|
default:
|
|
56
52
|
return field.kind;
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
55
|
function joinText(from, field, to, many) {
|
|
60
|
-
return many
|
|
61
|
-
? `${to}.id = ANY(${from}.${field})`
|
|
62
|
-
: `${from}.${field} = ${to}.id`;
|
|
56
|
+
return many ? `${to}.id = ANY(${from}.${field})` : `${from}.${field} = ${to}.id`;
|
|
63
57
|
}
|
|
64
58
|
/** Places tables in team groups. Linked tables sit one column right of the tables that link to them. */
|
|
65
59
|
function arrange(visible, hidden, groups, direction) {
|
|
@@ -67,9 +61,9 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
67
61
|
for (const table of visible) {
|
|
68
62
|
const team = table.owningTeamId;
|
|
69
63
|
const key = team !== undefined && groups?.[team] ? team : null;
|
|
70
|
-
let block = blocks.find(
|
|
64
|
+
let block = blocks.find(candidate => candidate.key === key);
|
|
71
65
|
if (!block) {
|
|
72
|
-
block = { key, label: key ? groups[key] :
|
|
66
|
+
block = { key, label: key ? groups[key] : '', tables: [] };
|
|
73
67
|
blocks.push(block);
|
|
74
68
|
}
|
|
75
69
|
block.tables.push(table);
|
|
@@ -82,21 +76,18 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
82
76
|
let bottom = 0;
|
|
83
77
|
let tone = 0;
|
|
84
78
|
for (const block of blocks) {
|
|
85
|
-
const ids = new Set(block.tables.map(
|
|
86
|
-
const depth = new Map(block.tables.map(
|
|
87
|
-
if (direction ===
|
|
79
|
+
const ids = new Set(block.tables.map(table => table.id));
|
|
80
|
+
const depth = new Map(block.tables.map(table => [table.id, 0]));
|
|
81
|
+
if (direction === 'horizontal') {
|
|
88
82
|
for (let pass = 0; pass < block.tables.length; pass++) {
|
|
89
83
|
let moved = false;
|
|
90
84
|
for (const table of block.tables)
|
|
91
85
|
for (const field of table.fields) {
|
|
92
|
-
if (field.kind !==
|
|
86
|
+
if (field.kind !== 'relationship')
|
|
93
87
|
continue;
|
|
94
88
|
const target = field.targetTableId;
|
|
95
89
|
const next = depth.get(table.id) + 1;
|
|
96
|
-
if (target !== table.id &&
|
|
97
|
-
ids.has(target) &&
|
|
98
|
-
next > depth.get(target) &&
|
|
99
|
-
next < block.tables.length) {
|
|
90
|
+
if (target !== table.id && ids.has(target) && next > depth.get(target) && next < block.tables.length) {
|
|
100
91
|
depth.set(target, next);
|
|
101
92
|
moved = true;
|
|
102
93
|
}
|
|
@@ -106,20 +97,18 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
106
97
|
}
|
|
107
98
|
}
|
|
108
99
|
const columnBottoms = new Map();
|
|
109
|
-
const placed = block.tables.map(
|
|
100
|
+
const placed = block.tables.map(table => {
|
|
110
101
|
const column = depth.get(table.id);
|
|
111
102
|
const y = columnBottoms.get(column) ?? 0;
|
|
112
103
|
columnBottoms.set(column, y + cardHeight(table) + rowGap);
|
|
113
104
|
return { table, x: column * (cardWidth + colGap), y };
|
|
114
105
|
});
|
|
115
|
-
const innerWidth = Math.max(...placed.map(
|
|
106
|
+
const innerWidth = Math.max(...placed.map(item => item.x)) + cardWidth;
|
|
116
107
|
const innerHeight = Math.max(...columnBottoms.values()) - rowGap;
|
|
117
108
|
const framed = block.key !== null;
|
|
118
109
|
const width = framed ? innerWidth + groupPad * 2 : innerWidth;
|
|
119
110
|
const height = framed ? innerHeight + groupHeader + groupPad : innerHeight;
|
|
120
|
-
if (direction ===
|
|
121
|
-
cursorX > 0 &&
|
|
122
|
-
cursorX + width > wrapWidth) {
|
|
111
|
+
if (direction === 'horizontal' && cursorX > 0 && cursorX + width > wrapWidth) {
|
|
123
112
|
cursorX = 0;
|
|
124
113
|
cursorY += lineHeight + groupGap;
|
|
125
114
|
lineHeight = 0;
|
|
@@ -129,7 +118,7 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
129
118
|
if (framed)
|
|
130
119
|
nodes.push({
|
|
131
120
|
id: parent,
|
|
132
|
-
type:
|
|
121
|
+
type: 'group',
|
|
133
122
|
position: { x: cursorX, y: cursorY },
|
|
134
123
|
width,
|
|
135
124
|
height,
|
|
@@ -138,26 +127,26 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
138
127
|
tone: blockTone,
|
|
139
128
|
count: block.tables.length,
|
|
140
129
|
},
|
|
141
|
-
dragHandle:
|
|
130
|
+
dragHandle: '.sprout-sql-group-label',
|
|
142
131
|
focusable: false,
|
|
143
132
|
selectable: false,
|
|
144
133
|
});
|
|
145
134
|
for (const item of placed)
|
|
146
135
|
nodes.push({
|
|
147
136
|
id: item.table.id,
|
|
148
|
-
type:
|
|
137
|
+
type: 'table',
|
|
149
138
|
position: framed
|
|
150
139
|
? { x: groupPad + item.x, y: groupHeader + item.y }
|
|
151
140
|
: { x: cursorX + item.x, y: cursorY + item.y },
|
|
152
141
|
...(parent ? { parentId: parent, expandParent: true } : {}),
|
|
153
142
|
data: { table: item.table, tone: blockTone },
|
|
154
|
-
ariaRole:
|
|
143
|
+
ariaRole: 'group',
|
|
155
144
|
ariaLabel: `${item.table.label} table, ${item.table.fields.length + 1} columns`,
|
|
156
|
-
domAttributes: {
|
|
145
|
+
domAttributes: { 'aria-roledescription': 'table card' },
|
|
157
146
|
});
|
|
158
147
|
right = Math.max(right, cursorX + width);
|
|
159
148
|
bottom = Math.max(bottom, cursorY + height);
|
|
160
|
-
if (direction ===
|
|
149
|
+
if (direction === 'horizontal') {
|
|
161
150
|
cursorX += width + groupGap;
|
|
162
151
|
lineHeight = Math.max(lineHeight, height);
|
|
163
152
|
}
|
|
@@ -168,42 +157,40 @@ function arrange(visible, hidden, groups, direction) {
|
|
|
168
157
|
if (hidden.length)
|
|
169
158
|
nodes.push({
|
|
170
159
|
id: moreId,
|
|
171
|
-
type:
|
|
172
|
-
position: direction ===
|
|
173
|
-
? { x: right + colGap, y: 0 }
|
|
174
|
-
: { x: 0, y: bottom + groupGap },
|
|
160
|
+
type: 'more',
|
|
161
|
+
position: direction === 'horizontal' ? { x: right + colGap, y: 0 } : { x: 0, y: bottom + groupGap },
|
|
175
162
|
data: { tables: [...hidden] },
|
|
176
163
|
focusable: false,
|
|
177
164
|
});
|
|
178
165
|
return nodes;
|
|
179
166
|
}
|
|
180
167
|
function joins(visible, hidden) {
|
|
181
|
-
const shown = new Set(visible.map(
|
|
182
|
-
const collapsed = new Set(hidden.map(
|
|
183
|
-
const labels = new Map([...visible, ...hidden].map(
|
|
184
|
-
const node = (id) => shown.has(id) ? id : collapsed.has(id) ? moreId : null;
|
|
168
|
+
const shown = new Set(visible.map(table => table.id));
|
|
169
|
+
const collapsed = new Set(hidden.map(table => table.id));
|
|
170
|
+
const labels = new Map([...visible, ...hidden].map(table => [table.id, table.label]));
|
|
171
|
+
const node = (id) => (shown.has(id) ? id : collapsed.has(id) ? moreId : null);
|
|
185
172
|
const edges = [];
|
|
186
173
|
for (const table of [...visible, ...hidden])
|
|
187
174
|
for (const field of table.fields) {
|
|
188
|
-
if (field.kind !==
|
|
175
|
+
if (field.kind !== 'relationship')
|
|
189
176
|
continue;
|
|
190
177
|
const source = node(table.id);
|
|
191
178
|
const target = node(field.targetTableId);
|
|
192
179
|
if (!source || !target || (source === moreId && target === moreId))
|
|
193
180
|
continue;
|
|
194
181
|
const to = labels.get(field.targetTableId);
|
|
195
|
-
const sourceMany = field.inverseCardinality ===
|
|
196
|
-
const targetMany = field.cardinality ===
|
|
182
|
+
const sourceMany = field.inverseCardinality === 'many';
|
|
183
|
+
const targetMany = field.cardinality === 'many';
|
|
197
184
|
edges.push({
|
|
198
185
|
id: `${table.id}/${field.id}`,
|
|
199
|
-
type:
|
|
186
|
+
type: 'join',
|
|
200
187
|
source,
|
|
201
|
-
sourceHandle: source === moreId ?
|
|
188
|
+
sourceHandle: source === moreId ? 'out:right' : `${field.id}:right`,
|
|
202
189
|
target,
|
|
203
|
-
targetHandle: target === moreId ?
|
|
190
|
+
targetHandle: target === moreId ? 'in:left' : 'id:left',
|
|
204
191
|
data: {
|
|
205
192
|
join: joinText(table.label, field.label, to, targetMany),
|
|
206
|
-
description: `${table.label}.${field.label} joins ${to}.id, ${sourceMany ?
|
|
193
|
+
description: `${table.label}.${field.label} joins ${to}.id, ${sourceMany ? 'many' : 'one'} to ${targetMany ? 'many' : 'one'}`,
|
|
207
194
|
sourceMany,
|
|
208
195
|
targetMany,
|
|
209
196
|
},
|
|
@@ -219,42 +206,36 @@ function sideHandles(type, id) {
|
|
|
219
206
|
function TableCard({ id, data: { table, tone } }) {
|
|
220
207
|
const viewer = useViewer();
|
|
221
208
|
const composite = compositeKeys(table);
|
|
222
|
-
const unique = new Set((table.uniqueKeys ?? [])
|
|
223
|
-
|
|
224
|
-
.map((key) => key.fieldIds[0]));
|
|
225
|
-
const labels = new Map(table.fields.map((field) => [field.id, field.label]));
|
|
209
|
+
const unique = new Set((table.uniqueKeys ?? []).filter(key => key.fieldIds.length === 1).map(key => key.fieldIds[0]));
|
|
210
|
+
const labels = new Map(table.fields.map(field => [field.id, field.label]));
|
|
226
211
|
const focused = viewer.focus?.table === id;
|
|
227
|
-
return (_jsxs("section", { "data-slot": "sql-table", "data-tone": tone, "data-dimmed": viewer.focus && !viewer.related.has(id) ?
|
|
228
|
-
const join = field.kind ===
|
|
212
|
+
return (_jsxs("section", { "data-slot": "sql-table", "data-tone": tone, "data-dimmed": viewer.focus && !viewer.related.has(id) ? '' : undefined, "data-active": focused ? '' : undefined, className: "sprout-sql-table", onMouseLeave: () => viewer.hover(null), children: [_jsx("header", { className: "sprout-sql-table-header", children: _jsx("h3", { className: "sprout-sql-table-title", title: table.label, children: table.label }) }), _jsxs("ul", { className: "sprout-sql-columns", children: [_jsxs("li", { className: "sprout-sql-column", "data-key": "primary", children: [sideHandles('target', 'id'), _jsx("span", { className: "sprout-sql-column-icon", children: _jsx(Key, { size: 12, role: "img", "aria-label": "Primary key" }) }), _jsx("span", { className: "sprout-sql-column-name", children: "id" }), _jsx("span", { className: "sprout-sql-column-type", children: "uuid" })] }), table.fields.map(field => {
|
|
213
|
+
const join = field.kind === 'relationship';
|
|
229
214
|
const type = columnType(field);
|
|
230
|
-
return (_jsxs("li", { className: "sprout-sql-column", "data-join": join ?
|
|
215
|
+
return (_jsxs("li", { className: "sprout-sql-column", "data-join": join ? '' : undefined, "data-active": focused && viewer.focus?.field === field.id ? '' : undefined, title: field.kind === 'single-select' || field.kind === 'multi-select'
|
|
231
216
|
? field.options
|
|
232
|
-
.filter(
|
|
233
|
-
.map(
|
|
234
|
-
.join(
|
|
235
|
-
: undefined, onMouseEnter: join
|
|
236
|
-
?
|
|
237
|
-
: undefined, onMouseLeave: join ? () => viewer.hover({ table: id }) : undefined, onClick: join
|
|
238
|
-
? (event) => {
|
|
217
|
+
.filter(option => !option.retired)
|
|
218
|
+
.map(option => option.label)
|
|
219
|
+
.join(' · ')
|
|
220
|
+
: undefined, onMouseEnter: join ? () => viewer.hover({ table: id, field: field.id }) : undefined, onMouseLeave: join ? () => viewer.hover({ table: id }) : undefined, onClick: join
|
|
221
|
+
? event => {
|
|
239
222
|
event.stopPropagation();
|
|
240
223
|
viewer.pin({ table: id, field: field.id });
|
|
241
224
|
}
|
|
242
|
-
: undefined, children: [join && sideHandles(
|
|
243
|
-
})] }), composite.length > 0 && (_jsxs("div", { className: "sprout-sql-keys", children: [_jsx("p", { className: "sprout-sql-keys-title", children: "Unique keys" }), _jsx("ul", { className: "sprout-sql-keys-list", children: composite.map(
|
|
244
|
-
.map((field) => labels.get(field) ?? field)
|
|
245
|
-
.join(", ") })] }, key.id))) })] }))] }));
|
|
225
|
+
: undefined, children: [join && sideHandles('source', field.id), _jsx("span", { className: "sprout-sql-column-icon", children: join ? (_jsx(Link, { size: 12, role: "img", "aria-label": "Foreign key" })) : unique.has(field.id) ? (_jsx(Fingerprint, { size: 12, role: "img", "aria-label": "Unique" })) : null }), _jsx("span", { className: "sprout-sql-column-name", title: field.label, children: field.label }), _jsxs("span", { className: "sprout-sql-column-type", children: [type, !field.required && (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: "?" }), _jsx("span", { className: "sprout-sql-sr-only", children: ", nullable" })] }))] })] }, field.id));
|
|
226
|
+
})] }), composite.length > 0 && (_jsxs("div", { className: "sprout-sql-keys", children: [_jsx("p", { className: "sprout-sql-keys-title", children: "Unique keys" }), _jsx("ul", { className: "sprout-sql-keys-list", children: composite.map(key => (_jsxs("li", { className: "sprout-sql-key", children: [_jsx(Fingerprint, { size: 12, "aria-hidden": "true" }), _jsx("span", { children: key.fieldIds.map(field => labels.get(field) ?? field).join(', ') })] }, key.id))) })] }))] }));
|
|
246
227
|
}
|
|
247
228
|
function GroupFrame({ data: { label, tone, count } }) {
|
|
248
|
-
return (_jsx("section", { "data-slot": "sql-group", "data-tone": tone, "aria-label": `${label}, ${count} ${count === 1 ?
|
|
229
|
+
return (_jsx("section", { "data-slot": "sql-group", "data-tone": tone, "aria-label": `${label}, ${count} ${count === 1 ? 'table' : 'tables'}`, className: "sprout-sql-group", children: _jsxs("header", { className: "sprout-sql-group-label", children: [_jsx(Layers, { size: 14, "aria-hidden": "true" }), _jsx("span", { className: "sprout-sql-group-name", children: label }), _jsx("span", { className: "sprout-sql-group-count", children: count })] }) }));
|
|
249
230
|
}
|
|
250
231
|
function MoreCard({ data: { tables } }) {
|
|
251
232
|
const { expand } = useViewer();
|
|
252
|
-
const names = tables.slice(0, 3).map(
|
|
233
|
+
const names = tables.slice(0, 3).map(table => table.label);
|
|
253
234
|
const rest = tables.length - names.length;
|
|
254
|
-
return (_jsxs("div", { "data-slot": "sql-more", className: "sprout-sql-more", children: [sideHandles(
|
|
235
|
+
return (_jsxs("div", { "data-slot": "sql-more", className: "sprout-sql-more", children: [sideHandles('target', 'in'), sideHandles('source', 'out'), _jsx("span", { "aria-hidden": "true", className: "sprout-sql-more-sheet" }), _jsx("span", { "aria-hidden": "true", className: "sprout-sql-more-sheet" }), _jsxs("button", { type: "button", className: "nodrag sprout-sql-more-button", onClick: expand, "aria-label": `Show ${tables.length} more ${tables.length === 1 ? 'table' : 'tables'}: ${tables.map(table => table.label).join(', ')}`, children: [_jsxs("span", { className: "sprout-sql-more-count", children: ["+", tables.length, " ", tables.length === 1 ? 'table' : 'tables'] }), _jsxs("span", { className: "sprout-sql-more-names", children: [names.join(', '), rest > 0 && ` and ${rest} more`] }), _jsx("span", { className: "sprout-sql-more-action", children: "Show all" })] })] }));
|
|
255
236
|
}
|
|
256
237
|
function handlePoint(node, type, id) {
|
|
257
|
-
const handle = node.internals.handleBounds?.[type]?.find(
|
|
238
|
+
const handle = node.internals.handleBounds?.[type]?.find(candidate => candidate.id === id);
|
|
258
239
|
if (!handle)
|
|
259
240
|
return null;
|
|
260
241
|
return {
|
|
@@ -264,13 +245,13 @@ function handlePoint(node, type, id) {
|
|
|
264
245
|
}
|
|
265
246
|
/** A crow's foot for many, a bar for one, drawn outward from the card edge. */
|
|
266
247
|
function endMark(x, y, side, many) {
|
|
267
|
-
const out = side ===
|
|
248
|
+
const out = side === 'right' ? 1 : -1;
|
|
268
249
|
return many
|
|
269
250
|
? `M ${x + out * 12} ${y} L ${x} ${y - 6} M ${x + out * 12} ${y} L ${x} ${y + 6}`
|
|
270
251
|
: `M ${x + out * 9} ${y - 5} L ${x + out * 9} ${y + 5}`;
|
|
271
252
|
}
|
|
272
253
|
// Picks the facing sides of the two cards on every render, so links stay tidy while tables are dragged.
|
|
273
|
-
function JoinLine({ id, source, target, sourceHandleId, targetHandleId, data
|
|
254
|
+
function JoinLine({ id, source, target, sourceHandleId, targetHandleId, data }) {
|
|
274
255
|
const from = useInternalNode(source);
|
|
275
256
|
const to = useInternalNode(target);
|
|
276
257
|
const viewer = useViewer();
|
|
@@ -280,17 +261,13 @@ function JoinLine({ id, source, target, sourceHandleId, targetHandleId, data, })
|
|
|
280
261
|
const toLeft = to.internals.positionAbsolute.x;
|
|
281
262
|
const fromRight = fromLeft + (from.measured.width ?? 0);
|
|
282
263
|
const toRight = toLeft + (to.measured.width ?? 0);
|
|
283
|
-
const [sourceSide, targetSide] = fromRight + 24 <= toLeft
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
: ["right", "right"];
|
|
288
|
-
const base = (handle) => handle.slice(0, handle.lastIndexOf(":"));
|
|
289
|
-
const start = handlePoint(from, "source", `${base(sourceHandleId)}:${sourceSide}`);
|
|
290
|
-
const end = handlePoint(to, "target", `${base(targetHandleId)}:${targetSide}`);
|
|
264
|
+
const [sourceSide, targetSide] = fromRight + 24 <= toLeft ? ['right', 'left'] : toRight + 24 <= fromLeft ? ['left', 'right'] : ['right', 'right'];
|
|
265
|
+
const base = (handle) => handle.slice(0, handle.lastIndexOf(':'));
|
|
266
|
+
const start = handlePoint(from, 'source', `${base(sourceHandleId)}:${sourceSide}`);
|
|
267
|
+
const end = handlePoint(to, 'target', `${base(targetHandleId)}:${targetSide}`);
|
|
291
268
|
if (!start || !end)
|
|
292
269
|
return null;
|
|
293
|
-
const position = (side) => side ===
|
|
270
|
+
const position = (side) => (side === 'right' ? Position.Right : Position.Left);
|
|
294
271
|
const [path] = getSmoothStepPath({
|
|
295
272
|
sourceX: start.x,
|
|
296
273
|
sourceY: start.y,
|
|
@@ -302,7 +279,7 @@ function JoinLine({ id, source, target, sourceHandleId, targetHandleId, data, })
|
|
|
302
279
|
offset: 28,
|
|
303
280
|
});
|
|
304
281
|
const active = viewer.activeEdges.has(id);
|
|
305
|
-
return (_jsx("path", { d: `${path} ${endMark(start.x, start.y, sourceSide, data.sourceMany)} ${endMark(end.x, end.y, targetSide, data.targetMany)}`, className: "react-flow__edge-path sprout-sql-join", "data-active": active ?
|
|
282
|
+
return (_jsx("path", { d: `${path} ${endMark(start.x, start.y, sourceSide, data.sourceMany)} ${endMark(end.x, end.y, targetSide, data.targetMany)}`, className: "react-flow__edge-path sprout-sql-join", "data-active": active ? '' : undefined, "data-dimmed": viewer.focus && !active ? '' : undefined }));
|
|
306
283
|
}
|
|
307
284
|
const nodeTypes = { table: TableCard, group: GroupFrame, more: MoreCard };
|
|
308
285
|
const edgeTypes = { join: JoinLine };
|
|
@@ -313,9 +290,7 @@ function ViewerFlow({ layoutNodes, edges, direction, label, hintId, expanded, se
|
|
|
313
290
|
const [hovered, setHovered] = useState(null);
|
|
314
291
|
const [pinned, setPinned] = useState(null);
|
|
315
292
|
const focus = hovered ?? pinned;
|
|
316
|
-
const pin = useCallback((next) => setPinned(
|
|
317
|
-
? null
|
|
318
|
-
: next), []);
|
|
293
|
+
const pin = useCallback((next) => setPinned(current => (current?.table === next.table && current.field === next.field ? null : next)), []);
|
|
319
294
|
const state = useMemo(() => {
|
|
320
295
|
const activeEdges = new Set();
|
|
321
296
|
const related = new Set();
|
|
@@ -341,69 +316,65 @@ function ViewerFlow({ layoutNodes, edges, direction, label, hintId, expanded, se
|
|
|
341
316
|
};
|
|
342
317
|
}, [focus, edges, pin, setExpanded]);
|
|
343
318
|
const shownEdges = useMemo(() => state.activeEdges.size
|
|
344
|
-
? edges.map(
|
|
319
|
+
? edges.map(edge => (state.activeEdges.has(edge.id) ? { ...edge, zIndex: 1000 } : edge))
|
|
345
320
|
: edges, [edges, state.activeEdges]);
|
|
346
321
|
// Phones fit the first block to the width and let people pan down; wider panes fit everything.
|
|
347
|
-
const fitFirst = useMemo(() => direction ===
|
|
322
|
+
const fitFirst = useMemo(() => direction === 'vertical' && layoutNodes[0]
|
|
348
323
|
? { padding: 0.06, maxZoom: 1, nodes: [{ id: layoutNodes[0].id }] }
|
|
349
324
|
: fitAll, [direction, layoutNodes]);
|
|
350
325
|
const keyboardView = useKeyboardView(fitAll);
|
|
351
326
|
const onKeyDown = (event) => {
|
|
352
327
|
const target = event.target;
|
|
353
|
-
const id = target.dataset[
|
|
354
|
-
if ((event.key ===
|
|
355
|
-
target.classList.contains("react-flow__node-table") &&
|
|
356
|
-
id) {
|
|
328
|
+
const id = target.dataset['id'];
|
|
329
|
+
if ((event.key === 'Enter' || event.key === ' ') && target.classList.contains('react-flow__node-table') && id) {
|
|
357
330
|
event.preventDefault();
|
|
358
331
|
pin({ table: id });
|
|
359
332
|
return;
|
|
360
333
|
}
|
|
361
|
-
if (event.key ===
|
|
334
|
+
if (event.key === 'Escape' && pinned) {
|
|
362
335
|
setPinned(null);
|
|
363
336
|
return;
|
|
364
337
|
}
|
|
365
338
|
keyboardView(event);
|
|
366
339
|
};
|
|
367
340
|
const onFocus = (event) => {
|
|
368
|
-
const id = event.target.dataset[
|
|
369
|
-
if (event.target.classList.contains(
|
|
341
|
+
const id = event.target.dataset['id'];
|
|
342
|
+
if (event.target.classList.contains('react-flow__node-table') && id)
|
|
370
343
|
setHovered({ table: id });
|
|
371
344
|
};
|
|
372
345
|
const onNodeMouseEnter = (_, node) => {
|
|
373
|
-
if (node.type ===
|
|
346
|
+
if (node.type === 'table')
|
|
374
347
|
setHovered({ table: node.id });
|
|
375
348
|
};
|
|
376
349
|
const onNodeClick = (_, node) => {
|
|
377
|
-
if (node.type ===
|
|
350
|
+
if (node.type === 'table')
|
|
378
351
|
pin({ table: node.id });
|
|
379
352
|
};
|
|
380
|
-
return (_jsx(ViewerContext.Provider, { value: state, children: _jsxs(ReactFlow, { className: "sprout-schema-flow sprout-sql-flow", "data-slot": "sql-viewer-viewport", "data-focus": focus ?
|
|
381
|
-
|
|
382
|
-
|
|
353
|
+
return (_jsx(ViewerContext.Provider, { value: state, children: _jsxs(ReactFlow, { className: "sprout-schema-flow sprout-sql-flow", "data-slot": "sql-viewer-viewport", "data-focus": focus ? '' : undefined, "aria-label": label, "aria-describedby": hintId, tabIndex: 0, onKeyDown: onKeyDown, onFocus: onFocus, onBlur: () => setHovered(null), nodes: nodes, edges: shownEdges, onNodesChange: onNodesChange, nodeTypes: nodeTypes, edgeTypes: edgeTypes, onNodeMouseEnter: onNodeMouseEnter, onNodeClick: onNodeClick, onPaneClick: () => setPinned(null), fitView: true, fitViewOptions: fitFirst, minZoom: flowMinZoom, maxZoom: flowMaxZoom, nodesConnectable: false, elementsSelectable: false, edgesFocusable: false, ariaLabelConfig: {
|
|
354
|
+
'node.a11yDescription.default': keyboardHint,
|
|
355
|
+
'node.a11yDescription.keyboardDisabled': keyboardHint,
|
|
383
356
|
}, proOptions: { hideAttribution: true }, children: [_jsx(Background, { gap: 16, size: 1 }), _jsx(FitOnResize, { nodes: layoutNodes, fit: fitFirst }), collapsible && expanded && (_jsx(Panel, { position: "top-left", "data-slot": "sql-viewer-collapse", children: _jsxs(Button, { variant: "secondary", size: "sm", onClick: () => {
|
|
384
357
|
setPinned(null);
|
|
385
358
|
setExpanded(false);
|
|
386
359
|
}, children: ["Show ", maxVisible, " tables"] }) })), state.activeEdges.size > 0 && (_jsxs(Panel, { position: "bottom-left", "data-slot": "sql-viewer-joins", "aria-hidden": "true", className: "sprout-sql-joins", children: [edges
|
|
387
|
-
.filter(
|
|
360
|
+
.filter(edge => state.activeEdges.has(edge.id))
|
|
388
361
|
.slice(0, 3)
|
|
389
|
-
.map(
|
|
362
|
+
.map(edge => (_jsx("code", { className: "sprout-sql-join-text", children: edge.data.join }, edge.id))), state.activeEdges.size > 3 && (_jsxs("span", { className: "sprout-sql-joins-more", children: ["and ", state.activeEdges.size - 3, " more"] }))] })), _jsx(ZoomControls, { position: direction === 'vertical' ? 'top-right' : 'bottom-right', fit: fitAll, className: "sprout-schema-controls", slot: "sql-viewer-controls" })] }) }));
|
|
390
363
|
}
|
|
391
364
|
/**
|
|
392
365
|
* A pan-and-zoom map of SQL tables, their columns and keys, and the joins
|
|
393
366
|
* between them. Hover or pin a table or a foreign key to trace its joins.
|
|
394
367
|
*/
|
|
395
|
-
export function SqlViewer({ tables, label, groups, maxVisible = 4, layout =
|
|
368
|
+
export function SqlViewer({ tables, label, groups, maxVisible = 4, layout = 'auto', className, ...props }) {
|
|
396
369
|
const mobile = useViewportMobile();
|
|
397
|
-
const direction = layout ===
|
|
370
|
+
const direction = layout === 'auto' ? (mobile ? 'vertical' : 'horizontal') : layout;
|
|
398
371
|
const hintId = useId();
|
|
399
372
|
const [expanded, setExpanded] = useState(false);
|
|
400
373
|
const limit = Math.max(1, Math.floor(maxVisible));
|
|
401
374
|
const collapsible = tables.length > limit;
|
|
402
|
-
const [visible, hidden] = useMemo(() => collapsible && !expanded
|
|
403
|
-
? [tables.slice(0, limit), tables.slice(limit)]
|
|
404
|
-
: [tables, []], [tables, limit, collapsible, expanded]);
|
|
375
|
+
const [visible, hidden] = useMemo(() => (collapsible && !expanded ? [tables.slice(0, limit), tables.slice(limit)] : [tables, []]), [tables, limit, collapsible, expanded]);
|
|
405
376
|
const layoutNodes = useMemo(() => arrange(visible, hidden, groups, direction), [visible, hidden, groups, direction]);
|
|
406
377
|
const edges = useMemo(() => joins(visible, hidden), [visible, hidden]);
|
|
407
378
|
const descriptions = joins(tables, []);
|
|
408
|
-
return (_jsxs("div", { ...props, role: "figure", "aria-label": label, "data-slot": "sql-viewer", "data-layout": direction, className: cn(
|
|
379
|
+
return (_jsxs("div", { ...props, role: "figure", "aria-label": label, "data-slot": "sql-viewer", "data-layout": direction, className: cn('sprout-sql-viewer', className), children: [_jsx("p", { id: hintId, className: "sprout-sql-sr-only", children: keyboardHint }), descriptions.length > 0 && (_jsx("ul", { "aria-label": "Joins", className: "sprout-sql-sr-only", children: descriptions.map(edge => (_jsx("li", { children: edge.data.description }, edge.id))) })), tables.length === 0 ? (_jsx("p", { className: "sprout-sql-empty", children: "No tables to show" })) : (_jsx(ReactFlowProvider, { children: _jsx(ViewerFlow, { layoutNodes: layoutNodes, edges: edges, direction: direction, label: label, hintId: hintId, expanded: expanded, setExpanded: setExpanded, collapsible: collapsible, maxVisible: limit }) }, direction))] }));
|
|
409
380
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type SampleWindowProps } from
|
|
1
|
+
import { type SampleWindowProps } from './state-sample.js';
|
|
2
2
|
/** Pass-through props for the outer sample window. */
|
|
3
|
-
export type StatePanelSampleProps = Omit<SampleWindowProps,
|
|
3
|
+
export type StatePanelSampleProps = Omit<SampleWindowProps, 'icon' | 'trail' | 'tabs' | 'children'>;
|
|
4
4
|
export declare function ChatsSample(props: StatePanelSampleProps): import("react").JSX.Element;
|
|
5
5
|
export declare function FilesSample(props: StatePanelSampleProps): import("react").JSX.Element;
|
|
6
6
|
export declare function AgentsSample(props: StatePanelSampleProps): import("react").JSX.Element;
|