@refraction-ui/react 0.19.1 → 0.20.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/dist/chunk-533MCMSU.js +2355 -0
- package/dist/chunk-533MCMSU.js.map +7 -0
- package/dist/chunk-AMK4VCBG.js +250 -0
- package/dist/chunk-AMK4VCBG.js.map +7 -0
- package/dist/chunk-BOLFYVI6.js +90 -0
- package/dist/chunk-BOLFYVI6.js.map +7 -0
- package/dist/chunk-F7B64KT3.js +187 -0
- package/dist/chunk-F7B64KT3.js.map +7 -0
- package/dist/chunk-FD37DFCC.js +77 -0
- package/dist/chunk-FD37DFCC.js.map +7 -0
- package/dist/chunk-H54FLNAF.js +132 -0
- package/dist/chunk-H54FLNAF.js.map +7 -0
- package/dist/chunk-INP46354.js +664 -0
- package/dist/chunk-INP46354.js.map +7 -0
- package/dist/chunk-ONACRMG4.js +116 -0
- package/dist/chunk-ONACRMG4.js.map +7 -0
- package/dist/chunk-SDZ3LROZ.js +7 -0
- package/dist/chunk-SDZ3LROZ.js.map +7 -0
- package/dist/chunk-TOZBR4GL.js +154 -0
- package/dist/chunk-TOZBR4GL.js.map +7 -0
- package/dist/chunk-UNEM2TRI.js +225 -0
- package/dist/chunk-UNEM2TRI.js.map +7 -0
- package/dist/chunk-VK3EQ34F.js +92 -0
- package/dist/chunk-VK3EQ34F.js.map +7 -0
- package/dist/chunk-WK2IIUFO.js +316 -0
- package/dist/chunk-WK2IIUFO.js.map +7 -0
- package/dist/chunk-WSPJTQAV.js +59 -0
- package/dist/chunk-WSPJTQAV.js.map +7 -0
- package/dist/chunk-XWP763SH.js +3 -2
- package/dist/chunk-XWP763SH.js.map +7 -1
- package/dist/chunk-Y2FNNAIL.js +80 -0
- package/dist/chunk-Y2FNNAIL.js.map +1 -0
- package/dist/{chunk-4A4BW3GR.js → chunk-ZF3OHCGV.js} +28 -5
- package/dist/chunk-ZF3OHCGV.js.map +7 -0
- package/dist/form.cjs +5 -11
- package/dist/form.cjs.map +1 -1
- package/dist/form.js +6 -12
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +2879 -33936
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +126 -28780
- package/dist/internal/react-app-shell/index.d.cts +2 -22
- package/dist/internal/react-app-shell/index.d.ts +2 -22
- package/dist/internal/react-badge/index.d.cts +5 -0
- package/dist/internal/react-badge/index.d.ts +5 -0
- package/dist/internal/react-callout/index.d.cts +4 -0
- package/dist/internal/react-callout/index.d.ts +4 -0
- package/dist/internal/react-card/index.d.cts +8 -1
- package/dist/internal/react-card/index.d.ts +8 -1
- package/dist/internal/react-collapsible/index.d.cts +0 -3
- package/dist/internal/react-collapsible/index.d.ts +0 -3
- package/dist/internal/react-combobox/index.d.cts +0 -3
- package/dist/internal/react-combobox/index.d.ts +0 -3
- package/dist/internal/react-command/index.d.cts +0 -3
- package/dist/internal/react-command/index.d.ts +0 -3
- package/dist/internal/react-data-table/index.d.cts +0 -3
- package/dist/internal/react-data-table/index.d.ts +0 -3
- package/dist/internal/react-dialog/index.d.cts +0 -3
- package/dist/internal/react-dialog/index.d.ts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.cts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.ts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.cts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.ts +0 -3
- package/dist/internal/react-form/index.d.cts +2 -21
- package/dist/internal/react-form/index.d.ts +2 -21
- package/dist/internal/react-keyboard-shortcut/index.d.cts +0 -6
- package/dist/internal/react-keyboard-shortcut/index.d.ts +0 -6
- package/dist/internal/react-link-card/index.d.cts +8 -2
- package/dist/internal/react-link-card/index.d.ts +8 -2
- package/dist/internal/react-popover/index.d.cts +0 -3
- package/dist/internal/react-popover/index.d.ts +0 -3
- package/dist/internal/react-sheet/index.d.cts +0 -3
- package/dist/internal/react-sheet/index.d.ts +0 -3
- package/dist/internal/react-slot/index.d.cts +23 -0
- package/dist/internal/react-slot/index.d.ts +23 -0
- package/dist/internal/react-toast/index.d.cts +0 -3
- package/dist/internal/react-toast/index.d.ts +0 -3
- package/dist/internal/react-tooltip/index.d.cts +0 -3
- package/dist/internal/react-tooltip/index.d.ts +0 -3
- package/dist/react-accordion.js +125 -0
- package/dist/react-accordion.js.map +7 -0
- package/dist/react-ai.js +257 -0
- package/dist/react-ai.js.map +7 -0
- package/dist/react-analytics.js +1698 -0
- package/dist/react-analytics.js.map +7 -0
- package/dist/react-animated-text.js +164 -0
- package/dist/react-animated-text.js.map +7 -0
- package/dist/react-app-shell.js +631 -0
- package/dist/react-app-shell.js.map +7 -0
- package/dist/react-audience-feature-card.js +53 -0
- package/dist/react-audience-feature-card.js.map +7 -0
- package/dist/react-audio-room.js +166 -0
- package/dist/react-audio-room.js.map +7 -0
- package/dist/react-auth.js +211 -0
- package/dist/react-auth.js.map +7 -0
- package/dist/react-avatar-group.js +154 -0
- package/dist/react-avatar-group.js.map +7 -0
- package/dist/react-avatar.js +168 -0
- package/dist/react-avatar.js.map +7 -0
- package/dist/react-badge.js +86 -0
- package/dist/react-badge.js.map +7 -0
- package/dist/react-bottom-nav.js +67 -0
- package/dist/react-bottom-nav.js.map +7 -0
- package/dist/react-brand-network-cell.js +92 -0
- package/dist/react-brand-network-cell.js.map +7 -0
- package/dist/react-breadcrumbs.js +104 -0
- package/dist/react-breadcrumbs.js.map +7 -0
- package/dist/react-browser-chrome-mock.js +89 -0
- package/dist/react-browser-chrome-mock.js.map +7 -0
- package/dist/react-button.js +13 -0
- package/dist/react-button.js.map +7 -0
- package/dist/react-calendar.js +301 -0
- package/dist/react-calendar.js.map +7 -0
- package/dist/react-call-controls.js +113 -0
- package/dist/react-call-controls.js.map +7 -0
- package/dist/react-callout.js +152 -0
- package/dist/react-callout.js.map +7 -0
- package/dist/react-card-grid.js +34 -0
- package/dist/react-card-grid.js.map +7 -0
- package/dist/react-card.js +200 -0
- package/dist/react-card.js.map +7 -0
- package/dist/react-carousel.js +132 -0
- package/dist/react-carousel.js.map +7 -0
- package/dist/react-charts.js +509 -0
- package/dist/react-charts.js.map +7 -0
- package/dist/react-checkbox.js +156 -0
- package/dist/react-checkbox.js.map +7 -0
- package/dist/react-checklist.js +168 -0
- package/dist/react-checklist.js.map +7 -0
- package/dist/react-code-block.js +78 -0
- package/dist/react-code-block.js.map +7 -0
- package/dist/react-code-editor.js +184 -0
- package/dist/react-code-editor.js.map +7 -0
- package/dist/react-collapsible.js +192 -0
- package/dist/react-collapsible.js.map +7 -0
- package/dist/react-combobox.js +609 -0
- package/dist/react-combobox.js.map +7 -0
- package/dist/react-command-input.js +202 -0
- package/dist/react-command-input.js.map +7 -0
- package/dist/react-command.js +364 -0
- package/dist/react-command.js.map +7 -0
- package/dist/react-composer.js +2036 -0
- package/dist/react-composer.js.map +7 -0
- package/dist/react-content-protection.js +133 -0
- package/dist/react-content-protection.js.map +7 -0
- package/dist/react-conversation.js +1226 -0
- package/dist/react-conversation.js.map +7 -0
- package/dist/react-cookie-consent.js +291 -0
- package/dist/react-cookie-consent.js.map +7 -0
- package/dist/react-data-table.js +310 -0
- package/dist/react-data-table.js.map +7 -0
- package/dist/react-date-picker.js +95 -0
- package/dist/react-date-picker.js.map +7 -0
- package/dist/react-device-frame.js +137 -0
- package/dist/react-device-frame.js.map +7 -0
- package/dist/react-dialog.js +218 -0
- package/dist/react-dialog.js.map +7 -0
- package/dist/react-diff-viewer.js +460 -0
- package/dist/react-diff-viewer.js.map +7 -0
- package/dist/react-dropdown-menu.js +292 -0
- package/dist/react-dropdown-menu.js.map +7 -0
- package/dist/react-editor-status-bar.js +148 -0
- package/dist/react-editor-status-bar.js.map +7 -0
- package/dist/react-editor-tabs.js +159 -0
- package/dist/react-editor-tabs.js.map +7 -0
- package/dist/react-emoji-picker.js +238 -0
- package/dist/react-emoji-picker.js.map +7 -0
- package/dist/react-empty-state.js +90 -0
- package/dist/react-empty-state.js.map +7 -0
- package/dist/react-feedback-dialog.js +275 -0
- package/dist/react-feedback-dialog.js.map +7 -0
- package/dist/react-file-tree.js +376 -0
- package/dist/react-file-tree.js.map +7 -0
- package/dist/react-file-upload.js +360 -0
- package/dist/react-file-upload.js.map +7 -0
- package/dist/react-floating-reactions.js +124 -0
- package/dist/react-floating-reactions.js.map +7 -0
- package/dist/react-flow-editor.js +198 -0
- package/dist/react-flow-editor.js.map +7 -0
- package/dist/react-footer.js +67 -0
- package/dist/react-footer.js.map +7 -0
- package/dist/react-graph-view.js +197 -0
- package/dist/react-graph-view.js.map +7 -0
- package/dist/react-icon-system.js +9 -0
- package/dist/react-icon-system.js.map +7 -0
- package/dist/react-infinite-canvas.js +8 -0
- package/dist/react-infinite-canvas.js.map +7 -0
- package/dist/react-inline-editor.js +216 -0
- package/dist/react-inline-editor.js.map +7 -0
- package/dist/react-input-group.js +150 -0
- package/dist/react-input-group.js.map +7 -0
- package/dist/react-input.js +10 -0
- package/dist/react-input.js.map +7 -0
- package/dist/react-install-prompt.js +132 -0
- package/dist/react-install-prompt.js.map +7 -0
- package/dist/react-kanban-board.js +198 -0
- package/dist/react-kanban-board.js.map +7 -0
- package/dist/react-keyboard-shortcut.js +36 -0
- package/dist/react-keyboard-shortcut.js.map +7 -0
- package/dist/react-language-selector.js +311 -0
- package/dist/react-language-selector.js.map +7 -0
- package/dist/react-link-card.js +48 -0
- package/dist/react-link-card.js.map +7 -0
- package/dist/react-live-captions.js +92 -0
- package/dist/react-live-captions.js.map +7 -0
- package/dist/react-live-cursors.js +145 -0
- package/dist/react-live-cursors.js.map +7 -0
- package/dist/react-live-transcript.js +117 -0
- package/dist/react-live-transcript.js.map +7 -0
- package/dist/react-location-selector.js +252 -0
- package/dist/react-location-selector.js.map +7 -0
- package/dist/react-logger.js +415 -0
- package/dist/react-logger.js.map +7 -0
- package/dist/react-markdown-renderer.js +12 -0
- package/dist/react-markdown-renderer.js.map +7 -0
- package/dist/react-marquee-strip.js +62 -0
- package/dist/react-marquee-strip.js.map +7 -0
- package/dist/react-mascot.js +367 -0
- package/dist/react-mascot.js.map +7 -0
- package/dist/react-mastery-bar.js +97 -0
- package/dist/react-mastery-bar.js.map +7 -0
- package/dist/react-mini-map.js +207 -0
- package/dist/react-mini-map.js.map +7 -0
- package/dist/react-mobile-nav.js +216 -0
- package/dist/react-mobile-nav.js.map +7 -0
- package/dist/react-navbar.js +96 -0
- package/dist/react-navbar.js.map +7 -0
- package/dist/react-numbered-steps.js +67 -0
- package/dist/react-numbered-steps.js.map +7 -0
- package/dist/react-otp-input.js +318 -0
- package/dist/react-otp-input.js.map +7 -0
- package/dist/react-pagination.js +199 -0
- package/dist/react-pagination.js.map +7 -0
- package/dist/react-password-input.js +104 -0
- package/dist/react-password-input.js.map +7 -0
- package/dist/react-payment.js +81 -0
- package/dist/react-payment.js.map +7 -0
- package/dist/react-popover.js +176 -0
- package/dist/react-popover.js.map +7 -0
- package/dist/react-pre-call-lobby.js +309 -0
- package/dist/react-pre-call-lobby.js.map +7 -0
- package/dist/react-presence-indicator.js +86 -0
- package/dist/react-presence-indicator.js.map +7 -0
- package/dist/react-pricing-card.js +142 -0
- package/dist/react-pricing-card.js.map +7 -0
- package/dist/react-progress-display.js +188 -0
- package/dist/react-progress-display.js.map +7 -0
- package/dist/react-radial-gauge.js +193 -0
- package/dist/react-radial-gauge.js.map +7 -0
- package/dist/react-radio.js +169 -0
- package/dist/react-radio.js.map +7 -0
- package/dist/react-rating-scale.js +171 -0
- package/dist/react-rating-scale.js.map +7 -0
- package/dist/react-reaction-bar.js +111 -0
- package/dist/react-reaction-bar.js.map +7 -0
- package/dist/react-resizable-layout.js +298 -0
- package/dist/react-resizable-layout.js.map +7 -0
- package/dist/react-rich-editor.js +1 -0
- package/dist/react-rich-editor.js.map +7 -0
- package/dist/react-search-bar.js +285 -0
- package/dist/react-search-bar.js.map +7 -0
- package/dist/react-section-head.js +65 -0
- package/dist/react-section-head.js.map +7 -0
- package/dist/react-segmented-control.js +215 -0
- package/dist/react-segmented-control.js.map +7 -0
- package/dist/react-select.js +389 -0
- package/dist/react-select.js.map +7 -0
- package/dist/react-separator.js +86 -0
- package/dist/react-separator.js.map +7 -0
- package/dist/react-sheet.js +310 -0
- package/dist/react-sheet.js.map +7 -0
- package/dist/react-sidebar.js +100 -0
- package/dist/react-sidebar.js.map +7 -0
- package/dist/react-skeleton.js +91 -0
- package/dist/react-skeleton.js.map +7 -0
- package/dist/react-skip-to-content.js +38 -0
- package/dist/react-skip-to-content.js.map +7 -0
- package/dist/react-slide-viewer.js +264 -0
- package/dist/react-slide-viewer.js.map +7 -0
- package/dist/react-slider.js +105 -0
- package/dist/react-slider.js.map +7 -0
- package/dist/react-slot-picker.js +184 -0
- package/dist/react-slot-picker.js.map +7 -0
- package/dist/react-slot.js +7 -0
- package/dist/react-slot.js.map +7 -0
- package/dist/react-social-auth-button.js +169 -0
- package/dist/react-social-auth-button.js.map +7 -0
- package/dist/react-sortable-list.js +204 -0
- package/dist/react-sortable-list.js.map +7 -0
- package/dist/react-stat-grid.js +69 -0
- package/dist/react-stat-grid.js.map +7 -0
- package/dist/react-status-indicator.js +117 -0
- package/dist/react-status-indicator.js.map +7 -0
- package/dist/react-steps.js +167 -0
- package/dist/react-steps.js.map +7 -0
- package/dist/react-sticky-note.js +131 -0
- package/dist/react-sticky-note.js.map +7 -0
- package/dist/react-switch.js +139 -0
- package/dist/react-switch.js.map +7 -0
- package/dist/react-table-of-contents.js +80 -0
- package/dist/react-table-of-contents.js.map +7 -0
- package/dist/react-tabs.js +230 -0
- package/dist/react-tabs.js.map +7 -0
- package/dist/react-terminal.js +101 -0
- package/dist/react-terminal.js.map +7 -0
- package/dist/react-test-results.js +158 -0
- package/dist/react-test-results.js.map +7 -0
- package/dist/react-textarea.js +89 -0
- package/dist/react-textarea.js.map +7 -0
- package/dist/react-thread-view.js +139 -0
- package/dist/react-thread-view.js.map +7 -0
- package/dist/react-timeline.js +153 -0
- package/dist/react-timeline.js.map +7 -0
- package/dist/react-toast.js +253 -0
- package/dist/react-toast.js.map +7 -0
- package/dist/react-tooltip.js +221 -0
- package/dist/react-tooltip.js.map +7 -0
- package/dist/react-version-selector.js +270 -0
- package/dist/react-version-selector.js.map +7 -0
- package/dist/react-video-grid.js +129 -0
- package/dist/react-video-grid.js.map +7 -0
- package/dist/react-video-player.js +206 -0
- package/dist/react-video-player.js.map +7 -0
- package/dist/react-video-tile.js +8 -0
- package/dist/react-video-tile.js.map +7 -0
- package/dist/react-voice-pill.js +314 -0
- package/dist/react-voice-pill.js.map +7 -0
- package/dist/react-waveform.js +558 -0
- package/dist/react-waveform.js.map +7 -0
- package/dist/react-wizard.js +337 -0
- package/dist/react-wizard.js.map +7 -0
- package/dist/styles.css +7 -0
- package/dist/theme.js +1 -1
- package/package.json +3 -2
- package/dist/applicationinsights-web-H7OBLIVJ.js +0 -23393
- package/dist/applicationinsights-web-H7OBLIVJ.js.map +0 -1
- package/dist/chunk-4A4BW3GR.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/module-X3HTTRQK.js +0 -6438
- package/dist/module-X3HTTRQK.js.map +0 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InfiniteCanvas
|
|
3
|
+
} from "./chunk-AMK4VCBG.js";
|
|
4
|
+
import {
|
|
5
|
+
cn,
|
|
6
|
+
cva
|
|
7
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
8
|
+
|
|
9
|
+
// ../react-flow-editor/dist/index.js
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
// ../flow-editor/dist/index.js
|
|
13
|
+
var DEFAULT_NODE_WIDTH = 160;
|
|
14
|
+
var DEFAULT_NODE_HEIGHT = 48;
|
|
15
|
+
function nodeById(nodes, id) {
|
|
16
|
+
return nodes.find((n) => n.id === id);
|
|
17
|
+
}
|
|
18
|
+
function edgePath(source, target) {
|
|
19
|
+
const sw = source.width ?? DEFAULT_NODE_WIDTH;
|
|
20
|
+
const sh = source.height ?? DEFAULT_NODE_HEIGHT;
|
|
21
|
+
const th = target.height ?? DEFAULT_NODE_HEIGHT;
|
|
22
|
+
const x1 = source.x + sw;
|
|
23
|
+
const y1 = source.y + sh / 2;
|
|
24
|
+
const x2 = target.x;
|
|
25
|
+
const y2 = target.y + th / 2;
|
|
26
|
+
const dx = Math.abs(x2 - x1);
|
|
27
|
+
const handleLen = Math.max(dx * 0.5, 40);
|
|
28
|
+
const cx1 = x1 + handleLen;
|
|
29
|
+
const cy1 = y1;
|
|
30
|
+
const cx2 = x2 - handleLen;
|
|
31
|
+
const cy2 = y2;
|
|
32
|
+
return `M ${x1} ${y1} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${x2} ${y2}`;
|
|
33
|
+
}
|
|
34
|
+
function createFlowEditor() {
|
|
35
|
+
return {
|
|
36
|
+
ariaProps: {
|
|
37
|
+
role: "group",
|
|
38
|
+
"aria-label": "Flow editor"
|
|
39
|
+
},
|
|
40
|
+
dataAttributes: {
|
|
41
|
+
"data-flow-editor": "true"
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
var flowEditorVariants = cva({
|
|
46
|
+
base: "relative w-full overflow-hidden bg-background border border-border rounded-lg",
|
|
47
|
+
variants: {
|
|
48
|
+
size: {
|
|
49
|
+
sm: "h-64",
|
|
50
|
+
md: "h-96",
|
|
51
|
+
lg: "h-[32rem]"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
defaultVariants: {
|
|
55
|
+
size: "md"
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
var flowNodeVariants = cva({
|
|
59
|
+
base: [
|
|
60
|
+
"absolute flex items-center justify-center rounded-md border",
|
|
61
|
+
"bg-card border-border text-card-foreground text-sm font-medium",
|
|
62
|
+
"cursor-pointer select-none transition-shadow",
|
|
63
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
64
|
+
].join(" "),
|
|
65
|
+
variants: {
|
|
66
|
+
selected: {
|
|
67
|
+
true: "ring-2 ring-ring ring-primary shadow-md",
|
|
68
|
+
false: "hover:border-primary/50 hover:shadow-sm"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
defaultVariants: {
|
|
72
|
+
selected: "false"
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
var flowNodeLabelClass = "px-3 py-2 truncate pointer-events-none";
|
|
76
|
+
var flowEdgePathClass = "fill-none stroke-current stroke-2 transition-opacity";
|
|
77
|
+
var flowEdgeLabelClass = "text-xs fill-muted-foreground select-none pointer-events-none";
|
|
78
|
+
|
|
79
|
+
// ../react-flow-editor/dist/index.js
|
|
80
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
81
|
+
var FlowEditor = React.forwardRef(
|
|
82
|
+
function FlowEditor2({
|
|
83
|
+
nodes,
|
|
84
|
+
edges,
|
|
85
|
+
onNodeClick,
|
|
86
|
+
onNodeMove,
|
|
87
|
+
renderNode,
|
|
88
|
+
selectedId,
|
|
89
|
+
showGrid = true,
|
|
90
|
+
showControls = true,
|
|
91
|
+
className,
|
|
92
|
+
...props
|
|
93
|
+
}, ref) {
|
|
94
|
+
const api = createFlowEditor();
|
|
95
|
+
const dragRef = React.useRef(null);
|
|
96
|
+
const handlePointerDown = React.useCallback(
|
|
97
|
+
(event, node) => {
|
|
98
|
+
if (!onNodeMove) return;
|
|
99
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
100
|
+
dragRef.current = {
|
|
101
|
+
id: node.id,
|
|
102
|
+
startPointerX: event.clientX,
|
|
103
|
+
startPointerY: event.clientY,
|
|
104
|
+
startNodeX: node.x,
|
|
105
|
+
startNodeY: node.y
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
[onNodeMove]
|
|
109
|
+
);
|
|
110
|
+
const handlePointerMove = React.useCallback(
|
|
111
|
+
(event) => {
|
|
112
|
+
const drag = dragRef.current;
|
|
113
|
+
if (!drag || !onNodeMove) return;
|
|
114
|
+
const dx = event.clientX - drag.startPointerX;
|
|
115
|
+
const dy = event.clientY - drag.startPointerY;
|
|
116
|
+
onNodeMove(drag.id, drag.startNodeX + dx, drag.startNodeY + dy);
|
|
117
|
+
},
|
|
118
|
+
[onNodeMove]
|
|
119
|
+
);
|
|
120
|
+
const handlePointerUp = React.useCallback(() => {
|
|
121
|
+
dragRef.current = null;
|
|
122
|
+
}, []);
|
|
123
|
+
return /* @__PURE__ */ jsx(
|
|
124
|
+
"div",
|
|
125
|
+
{
|
|
126
|
+
ref,
|
|
127
|
+
className: cn(flowEditorVariants(), className),
|
|
128
|
+
...api.ariaProps,
|
|
129
|
+
...api.dataAttributes,
|
|
130
|
+
...props,
|
|
131
|
+
children: /* @__PURE__ */ jsxs(InfiniteCanvas, { showGrid, showControls, children: [
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
"svg",
|
|
134
|
+
{
|
|
135
|
+
className: "absolute inset-0 w-full h-full overflow-visible text-muted-foreground pointer-events-none",
|
|
136
|
+
"aria-hidden": "true",
|
|
137
|
+
children: edges.map((edge) => {
|
|
138
|
+
const src = nodeById(nodes, edge.source);
|
|
139
|
+
const tgt = nodeById(nodes, edge.target);
|
|
140
|
+
if (!src || !tgt) return null;
|
|
141
|
+
const d = edgePath(src, tgt);
|
|
142
|
+
const midX = (src.x + (src.width ?? 160) + tgt.x) / 2;
|
|
143
|
+
const midY = (src.y + (src.height ?? 48) / 2 + tgt.y + (tgt.height ?? 48) / 2) / 2;
|
|
144
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
145
|
+
/* @__PURE__ */ jsx("path", { d, className: flowEdgePathClass }),
|
|
146
|
+
edge.label != null && /* @__PURE__ */ jsx(
|
|
147
|
+
"text",
|
|
148
|
+
{
|
|
149
|
+
x: midX,
|
|
150
|
+
y: midY - 4,
|
|
151
|
+
textAnchor: "middle",
|
|
152
|
+
className: flowEdgeLabelClass,
|
|
153
|
+
children: edge.label
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
] }, edge.id);
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
nodes.map((node) => {
|
|
161
|
+
const isSelected = node.selected || node.id === selectedId;
|
|
162
|
+
const w = node.width ?? 160;
|
|
163
|
+
const h = node.height ?? 48;
|
|
164
|
+
return /* @__PURE__ */ jsx(
|
|
165
|
+
"div",
|
|
166
|
+
{
|
|
167
|
+
role: "button",
|
|
168
|
+
tabIndex: 0,
|
|
169
|
+
"aria-pressed": isSelected,
|
|
170
|
+
"aria-label": node.label,
|
|
171
|
+
className: flowNodeVariants({
|
|
172
|
+
selected: isSelected ? "true" : "false"
|
|
173
|
+
}),
|
|
174
|
+
style: { left: node.x, top: node.y, width: w, height: h },
|
|
175
|
+
onClick: () => onNodeClick?.(node.id),
|
|
176
|
+
onKeyDown: (e) => {
|
|
177
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
178
|
+
e.preventDefault();
|
|
179
|
+
onNodeClick?.(node.id);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
onPointerDown: (e) => handlePointerDown(e, node),
|
|
183
|
+
onPointerMove: handlePointerMove,
|
|
184
|
+
onPointerUp: handlePointerUp,
|
|
185
|
+
children: renderNode ? renderNode(node) : /* @__PURE__ */ jsx("span", { className: flowNodeLabelClass, children: node.label })
|
|
186
|
+
},
|
|
187
|
+
node.id
|
|
188
|
+
);
|
|
189
|
+
})
|
|
190
|
+
] })
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
export {
|
|
196
|
+
FlowEditor
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=react-flow-editor.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../flow-editor/src/flow-editor.ts", "../../flow-editor/src/flow-editor.styles.ts", "../../react-flow-editor/src/flow-editor.tsx"],
|
|
4
|
+
"sourcesContent": ["/** A node in the flow editor canvas. */\nexport type FlowNode = {\n id: string\n x: number\n y: number\n width?: number\n height?: number\n label: string\n selected?: boolean\n}\n\n/** A directed edge connecting two nodes. */\nexport type FlowEdge = {\n id: string\n source: string\n target: string\n label?: string\n}\n\n/** Default node dimensions used when width/height are not specified. */\nconst DEFAULT_NODE_WIDTH = 160\nconst DEFAULT_NODE_HEIGHT = 48\n\n/**\n * Compute the center point of a node in canvas coordinates.\n * Uses the node's explicit dimensions when available, otherwise falls back to\n * the default box size.\n */\nexport function nodeCenter(node: FlowNode): { x: number; y: number } {\n const w = node.width ?? DEFAULT_NODE_WIDTH\n const h = node.height ?? DEFAULT_NODE_HEIGHT\n return { x: node.x + w / 2, y: node.y + h / 2 }\n}\n\n/**\n * Find a node by id. Returns `undefined` when not found; callers must guard.\n */\nexport function nodeById(\n nodes: FlowNode[],\n id: string,\n): FlowNode | undefined {\n return nodes.find((n) => n.id === id)\n}\n\n/**\n * Produce an SVG cubic-bezier path `d` string between the anchor points of two\n * nodes. The control handles extend horizontally from each anchor so the curve\n * is smooth regardless of relative position — matching the easyloops\n * architecture-editor style.\n *\n * Source anchor: right-center of the source node.\n * Target anchor: left-center of the target node.\n */\nexport function edgePath(source: FlowNode, target: FlowNode): string {\n const sw = source.width ?? DEFAULT_NODE_WIDTH\n const sh = source.height ?? DEFAULT_NODE_HEIGHT\n const th = target.height ?? DEFAULT_NODE_HEIGHT\n\n const x1 = source.x + sw\n const y1 = source.y + sh / 2\n const x2 = target.x\n const y2 = target.y + th / 2\n\n const dx = Math.abs(x2 - x1)\n const handleLen = Math.max(dx * 0.5, 40)\n\n const cx1 = x1 + handleLen\n const cy1 = y1\n const cx2 = x2 - handleLen\n const cy2 = y2\n\n return `M ${x1} ${y1} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${x2} ${y2}`\n}\n\n/**\n * Compute the bounding box that contains all nodes. Useful for fitting the\n * viewport to content. Returns zeros when the node list is empty.\n */\nexport function contentBounds(nodes: FlowNode[]): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n if (nodes.length === 0) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const node of nodes) {\n const w = node.width ?? DEFAULT_NODE_WIDTH\n const h = node.height ?? DEFAULT_NODE_HEIGHT\n if (node.x < minX) minX = node.x\n if (node.y < minY) minY = node.y\n if (node.x + w > maxX) maxX = node.x + w\n if (node.y + h > maxY) maxY = node.y + h\n }\n\n return { minX, minY, maxX, maxY }\n}\n\n/** Accessibility and data attributes for the root flow editor container. */\nexport interface FlowEditorAPI {\n ariaProps: Record<string, string | number | boolean>\n dataAttributes: Record<string, string>\n}\n\n/**\n * Return the framework-agnostic root attributes for a FlowEditor.\n * Adapters spread `ariaProps` and `dataAttributes` on the outermost element.\n */\nexport function createFlowEditor(): FlowEditorAPI {\n return {\n ariaProps: {\n role: 'group',\n 'aria-label': 'Flow editor',\n },\n dataAttributes: {\n 'data-flow-editor': 'true',\n },\n }\n}\n", "import { cva } from '@refraction-ui/shared'\n\n/**\n * Root wrapper for the flow editor canvas. Sized to fill its container and\n * provides a relative positioning context for the node/edge layers.\n */\nexport const flowEditorVariants = cva({\n base: 'relative w-full overflow-hidden bg-background border border-border rounded-lg',\n variants: {\n size: {\n sm: 'h-64',\n md: 'h-96',\n lg: 'h-[32rem]',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n})\n\n/**\n * A single node box. The `selected` variant adds a ring to signal the active\n * selection; semantic token classes are used throughout so theming works\n * without overrides.\n */\nexport const flowNodeVariants = cva({\n base: [\n 'absolute flex items-center justify-center rounded-md border',\n 'bg-card border-border text-card-foreground text-sm font-medium',\n 'cursor-pointer select-none transition-shadow',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n ].join(' '),\n variants: {\n selected: {\n true: 'ring-2 ring-ring ring-primary shadow-md',\n false: 'hover:border-primary/50 hover:shadow-sm',\n },\n },\n defaultVariants: {\n selected: 'false',\n },\n})\n\n/** Accessible label text inside a node box. */\nexport const flowNodeLabelClass = 'px-3 py-2 truncate pointer-events-none'\n\n/**\n * SVG `<path>` element class for an edge. `currentColor` is used so the\n * stroke inherits from `text-muted-foreground` set on the SVG container.\n */\nexport const flowEdgePathClass =\n 'fill-none stroke-current stroke-2 transition-opacity'\n\n/** Text label positioned at the midpoint of an edge. */\nexport const flowEdgeLabelClass =\n 'text-xs fill-muted-foreground select-none pointer-events-none'\n", "import * as React from 'react'\nimport { InfiniteCanvas } from '@refraction-ui/react-infinite-canvas'\nimport {\n createFlowEditor,\n edgePath,\n nodeById,\n flowEditorVariants,\n flowNodeVariants,\n flowNodeLabelClass,\n flowEdgePathClass,\n flowEdgeLabelClass,\n type FlowNode,\n type FlowEdge,\n} from '@refraction-ui/flow-editor'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { FlowNode, FlowEdge }\n\nexport interface FlowEditorProps\n extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onSelect' | 'results' | 'color' | 'content'\n > {\n /** The nodes to render on the canvas. */\n nodes: FlowNode[]\n /** The directed edges connecting nodes. */\n edges: FlowEdge[]\n /** Called when a node is clicked; receives the node's id. */\n onNodeClick?: (id: string) => void\n /**\n * Called when a node has been dragged to a new position.\n * Receives the node id and the new (x, y) canvas coordinates.\n */\n onNodeMove?: (id: string, x: number, y: number) => void\n /** Override the rendering of individual node boxes. */\n renderNode?: (node: FlowNode) => React.ReactNode\n /** Id of the currently selected node (controlled). */\n selectedId?: string\n /** Whether to show the pan/zoom grid. */\n showGrid?: boolean\n /** Whether to show zoom/pan controls. */\n showControls?: boolean\n}\n\n/**\n * FlowEditor — a node-and-edge diagram editor.\n *\n * Renders inside `<InfiniteCanvas>` so the whole graph is pan/zoomable.\n * Edges are drawn as SVG cubic bezier paths behind the node boxes.\n * Node drag is kept deliberately simple (pointer events, no external dep).\n */\nexport const FlowEditor = React.forwardRef<HTMLDivElement, FlowEditorProps>(\n function FlowEditor(\n {\n nodes,\n edges,\n onNodeClick,\n onNodeMove,\n renderNode,\n selectedId,\n showGrid = true,\n showControls = true,\n className,\n ...props\n },\n ref,\n ) {\n const api = createFlowEditor()\n\n // Track pointer-drag state without triggering re-renders mid-drag.\n const dragRef = React.useRef<{\n id: string\n startPointerX: number\n startPointerY: number\n startNodeX: number\n startNodeY: number\n } | null>(null)\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>, node: FlowNode) => {\n if (!onNodeMove) return\n event.currentTarget.setPointerCapture(event.pointerId)\n dragRef.current = {\n id: node.id,\n startPointerX: event.clientX,\n startPointerY: event.clientY,\n startNodeX: node.x,\n startNodeY: node.y,\n }\n },\n [onNodeMove],\n )\n\n const handlePointerMove = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n const drag = dragRef.current\n if (!drag || !onNodeMove) return\n const dx = event.clientX - drag.startPointerX\n const dy = event.clientY - drag.startPointerY\n onNodeMove(drag.id, drag.startNodeX + dx, drag.startNodeY + dy)\n },\n [onNodeMove],\n )\n\n const handlePointerUp = React.useCallback(() => {\n dragRef.current = null\n }, [])\n\n return (\n <div\n ref={ref}\n className={cn(flowEditorVariants(), className)}\n {...(api.ariaProps as React.HTMLAttributes<HTMLDivElement>)}\n {...api.dataAttributes}\n {...props}\n >\n <InfiniteCanvas showGrid={showGrid} showControls={showControls}>\n {/* SVG edge layer — rendered behind nodes */}\n <svg\n className=\"absolute inset-0 w-full h-full overflow-visible text-muted-foreground pointer-events-none\"\n aria-hidden=\"true\"\n >\n {edges.map((edge) => {\n const src = nodeById(nodes, edge.source)\n const tgt = nodeById(nodes, edge.target)\n if (!src || !tgt) return null\n const d = edgePath(src, tgt)\n const midX = (src.x + (src.width ?? 160) + tgt.x) / 2\n const midY = (src.y + (src.height ?? 48) / 2 + tgt.y + (tgt.height ?? 48) / 2) / 2\n return (\n <g key={edge.id}>\n <path d={d} className={flowEdgePathClass} />\n {edge.label != null && (\n <text\n x={midX}\n y={midY - 4}\n textAnchor=\"middle\"\n className={flowEdgeLabelClass}\n >\n {edge.label}\n </text>\n )}\n </g>\n )\n })}\n </svg>\n\n {/* Node layer — positioned absolutely by x/y */}\n {nodes.map((node) => {\n const isSelected = node.selected || node.id === selectedId\n const w = node.width ?? 160\n const h = node.height ?? 48\n return (\n <div\n key={node.id}\n role=\"button\"\n tabIndex={0}\n aria-pressed={isSelected}\n aria-label={node.label}\n className={flowNodeVariants({\n selected: isSelected ? 'true' : 'false',\n })}\n style={{ left: node.x, top: node.y, width: w, height: h }}\n onClick={() => onNodeClick?.(node.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onNodeClick?.(node.id)\n }\n }}\n onPointerDown={(e) => handlePointerDown(e, node)}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n >\n {renderNode ? (\n renderNode(node)\n ) : (\n <span className={flowNodeLabelClass}>{node.label}</span>\n )}\n </div>\n )\n })}\n </InfiniteCanvas>\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["FlowEditor"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn,
|
|
3
|
+
cva
|
|
4
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
5
|
+
|
|
6
|
+
// ../react-footer/dist/index.js
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
// ../footer/dist/index.js
|
|
10
|
+
function createFooter(props = {}) {
|
|
11
|
+
const { copyright } = props;
|
|
12
|
+
const year = props.year ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
13
|
+
const copyrightText = copyright ?? `\xA9 ${year} All rights reserved.`;
|
|
14
|
+
return {
|
|
15
|
+
ariaProps: { role: "contentinfo" },
|
|
16
|
+
copyrightText
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
var footerVariants = cva({
|
|
20
|
+
base: "border-t bg-background py-8"
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// ../react-footer/dist/index.js
|
|
24
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
25
|
+
var Footer = React.forwardRef(
|
|
26
|
+
function Footer2({
|
|
27
|
+
copyright,
|
|
28
|
+
socialLinks = [],
|
|
29
|
+
columns = [],
|
|
30
|
+
className,
|
|
31
|
+
...props
|
|
32
|
+
}, ref) {
|
|
33
|
+
const api = createFooter({ copyright, socialLinks, columns });
|
|
34
|
+
const classes = cn(footerVariants(), className);
|
|
35
|
+
return /* @__PURE__ */ jsx("footer", { ref, className: classes, ...api.ariaProps, ...props, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-7xl px-4", children: [
|
|
36
|
+
columns.length > 0 && /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-8 md:grid-cols-4", children: columns.map((column) => /* @__PURE__ */ jsxs("div", { children: [
|
|
37
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold", children: column.title }),
|
|
38
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-2", children: column.links.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
39
|
+
"a",
|
|
40
|
+
{
|
|
41
|
+
href: link.href,
|
|
42
|
+
className: "text-sm text-muted-foreground transition-colors hover:text-foreground",
|
|
43
|
+
children: link.label
|
|
44
|
+
}
|
|
45
|
+
) }, link.href)) })
|
|
46
|
+
] }, column.title)) }),
|
|
47
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-8", children: [
|
|
48
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: api.copyrightText }),
|
|
49
|
+
socialLinks.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4", children: socialLinks.map((link) => /* @__PURE__ */ jsx(
|
|
50
|
+
"a",
|
|
51
|
+
{
|
|
52
|
+
href: link.href,
|
|
53
|
+
className: "text-muted-foreground transition-colors hover:text-foreground",
|
|
54
|
+
"aria-label": link.label,
|
|
55
|
+
children: link.icon ?? link.label
|
|
56
|
+
},
|
|
57
|
+
link.href
|
|
58
|
+
)) })
|
|
59
|
+
] })
|
|
60
|
+
] }) });
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
export {
|
|
64
|
+
Footer,
|
|
65
|
+
footerVariants
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=react-footer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../footer/src/footer.ts", "../../footer/src/footer.styles.ts", "../../react-footer/src/footer.tsx"],
|
|
4
|
+
"sourcesContent": ["export interface SocialLink {\n label: string\n href: string\n icon?: string\n}\n\nexport interface FooterColumn {\n title: string\n links: { label: string; href: string }[]\n}\n\nexport interface FooterProps {\n copyright?: string\n socialLinks?: SocialLink[]\n columns?: FooterColumn[]\n /** Year for the default copyrightText. Default: ambient current year — inject to avoid year-boundary SSR/CSR mismatch. */\n year?: number\n}\n\nexport interface FooterAPI {\n ariaProps: Record<string, string>\n copyrightText: string\n}\n\nexport function createFooter(props: FooterProps = {}): FooterAPI {\n const { copyright } = props\n // Injection boundary: ambient year is the default; inject `year` so SSR and client agree.\n const year = props.year ?? new Date().getFullYear()\n const copyrightText = copyright ?? `© ${year} All rights reserved.`\n\n return {\n ariaProps: { role: 'contentinfo' },\n copyrightText,\n }\n}\n", "import { cva } from '@refraction-ui/shared'\n\nexport const footerVariants = cva({\n base: 'border-t bg-background py-8',\n})\n", "import * as React from 'react'\nimport {\n createFooter,\n footerVariants,\n type FooterColumn,\n type SocialLink,\n} from '@refraction-ui/footer'\nimport { cn } from '@refraction-ui/shared'\n\nexport interface FooterProps extends React.HTMLAttributes<HTMLElement> {\n /** Custom copyright text */\n copyright?: string\n /** Social media links */\n socialLinks?: SocialLink[]\n /** Footer link columns */\n columns?: FooterColumn[]\n}\n\n/**\n * Footer component — renders a site footer with copyright, social links, and link columns.\n *\n * Uses the headless @refraction-ui/footer core for state and ARIA attributes.\n */\nexport const Footer = React.forwardRef<HTMLElement, FooterProps>(\n function Footer(\n {\n copyright,\n socialLinks = [],\n columns = [],\n className,\n ...props\n },\n ref,\n ) {\n const api = createFooter({ copyright, socialLinks, columns })\n const classes = cn(footerVariants(), className)\n\n return (\n <footer ref={ref} className={classes} {...api.ariaProps} {...props}>\n <div className=\"mx-auto max-w-7xl px-4\">\n {columns.length > 0 && (\n <div className=\"grid grid-cols-2 gap-8 md:grid-cols-4\">\n {columns.map((column) => (\n <div key={column.title}>\n <h3 className=\"mb-3 text-sm font-semibold\">{column.title}</h3>\n <ul className=\"space-y-2\">\n {column.links.map((link) => (\n <li key={link.href}>\n <a\n href={link.href}\n className=\"text-sm text-muted-foreground transition-colors hover:text-foreground\"\n >\n {link.label}\n </a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n )}\n <div className=\"flex items-center justify-between pt-8\">\n <p className=\"text-sm text-muted-foreground\">{api.copyrightText}</p>\n {socialLinks.length > 0 && (\n <div className=\"flex items-center gap-4\">\n {socialLinks.map((link) => (\n <a\n key={link.href}\n href={link.href}\n className=\"text-muted-foreground transition-colors hover:text-foreground\"\n aria-label={link.label}\n >\n {link.icon ?? link.label}\n </a>\n ))}\n </div>\n )}\n </div>\n </div>\n </footer>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;AAwBO,SAAS,aAAa,QAAqB,CAAA,GAAe;AAC/D,QAAM,EAAE,UAAA,IAAc;AAEtB,QAAM,OAAO,MAAM,SAAQ,oBAAI,KAAA,GAAO,YAAA;AACtC,QAAM,gBAAgB,aAAa,QAAK,IAAI;AAE5C,SAAO;IACL,WAAW,EAAE,MAAM,cAAA;IACnB;EAAA;AAEJ;AChCO,IAAM,iBAAiB,IAAI;EAChC,MAAM;AACR,CAAC;;;;ACmBM,IAAM,SAAe;EAC1B,SAASA,QACP;IACE;IACA,cAAc,CAAA;IACd,UAAU,CAAA;IACV;IACA,GAAG;EAAA,GAEL,KACA;AACA,UAAM,MAAM,aAAa,EAAE,WAAW,aAAa,QAAA,CAAS;AAC5D,UAAM,UAAU,GAAG,eAAA,GAAkB,SAAS;AAE9C,WACE,oBAAC,UAAA,EAAO,KAAU,WAAW,SAAU,GAAG,IAAI,WAAY,GAAG,OAC3D,UAAA,qBAAC,OAAA,EAAI,WAAU,0BACZ,UAAA;MAAA,QAAQ,SAAS,KAChB,oBAAC,OAAA,EAAI,WAAU,yCACZ,UAAA,QAAQ,IAAI,CAAC,WACZ,qBAAC,OAAA,EACC,UAAA;QAAA,oBAAC,MAAA,EAAG,WAAU,8BAA8B,UAAA,OAAO,MAAA,CAAM;QACzD,oBAAC,MAAA,EAAG,WAAU,aACX,UAAA,OAAO,MAAM,IAAI,CAAC,SACjB,oBAAC,MAAA,EACC,UAAA;UAAC;UAAA;YACC,MAAM,KAAK;YACX,WAAU;YAET,UAAA,KAAK;UAAA;QAAA,EACR,GANO,KAAK,IAOd,CACD,EAAA,CACH;MAAA,EAAA,GAbQ,OAAO,KAcjB,CACD,EAAA,CACH;MAEF,qBAAC,OAAA,EAAI,WAAU,0CACb,UAAA;QAAA,oBAAC,KAAA,EAAE,WAAU,iCAAiC,UAAA,IAAI,cAAA,CAAc;QAC/D,YAAY,SAAS,KACpB,oBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA,YAAY,IAAI,CAAC,SAChB;UAAC;UAAA;YAEC,MAAM,KAAK;YACX,WAAU;YACV,cAAY,KAAK;YAEhB,UAAA,KAAK,QAAQ,KAAK;UAAA;UALd,KAAK;QAAA,CAOb,EAAA,CACH;MAAA,EAAA,CAEJ;IAAA,EAAA,CACF,EAAA,CACF;EAEJ;AACF;",
|
|
6
|
+
"names": ["Footer"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InfiniteCanvas
|
|
3
|
+
} from "./chunk-AMK4VCBG.js";
|
|
4
|
+
import {
|
|
5
|
+
cn,
|
|
6
|
+
cva
|
|
7
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
8
|
+
|
|
9
|
+
// ../react-graph-view/dist/index.js
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
// ../graph-view/dist/index.js
|
|
13
|
+
function createGraphView() {
|
|
14
|
+
return {
|
|
15
|
+
ariaProps: {
|
|
16
|
+
role: "group",
|
|
17
|
+
"aria-label": "Knowledge graph"
|
|
18
|
+
},
|
|
19
|
+
dataAttributes: {}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function nodeById(nodes, id) {
|
|
23
|
+
return nodes.find((n) => n.id === id);
|
|
24
|
+
}
|
|
25
|
+
function graphEdgePath(source, target) {
|
|
26
|
+
const sx = source.x;
|
|
27
|
+
const sy = source.y;
|
|
28
|
+
const tx = target.x;
|
|
29
|
+
const ty = target.y;
|
|
30
|
+
const dy = Math.abs(ty - sy);
|
|
31
|
+
const offset = Math.max(dy * 0.5, 40);
|
|
32
|
+
return `M ${sx} ${sy} C ${sx} ${sy + offset}, ${tx} ${ty - offset}, ${tx} ${ty}`;
|
|
33
|
+
}
|
|
34
|
+
function summarizeStates(nodes) {
|
|
35
|
+
const counts = {
|
|
36
|
+
mastered: 0,
|
|
37
|
+
"in-progress": 0,
|
|
38
|
+
"not-started": 0,
|
|
39
|
+
highlight: 0
|
|
40
|
+
};
|
|
41
|
+
for (const n of nodes) {
|
|
42
|
+
const s = n.state ?? "not-started";
|
|
43
|
+
counts[s] += 1;
|
|
44
|
+
}
|
|
45
|
+
return counts;
|
|
46
|
+
}
|
|
47
|
+
var graphViewVariants = cva({
|
|
48
|
+
base: "relative w-full overflow-hidden rounded-lg border border-border bg-background",
|
|
49
|
+
variants: {},
|
|
50
|
+
defaultVariants: {}
|
|
51
|
+
});
|
|
52
|
+
var graphNodeVariants = cva({
|
|
53
|
+
base: [
|
|
54
|
+
"absolute inline-flex items-center justify-center rounded-md border px-2 py-1",
|
|
55
|
+
"text-xs font-medium leading-none -translate-x-1/2 -translate-y-1/2",
|
|
56
|
+
"transition-colors focus-visible:outline-none focus-visible:ring-2",
|
|
57
|
+
"focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
58
|
+
"cursor-default select-none"
|
|
59
|
+
].join(" "),
|
|
60
|
+
variants: {
|
|
61
|
+
state: {
|
|
62
|
+
mastered: "border-transparent bg-primary text-primary-foreground",
|
|
63
|
+
"in-progress": "border-transparent bg-accent text-accent-foreground ring-2 ring-ring",
|
|
64
|
+
"not-started": "border-transparent bg-muted text-muted-foreground",
|
|
65
|
+
highlight: "border-primary bg-background text-foreground"
|
|
66
|
+
},
|
|
67
|
+
interactive: {
|
|
68
|
+
true: "cursor-pointer hover:opacity-90",
|
|
69
|
+
false: "cursor-default"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
defaultVariants: {
|
|
73
|
+
state: "not-started",
|
|
74
|
+
interactive: "false"
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
var graphNodeLabelClass = "pointer-events-none whitespace-nowrap";
|
|
78
|
+
var graphEdgeClass = "fill-none stroke-border stroke-[1.5] opacity-60";
|
|
79
|
+
var graphLegendChipVariants = cva({
|
|
80
|
+
base: "inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium",
|
|
81
|
+
variants: {
|
|
82
|
+
state: {
|
|
83
|
+
mastered: "border-transparent bg-primary text-primary-foreground",
|
|
84
|
+
"in-progress": "border-transparent bg-accent text-accent-foreground ring-1 ring-ring",
|
|
85
|
+
"not-started": "border-transparent bg-muted text-muted-foreground",
|
|
86
|
+
highlight: "border-primary bg-background text-foreground"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
defaultVariants: {
|
|
90
|
+
state: "not-started"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ../react-graph-view/dist/index.js
|
|
95
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
96
|
+
var STATE_LABELS = {
|
|
97
|
+
mastered: "Mastered",
|
|
98
|
+
"in-progress": "In Progress",
|
|
99
|
+
"not-started": "Not Started",
|
|
100
|
+
highlight: "Highlight"
|
|
101
|
+
};
|
|
102
|
+
var LEGEND_ORDER = ["mastered", "in-progress", "not-started", "highlight"];
|
|
103
|
+
var GraphView = React.forwardRef(
|
|
104
|
+
function GraphView2({ nodes, edges, onNodeClick, showLegend = false, className, ...props }, ref) {
|
|
105
|
+
const api = createGraphView();
|
|
106
|
+
const summary = React.useMemo(() => summarizeStates(nodes), [nodes]);
|
|
107
|
+
const nodeMap = React.useMemo(() => {
|
|
108
|
+
const m = /* @__PURE__ */ new Map();
|
|
109
|
+
for (const n of nodes) m.set(n.id, n);
|
|
110
|
+
return m;
|
|
111
|
+
}, [nodes]);
|
|
112
|
+
const padding = 80;
|
|
113
|
+
const xs = nodes.map((n) => n.x);
|
|
114
|
+
const ys = nodes.map((n) => n.y);
|
|
115
|
+
const svgWidth = nodes.length === 0 ? 400 : Math.max(...xs) + padding * 2;
|
|
116
|
+
const svgHeight = nodes.length === 0 ? 300 : Math.max(...ys) + padding * 2;
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
ref,
|
|
121
|
+
className: cn(graphViewVariants(), className),
|
|
122
|
+
...api.ariaProps,
|
|
123
|
+
...api.dataAttributes,
|
|
124
|
+
...props,
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsxs(InfiniteCanvas, { showGrid: true, showControls: true, children: [
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
"svg",
|
|
129
|
+
{
|
|
130
|
+
width: svgWidth,
|
|
131
|
+
height: svgHeight,
|
|
132
|
+
"aria-hidden": "true",
|
|
133
|
+
style: { position: "absolute", top: 0, left: 0, pointerEvents: "none" },
|
|
134
|
+
children: edges.map((edge) => {
|
|
135
|
+
const source = nodeMap.get(edge.source) ?? nodeById(nodes, edge.source);
|
|
136
|
+
const target = nodeMap.get(edge.target) ?? nodeById(nodes, edge.target);
|
|
137
|
+
if (!source || !target) return null;
|
|
138
|
+
return /* @__PURE__ */ jsx(
|
|
139
|
+
"path",
|
|
140
|
+
{
|
|
141
|
+
d: graphEdgePath(source, target),
|
|
142
|
+
className: graphEdgeClass
|
|
143
|
+
},
|
|
144
|
+
edge.id
|
|
145
|
+
);
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
),
|
|
149
|
+
nodes.map((node) => {
|
|
150
|
+
const state = node.state ?? "not-started";
|
|
151
|
+
const interactive = onNodeClick !== void 0;
|
|
152
|
+
return /* @__PURE__ */ jsx(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
type: "button",
|
|
156
|
+
"aria-label": node.label,
|
|
157
|
+
"data-state": state,
|
|
158
|
+
className: cn(
|
|
159
|
+
graphNodeVariants({ state, interactive: interactive ? "true" : "false" })
|
|
160
|
+
),
|
|
161
|
+
style: { left: node.x, top: node.y },
|
|
162
|
+
onClick: onNodeClick ? () => onNodeClick(node) : void 0,
|
|
163
|
+
children: /* @__PURE__ */ jsx("span", { className: graphNodeLabelClass, children: node.label })
|
|
164
|
+
},
|
|
165
|
+
node.id
|
|
166
|
+
);
|
|
167
|
+
})
|
|
168
|
+
] }),
|
|
169
|
+
showLegend && /* @__PURE__ */ jsx(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
role: "list",
|
|
173
|
+
"aria-label": "Graph legend",
|
|
174
|
+
className: "absolute bottom-3 left-3 flex flex-wrap gap-1.5",
|
|
175
|
+
children: LEGEND_ORDER.filter((s) => summary[s] > 0).map((s) => /* @__PURE__ */ jsxs(
|
|
176
|
+
"span",
|
|
177
|
+
{
|
|
178
|
+
role: "listitem",
|
|
179
|
+
className: graphLegendChipVariants({ state: s }),
|
|
180
|
+
children: [
|
|
181
|
+
STATE_LABELS[s],
|
|
182
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-70", children: summary[s] })
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
s
|
|
186
|
+
))
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
export {
|
|
195
|
+
GraphView
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=react-graph-view.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../graph-view/src/graph-view.ts", "../../graph-view/src/graph-view.styles.ts", "../../react-graph-view/src/graph-view.tsx"],
|
|
4
|
+
"sourcesContent": ["/** Visual mastery state of a node in the knowledge graph. */\nexport type GraphNodeState = 'mastered' | 'in-progress' | 'not-started' | 'highlight'\n\n/** A concept node in the knowledge / dependency graph. */\nexport interface GraphNode {\n /** Unique identifier. */\n id: string\n /** X position in the graph canvas (pixels). */\n x: number\n /** Y position in the graph canvas (pixels). */\n y: number\n /** Human-readable label shown in the node chip. */\n label: string\n /** Visual mastery state. Defaults to `'not-started'`. */\n state?: GraphNodeState\n}\n\n/** A directed edge connecting two nodes. */\nexport interface GraphEdge {\n /** Unique identifier. */\n id: string\n /** ID of the source node. */\n source: string\n /** ID of the target node. */\n target: string\n}\n\n/** Axis-aligned bounding box of a set of nodes. */\nexport interface GraphBounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n width: number\n height: number\n}\n\n/** Per-state counts returned by {@link summarizeStates}. */\nexport type GraphStateSummary = Record<GraphNodeState, number>\n\nexport interface GraphViewAPI {\n /** ARIA/role attributes to spread on the graph container. */\n ariaProps: { role: string; 'aria-label': string }\n /** Data attributes for styling hooks. */\n dataAttributes: Record<string, string>\n}\n\n/**\n * Build framework-agnostic container props for a GraphView.\n *\n * The graph is a read-only knowledge / dependency visualisation. It uses\n * `role=\"group\"` (not `role=\"img\"`) so that interactive node chips inside it\n * remain in the accessibility tree and keyboard reachable.\n */\nexport function createGraphView(): GraphViewAPI {\n return {\n ariaProps: {\n role: 'group',\n 'aria-label': 'Knowledge graph',\n },\n dataAttributes: {},\n }\n}\n\n/**\n * Look up a node by id. Returns `undefined` when not found.\n */\nexport function nodeById(nodes: GraphNode[], id: string): GraphNode | undefined {\n return nodes.find((n) => n.id === id)\n}\n\n/**\n * Build an SVG cubic-bezier path `d` string between two node positions.\n *\n * The control points bend the curve vertically so edges arc gracefully between\n * horizontally or vertically offset nodes. Works for any pair of positions.\n */\nexport function graphEdgePath(source: GraphNode, target: GraphNode): string {\n const sx = source.x\n const sy = source.y\n const tx = target.x\n const ty = target.y\n\n // Control-point offset: half the vertical distance, minimum 40px.\n const dy = Math.abs(ty - sy)\n const offset = Math.max(dy * 0.5, 40)\n\n return `M ${sx} ${sy} C ${sx} ${sy + offset}, ${tx} ${ty - offset}, ${tx} ${ty}`\n}\n\n/**\n * Compute the axis-aligned bounding box of a set of nodes.\n *\n * Returns zero-area bounds centred at the origin when the array is empty.\n */\nexport function graphBounds(nodes: GraphNode[]): GraphBounds {\n if (nodes.length === 0) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0, width: 0, height: 0 }\n }\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const n of nodes) {\n if (n.x < minX) minX = n.x\n if (n.y < minY) minY = n.y\n if (n.x > maxX) maxX = n.x\n if (n.y > maxY) maxY = n.y\n }\n return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY }\n}\n\n/**\n * Count nodes per mastery state.\n *\n * Nodes without an explicit `state` are counted as `'not-started'`.\n */\nexport function summarizeStates(nodes: GraphNode[]): GraphStateSummary {\n const counts: GraphStateSummary = {\n mastered: 0,\n 'in-progress': 0,\n 'not-started': 0,\n highlight: 0,\n }\n for (const n of nodes) {\n const s: GraphNodeState = n.state ?? 'not-started'\n counts[s] += 1\n }\n return counts\n}\n", "import { cva } from '@refraction-ui/shared'\n\n/** Outer wrapper for the graph canvas area. */\nexport const graphViewVariants = cva({\n base: 'relative w-full overflow-hidden rounded-lg border border-border bg-background',\n variants: {},\n defaultVariants: {},\n})\n\n/**\n * Node chip: a positioned, clickable concept node colored by mastery state.\n *\n * `state` variant drives the visual treatment:\n * - mastered → filled primary (bg-primary / text-primary-foreground)\n * - in-progress → accent ring (ring-2 ring-ring bg-accent text-accent-foreground)\n * - not-started → muted (bg-muted text-muted-foreground)\n * - highlight → border-primary accent outline (border-primary bg-background text-foreground)\n */\nexport const graphNodeVariants = cva({\n base: [\n 'absolute inline-flex items-center justify-center rounded-md border px-2 py-1',\n 'text-xs font-medium leading-none -translate-x-1/2 -translate-y-1/2',\n 'transition-colors focus-visible:outline-none focus-visible:ring-2',\n 'focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'cursor-default select-none',\n ].join(' '),\n variants: {\n state: {\n mastered: 'border-transparent bg-primary text-primary-foreground',\n 'in-progress': 'border-transparent bg-accent text-accent-foreground ring-2 ring-ring',\n 'not-started': 'border-transparent bg-muted text-muted-foreground',\n highlight: 'border-primary bg-background text-foreground',\n },\n interactive: {\n true: 'cursor-pointer hover:opacity-90',\n false: 'cursor-default',\n },\n },\n defaultVariants: {\n state: 'not-started',\n interactive: 'false',\n },\n})\n\n/** Label text rendered inside each node chip (already embedded in the chip). */\nexport const graphNodeLabelClass = 'pointer-events-none whitespace-nowrap'\n\n/** SVG edge line/path classes. */\nexport const graphEdgeClass =\n 'fill-none stroke-border stroke-[1.5] opacity-60'\n\n/** Legend chip: a small colored indicator + label for a single mastery state. */\nexport const graphLegendChipVariants = cva({\n base: 'inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium',\n variants: {\n state: {\n mastered: 'border-transparent bg-primary text-primary-foreground',\n 'in-progress': 'border-transparent bg-accent text-accent-foreground ring-1 ring-ring',\n 'not-started': 'border-transparent bg-muted text-muted-foreground',\n highlight: 'border-primary bg-background text-foreground',\n },\n },\n defaultVariants: {\n state: 'not-started',\n },\n})\n", "import * as React from 'react'\nimport { InfiniteCanvas } from '@refraction-ui/react-infinite-canvas'\nimport {\n createGraphView,\n graphEdgePath,\n nodeById,\n summarizeStates,\n graphNodeVariants,\n graphNodeLabelClass,\n graphEdgeClass,\n graphLegendChipVariants,\n graphViewVariants,\n type GraphNode,\n type GraphEdge,\n type GraphNodeState,\n} from '@refraction-ui/graph-view'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { GraphNode, GraphEdge, GraphNodeState }\n\n/** Labels shown in the legend for each mastery state. */\nconst STATE_LABELS: Record<GraphNodeState, string> = {\n mastered: 'Mastered',\n 'in-progress': 'In Progress',\n 'not-started': 'Not Started',\n highlight: 'Highlight',\n}\n\n/** Order in which legend states are rendered. */\nconst LEGEND_ORDER: GraphNodeState[] = ['mastered', 'in-progress', 'not-started', 'highlight']\n\nexport interface GraphViewProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect' | 'color' | 'content' | 'results'> {\n /** Nodes to render. */\n nodes: GraphNode[]\n /** Directed edges connecting nodes. */\n edges: GraphEdge[]\n /** Called when a node chip is clicked (read-only — no selection state). */\n onNodeClick?: (node: GraphNode) => void\n /** Show a legend summarising mastery-state counts. */\n showLegend?: boolean\n}\n\n/**\n * GraphView — a read-only knowledge / dependency graph rendered inside an\n * InfiniteCanvas (pan + zoom). Concept nodes are coloured by mastery state;\n * edges are SVG curves. An optional legend shows per-state node counts.\n *\n * The component is intentionally read-only: clicking a node fires `onNodeClick`\n * but never mutates the `nodes` array — the caller owns the data.\n */\nexport const GraphView = React.forwardRef<HTMLDivElement, GraphViewProps>(\n function GraphView({ nodes, edges, onNodeClick, showLegend = false, className, ...props }, ref) {\n const api = createGraphView()\n const summary = React.useMemo(() => summarizeStates(nodes), [nodes])\n\n // Build a lookup map for resolving edge endpoints.\n const nodeMap = React.useMemo(() => {\n const m = new Map<string, GraphNode>()\n for (const n of nodes) m.set(n.id, n)\n return m\n }, [nodes])\n\n // Estimate SVG viewport from node extents (with padding).\n const padding = 80\n const xs = nodes.map((n) => n.x)\n const ys = nodes.map((n) => n.y)\n const svgWidth = nodes.length === 0 ? 400 : Math.max(...xs) + padding * 2\n const svgHeight = nodes.length === 0 ? 300 : Math.max(...ys) + padding * 2\n\n return (\n <div\n ref={ref}\n className={cn(graphViewVariants(), className)}\n {...api.ariaProps}\n {...api.dataAttributes}\n {...props}\n >\n <InfiniteCanvas showGrid showControls>\n {/* SVG layer for edges */}\n <svg\n width={svgWidth}\n height={svgHeight}\n aria-hidden=\"true\"\n style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}\n >\n {edges.map((edge) => {\n const source = nodeMap.get(edge.source) ?? nodeById(nodes, edge.source)\n const target = nodeMap.get(edge.target) ?? nodeById(nodes, edge.target)\n if (!source || !target) return null\n return (\n <path\n key={edge.id}\n d={graphEdgePath(source, target)}\n className={graphEdgeClass}\n />\n )\n })}\n </svg>\n\n {/* Node chips positioned absolutely */}\n {nodes.map((node) => {\n const state: GraphNodeState = node.state ?? 'not-started'\n const interactive = onNodeClick !== undefined\n return (\n <button\n key={node.id}\n type=\"button\"\n aria-label={node.label}\n data-state={state}\n className={cn(\n graphNodeVariants({ state, interactive: interactive ? 'true' : 'false' }),\n )}\n style={{ left: node.x, top: node.y }}\n onClick={onNodeClick ? () => onNodeClick(node) : undefined}\n >\n <span className={graphNodeLabelClass}>{node.label}</span>\n </button>\n )\n })}\n </InfiniteCanvas>\n\n {/* Optional legend */}\n {showLegend && (\n <div\n role=\"list\"\n aria-label=\"Graph legend\"\n className=\"absolute bottom-3 left-3 flex flex-wrap gap-1.5\"\n >\n {LEGEND_ORDER.filter((s) => summary[s] > 0).map((s) => (\n <span\n key={s}\n role=\"listitem\"\n className={graphLegendChipVariants({ state: s })}\n >\n {STATE_LABELS[s]}\n <span className=\"opacity-70\">{summary[s]}</span>\n </span>\n ))}\n </div>\n )}\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["GraphView"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../react-icon-system/src/react-icon-system.tsx"],
|
|
4
|
+
"sourcesContent": ["import React from \"react\";\nexport const IconSystem = () => { return <div></div>; };\n"],
|
|
5
|
+
"mappings": ";;AACO,IAAM,aAAa,MAAM;AAAE,SAAA,oBAAQ,OAAA,CAAA,CAAI;AAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|