@flikk/ui 1.0.0-beta.27 → 1.0.0-beta.29
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/bg/1.webp +0 -0
- package/dist/bg/10.webp +0 -0
- package/dist/bg/11.webp +0 -0
- package/dist/bg/12.webp +0 -0
- package/dist/bg/13.webp +0 -0
- package/dist/bg/14.webp +0 -0
- package/dist/bg/15.webp +0 -0
- package/dist/bg/16.webp +0 -0
- package/dist/bg/17.webp +0 -0
- package/dist/bg/18.webp +0 -0
- package/dist/bg/19.webp +0 -0
- package/dist/bg/2.webp +0 -0
- package/dist/bg/20.webp +0 -0
- package/dist/bg/21.webp +0 -0
- package/dist/bg/22.webp +0 -0
- package/dist/bg/3.webp +0 -0
- package/dist/bg/4.webp +0 -0
- package/dist/bg/5.webp +0 -0
- package/dist/bg/6.webp +0 -0
- package/dist/bg/7.webp +0 -0
- package/dist/bg/8.webp +0 -0
- package/dist/bg/9.webp +0 -0
- package/dist/components/ai/CodeBlock/CodeBlock.theme.js +1 -1
- package/dist/components/ai/PromptInput/PromptInput.js +7 -143
- package/dist/components/ai/PromptInput/VoiceRecorder.js +0 -88
- package/dist/components/ai/TokenCounter/TokenCounter.theme.js +1 -1
- package/dist/components/canvas/Background.d.ts +19 -0
- package/dist/components/canvas/Background.js +23 -0
- package/dist/components/canvas/BaseNode.d.ts +15 -0
- package/dist/components/canvas/BaseNode.js +20 -0
- package/dist/components/canvas/CanvasToolbar.d.ts +34 -0
- package/dist/components/canvas/CanvasToolbar.js +34 -0
- package/dist/components/canvas/Edge.d.ts +35 -0
- package/dist/components/canvas/Edge.js +97 -0
- package/dist/components/canvas/EdgeLayer.d.ts +18 -0
- package/dist/components/canvas/EdgeLayer.js +111 -0
- package/dist/components/canvas/Handle.d.ts +11 -0
- package/dist/components/canvas/Handle.js +63 -0
- package/dist/components/canvas/MiniMap.d.ts +22 -0
- package/dist/components/canvas/MiniMap.js +105 -0
- package/dist/components/canvas/NodeCanvas.d.ts +10 -0
- package/dist/components/canvas/NodeCanvas.js +477 -0
- package/dist/components/canvas/NodeCanvas.theme.d.ts +7 -0
- package/dist/components/canvas/NodeCanvas.theme.js +9 -0
- package/dist/components/canvas/NodeCanvas.types.d.ts +187 -0
- package/dist/components/canvas/NodeControls.d.ts +25 -0
- package/dist/components/canvas/NodeControls.js +40 -0
- package/dist/components/canvas/NodeRenderer.d.ts +19 -0
- package/dist/components/canvas/NodeRenderer.js +117 -0
- package/dist/components/canvas/changes.d.ts +11 -0
- package/dist/components/canvas/changes.js +76 -0
- package/dist/components/canvas/context/CanvasContext.d.ts +32 -0
- package/dist/components/canvas/context/CanvasContext.js +14 -0
- package/dist/components/canvas/context/ConnectionContext.d.ts +33 -0
- package/dist/components/canvas/context/ConnectionContext.js +13 -0
- package/dist/components/canvas/context/HandleRegistry.d.ts +24 -0
- package/dist/components/canvas/context/HandleRegistry.js +15 -0
- package/dist/components/canvas/context/InteractionContext.d.ts +18 -0
- package/dist/components/canvas/context/InteractionContext.js +14 -0
- package/dist/components/canvas/context/NodeIdContext.d.ts +3 -0
- package/dist/components/canvas/context/NodeIdContext.js +15 -0
- package/dist/components/canvas/hooks/useConnection.d.ts +17 -0
- package/dist/components/canvas/hooks/useConnection.js +29 -0
- package/dist/components/canvas/hooks/useMarquee.d.ts +25 -0
- package/dist/components/canvas/hooks/useMarquee.js +91 -0
- package/dist/components/canvas/hooks/useNodeDrag.d.ts +23 -0
- package/dist/components/canvas/hooks/useNodeDrag.js +110 -0
- package/dist/components/canvas/hooks/usePanZoom.d.ts +30 -0
- package/dist/components/canvas/hooks/usePanZoom.js +189 -0
- package/dist/components/canvas/hooks/useViewport.d.ts +21 -0
- package/dist/components/canvas/hooks/useViewport.js +35 -0
- package/dist/components/canvas/index.d.ts +14 -0
- package/dist/components/canvas/index.js +12 -0
- package/dist/components/canvas/layout.d.ts +22 -0
- package/dist/components/canvas/layout.js +67 -0
- package/dist/components/canvas/utils.d.ts +4 -0
- package/dist/components/canvas/utils.js +16 -0
- package/dist/components/charts/AreaChart/AreaChart.js +136 -101
- package/dist/components/charts/AreaChart/AreaChart.types.d.ts +5 -5
- package/dist/components/charts/AreaChart/AreaChart.types.js +1 -0
- package/dist/components/charts/BarChart/BarChart.js +207 -143
- package/dist/components/charts/BarChart/BarChart.types.d.ts +4 -4
- package/dist/components/charts/BarChart/BarChart.types.js +1 -0
- package/dist/components/charts/ComboChart/ComboChart.types.d.ts +2 -2
- package/dist/components/charts/LineChart/LineChart.js +88 -72
- package/dist/components/charts/LineChart/LineChart.types.d.ts +3 -3
- package/dist/components/charts/RadarChart/RadarChart.js +1 -1
- package/dist/components/charts/RadarChart/RadarChart.theme.js +5 -2
- package/dist/components/charts/ScatterPlot/ScatterPlot.js +62 -72
- package/dist/components/charts/ScatterPlot/ScatterPlot.theme.js +10 -1
- package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +3 -9
- package/dist/components/charts/StackedBarChart/StackedBarChart.js +33 -33
- package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +2 -2
- package/dist/components/charts/hooks/index.d.ts +2 -0
- package/dist/components/charts/hooks/useChartDimensions.d.ts +29 -0
- package/dist/components/charts/hooks/useChartDimensions.js +42 -0
- package/dist/components/charts/shared/ChartTooltip/ChartTooltip.theme.js +1 -1
- package/dist/components/charts/types/chart.types.d.ts +11 -2
- package/dist/components/charts/types/chart.types.js +10 -3
- package/dist/components/core/Avatar/Avatar.js +5 -1
- package/dist/components/core/AvatarGroup/AvatarGroup.js +5 -1
- package/dist/components/core/AvatarGroup/AvatarGroup.theme.js +4 -2
- package/dist/components/core/Badge/Badge.js +1 -1
- package/dist/components/core/Badge/Badge.theme.js +16 -15
- package/dist/components/core/Button/Button.js +1 -1
- package/dist/components/core/Button/Button.theme.js +2 -2
- package/dist/components/core/Calendar/Calendar.theme.js +1 -1
- package/dist/components/core/Card/Card.js +7 -3
- package/dist/components/core/Card/Card.theme.js +3 -3
- package/dist/components/core/Card/Card.types.d.ts +27 -2
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +2 -2
- package/dist/components/core/ContextMenu/ContextMenu.theme.js +2 -2
- package/dist/components/core/ContextMenu/ContextMenuItem.js +1 -1
- package/dist/components/core/ContextMenu/ContextMenuSubTrigger.js +1 -1
- package/dist/components/core/Dropdown/Dropdown.theme.js +3 -3
- package/dist/components/core/Dropdown/DropdownMenu.js +57 -29
- package/dist/components/core/HeroCard/HeroCard.d.ts +5 -0
- package/dist/components/core/HeroCard/HeroCard.js +99 -0
- package/dist/components/core/HeroCard/HeroCard.theme.d.ts +3 -0
- package/dist/components/core/HeroCard/HeroCard.theme.js +8 -0
- package/dist/components/core/HeroCard/HeroCard.types.d.ts +70 -0
- package/dist/components/core/HeroCard/index.d.ts +4 -0
- package/dist/components/core/Kbd/Kbd.theme.js +1 -1
- package/dist/components/core/Modal/Modal.d.ts +1 -1
- package/dist/components/core/Modal/Modal.js +2 -7
- package/dist/components/core/Modal/Modal.theme.js +4 -8
- package/dist/components/core/Modal/Modal.types.d.ts +0 -10
- package/dist/components/core/Modal/index.d.ts +1 -1
- package/dist/components/core/ModalStack/ModalStack.d.ts +1 -1
- package/dist/components/core/ModalStack/ModalStack.types.d.ts +1 -2
- package/dist/components/core/ModalStack/ModalStackModal.d.ts +1 -1
- package/dist/components/core/ModalStack/ModalStackModal.js +2 -7
- package/dist/components/core/PageHeading/PageHeading.theme.js +2 -2
- package/dist/components/core/Popover/Popover.theme.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +242 -4
- package/dist/components/core/ScrollArea/ScrollArea.types.d.ts +12 -1
- package/dist/components/core/ScrollArea/smooth/SmoothScrollEngine.d.ts +4 -0
- package/dist/components/core/ScrollArea/smooth/SmoothScrollEngine.js +23 -0
- package/dist/components/core/ScrollArea/smooth/useSmoothScroll.js +4 -0
- package/dist/components/core/ScrollArea/smooth/useSmoothScroll.types.d.ts +5 -0
- package/dist/components/core/Segmented/Segmented.theme.js +2 -2
- package/dist/components/core/Tooltip/Tooltip.theme.js +1 -1
- package/dist/components/core/index.d.ts +1 -0
- package/dist/components/core/index.js +2 -0
- package/dist/components/data-display/DescriptionList/DescriptionList.js +23 -7
- package/dist/components/data-display/DescriptionList/DescriptionList.theme.js +10 -3
- package/dist/components/data-display/DescriptionList/DescriptionList.types.d.ts +49 -10
- package/dist/components/data-display/DescriptionList/index.d.ts +1 -1
- package/dist/components/data-display/Feed/Feed.js +28 -5
- package/dist/components/data-display/Feed/Feed.theme.js +1 -1
- package/dist/components/data-display/Feed/Feed.types.d.ts +32 -1
- package/dist/components/data-display/Feed/index.d.ts +1 -1
- package/dist/components/data-display/Metric/Metric.js +8 -8
- package/dist/components/data-display/Metric/Metric.theme.d.ts +3 -2
- package/dist/components/data-display/Metric/Metric.theme.js +50 -68
- package/dist/components/data-display/Metric/Metric.types.d.ts +19 -21
- package/dist/components/data-display/Table/Table.js +2 -2
- package/dist/components/data-display/Table/Table.theme.js +24 -4
- package/dist/components/data-display/Table/Table.types.d.ts +8 -0
- package/dist/components/data-display/Table/TableBody.js +2 -2
- package/dist/components/data-display/Table/TableCell.js +1 -1
- package/dist/components/data-display/Table/TableRow.d.ts +1 -1
- package/dist/components/data-display/Table/TableRow.js +9 -2
- package/dist/components/data-display/Timeline/Timeline.js +27 -5
- package/dist/components/data-display/Timeline/Timeline.theme.d.ts +10 -2
- package/dist/components/data-display/Timeline/Timeline.theme.js +14 -8
- package/dist/components/data-display/Timeline/Timeline.types.d.ts +48 -16
- package/dist/components/data-display/Timeline/Timeline.utils.d.ts +6 -0
- package/dist/components/data-display/Timeline/Timeline.utils.js +11 -0
- package/dist/components/data-display/Timeline/TimelineItem.js +13 -20
- package/dist/components/data-display/Timeline/TimelineMarker.js +10 -8
- package/dist/components/data-display/Timeline/index.d.ts +2 -2
- package/dist/components/effects/3d/index.d.ts +0 -2
- package/dist/components/effects/3d/index.js +0 -1
- package/dist/components/effects/DotPattern/DotPattern.js +1 -1
- package/dist/components/effects/Overlay/Overlay.theme.js +1 -2
- package/dist/components/effects/Preloader/Preloader.animations.d.ts +23 -0
- package/dist/components/effects/Preloader/Preloader.animations.js +150 -0
- package/dist/components/effects/Preloader/Preloader.d.ts +3 -0
- package/dist/components/effects/Preloader/Preloader.js +119 -0
- package/dist/components/effects/Preloader/Preloader.types.d.ts +63 -0
- package/dist/components/effects/Preloader/index.d.ts +2 -0
- package/dist/components/effects/ProgressiveBlur/ProgressiveBlur.js +25 -14
- package/dist/components/effects/ProgressiveBlur/ProgressiveBlur.types.d.ts +2 -0
- package/dist/components/effects/TiltCard/TiltCard.d.ts +12 -0
- package/dist/components/effects/TiltCard/TiltCard.glow.d.ts +28 -0
- package/dist/components/effects/TiltCard/TiltCard.glow.js +114 -0
- package/dist/components/effects/TiltCard/TiltCard.js +177 -0
- package/dist/components/effects/{SpotlightBorder/SpotlightBorder.shaders.d.ts → TiltCard/TiltCard.shaders.d.ts} +1 -1
- package/dist/components/effects/{SpotlightBorder/SpotlightBorder.shaders.js → TiltCard/TiltCard.shaders.js} +1 -1
- package/dist/components/effects/TiltCard/TiltCard.types.d.ts +88 -0
- package/dist/components/effects/TiltCard/index.d.ts +2 -0
- package/dist/components/effects/index.d.ts +4 -12
- package/dist/components/effects/index.js +2 -6
- package/dist/components/forms/Checkbox/Checkbox.js +7 -1
- package/dist/components/forms/DatePicker/DatePicker.theme.js +2 -2
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +1 -1
- package/dist/components/forms/FileUpload/FileUpload.js +1 -89
- package/dist/components/forms/FormLabel/FormLabel.theme.js +1 -1
- package/dist/components/forms/RichTextEditor/RichTextEditor.theme.js +2 -2
- package/dist/components/forms/Slider/Slider.theme.js +1 -1
- package/dist/components/forms/Switch/Switch.js +27 -15
- package/dist/components/forms/Switch/Switch.theme.d.ts +36 -6
- package/dist/components/forms/Switch/Switch.theme.js +33 -4
- package/dist/components/forms/Switch/Switch.types.d.ts +12 -2
- package/dist/components/forms/TimePicker/TimePicker.theme.js +1 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +1 -90
- package/dist/components/forms/forms.theme.js +1 -1
- package/dist/components/forms/index.d.ts +0 -4
- package/dist/components/forms/index.js +0 -92
- package/dist/components/generative/GenerativeView.d.ts +21 -0
- package/dist/components/generative/GenerativeView.js +116 -0
- package/dist/components/generative/actions.d.ts +26 -0
- package/dist/components/generative/actions.js +51 -0
- package/dist/components/generative/index.d.ts +11 -0
- package/dist/components/generative/index.js +9 -0
- package/dist/components/generative/registry.d.ts +43 -0
- package/dist/components/generative/registry.js +114 -0
- package/dist/components/generative/resolvers.d.ts +35 -0
- package/dist/components/generative/resolvers.js +93 -0
- package/dist/components/generative/schema.generated.d.ts +2 -0
- package/dist/components/generative/schema.generated.js +1639 -0
- package/dist/components/generative/schemaTypes.d.ts +30 -0
- package/dist/components/generative/streaming.d.ts +39 -0
- package/dist/components/generative/streaming.js +283 -0
- package/dist/components/generative/tools.d.ts +21 -0
- package/dist/components/generative/tools.js +54 -0
- package/dist/components/generative/types.d.ts +41 -0
- package/dist/components/generative/useGenerativeStream.d.ts +37 -0
- package/dist/components/generative/useGenerativeStream.js +36 -0
- package/dist/components/generative/validate.d.ts +24 -0
- package/dist/components/generative/validate.js +259 -0
- package/dist/components/layout/AppShell/AppShell.theme.js +2 -2
- package/dist/components/layout/FormLayout/FormLayout.js +1 -90
- package/dist/components/layout/Grid/Grid.d.ts +3 -0
- package/dist/components/layout/Grid/Grid.js +50 -0
- package/dist/components/layout/Grid/Grid.theme.d.ts +2 -0
- package/dist/components/layout/Grid/Grid.theme.js +35 -0
- package/dist/components/layout/Grid/Grid.types.d.ts +57 -0
- package/dist/components/layout/Grid/index.d.ts +3 -0
- package/dist/components/layout/Section/Section.d.ts +3 -0
- package/dist/components/layout/Section/Section.js +18 -0
- package/dist/components/layout/Section/Section.theme.d.ts +2 -0
- package/dist/components/layout/Section/Section.theme.js +10 -0
- package/dist/components/layout/Section/Section.types.d.ts +34 -0
- package/dist/components/layout/Section/index.d.ts +3 -0
- package/dist/components/layout/Stack/Stack.d.ts +3 -0
- package/dist/components/layout/Stack/Stack.js +19 -0
- package/dist/components/layout/Stack/Stack.theme.d.ts +2 -0
- package/dist/components/layout/Stack/Stack.theme.js +10 -0
- package/dist/components/layout/Stack/Stack.types.d.ts +39 -0
- package/dist/components/layout/Stack/index.d.ts +3 -0
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.js +6 -0
- package/dist/generative.schema.json +1637 -0
- package/dist/index.js +180 -180
- package/dist/registry.json +648 -464
- package/dist/styles.css +1 -1
- package/dist/tools.json +1731 -0
- package/package.json +17 -4
- package/src/global.scss +53 -101
- package/src/styles/theme.css +687 -493
- package/dist/bg/1.jpg +0 -0
- package/dist/bg/10.jpg +0 -0
- package/dist/bg/11.jpg +0 -0
- package/dist/bg/14.jpg +0 -0
- package/dist/bg/15.jpg +0 -0
- package/dist/bg/16.jpg +0 -0
- package/dist/bg/17.jpg +0 -0
- package/dist/bg/18.jpg +0 -0
- package/dist/bg/19.jpg +0 -0
- package/dist/bg/2.jpg +0 -0
- package/dist/bg/20.jpg +0 -0
- package/dist/bg/21.jpg +0 -0
- package/dist/bg/22.jpg +0 -0
- package/dist/bg/23.jpg +0 -0
- package/dist/bg/24.jpg +0 -0
- package/dist/bg/3.jpg +0 -0
- package/dist/bg/4.jpg +0 -0
- package/dist/bg/5.jpg +0 -0
- package/dist/bg/6.jpg +0 -0
- package/dist/bg/7.jpg +0 -0
- package/dist/bg/8.jpg +0 -0
- package/dist/bg/9.jpg +0 -0
- package/dist/components/effects/MorphingText/MorphingText.d.ts +0 -17
- package/dist/components/effects/MorphingText/MorphingText.js +0 -125
- package/dist/components/effects/MorphingText/MorphingText.types.d.ts +0 -18
- package/dist/components/effects/MorphingText/index.d.ts +0 -2
- package/dist/components/effects/PageTransition/PageTransition.animations.d.ts +0 -8
- package/dist/components/effects/PageTransition/PageTransition.animations.js +0 -53
- package/dist/components/effects/PageTransition/PageTransition.d.ts +0 -3
- package/dist/components/effects/PageTransition/PageTransition.js +0 -82
- package/dist/components/effects/PageTransition/PageTransition.types.d.ts +0 -15
- package/dist/components/effects/PageTransition/index.d.ts +0 -2
- package/dist/components/effects/ParallaxSection/ParallaxSection.d.ts +0 -26
- package/dist/components/effects/ParallaxSection/ParallaxSection.js +0 -71
- package/dist/components/effects/ParallaxSection/ParallaxSection.types.d.ts +0 -11
- package/dist/components/effects/ParallaxSection/index.d.ts +0 -2
- package/dist/components/effects/Spotlight/Spotlight.d.ts +0 -36
- package/dist/components/effects/Spotlight/Spotlight.js +0 -112
- package/dist/components/effects/Spotlight/Spotlight.types.d.ts +0 -29
- package/dist/components/effects/Spotlight/index.d.ts +0 -2
- package/dist/components/effects/SpotlightBorder/SpotlightBorder.d.ts +0 -18
- package/dist/components/effects/SpotlightBorder/SpotlightBorder.js +0 -213
- package/dist/components/effects/SpotlightBorder/SpotlightBorder.types.d.ts +0 -61
- package/dist/components/effects/SpotlightBorder/index.d.ts +0 -2
- package/dist/components/effects/StickyScroll/StickyScroll.d.ts +0 -30
- package/dist/components/effects/StickyScroll/StickyScroll.js +0 -128
- package/dist/components/effects/StickyScroll/StickyScroll.types.d.ts +0 -19
- package/dist/components/effects/StickyScroll/index.d.ts +0 -2
- package/dist/components/forms/CronInput/CronInput.d.ts +0 -3
- package/dist/components/forms/CronInput/CronInput.js +0 -113
- package/dist/components/forms/CronInput/CronInput.theme.d.ts +0 -6
- package/dist/components/forms/CronInput/CronInput.theme.js +0 -75
- package/dist/components/forms/CronInput/CronInput.types.d.ts +0 -109
- package/dist/components/forms/CronInput/CronInput.utils.d.ts +0 -67
- package/dist/components/forms/CronInput/CronInput.utils.js +0 -505
- package/dist/components/forms/CronInput/index.d.ts +0 -4
- /package/dist/components/effects/{SpotlightBorder/SpotlightBorder.types.js → TiltCard/TiltCard.types.js} +0 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React__default, { useRef, useState, useCallback, useEffect, useMemo } from 'react';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
import { useIsClient } from '../../hooks/useIsClient.js';
|
|
5
|
+
import { CanvasProvider } from './context/CanvasContext.js';
|
|
6
|
+
import { InteractionProvider } from './context/InteractionContext.js';
|
|
7
|
+
import { handleKey, HandleRegistryProvider } from './context/HandleRegistry.js';
|
|
8
|
+
import { ConnectionActionsProvider } from './context/ConnectionContext.js';
|
|
9
|
+
import { useViewport } from './hooks/useViewport.js';
|
|
10
|
+
import { usePanZoom } from './hooks/usePanZoom.js';
|
|
11
|
+
import { useMarquee } from './hooks/useMarquee.js';
|
|
12
|
+
import { NodeRenderer } from './NodeRenderer.js';
|
|
13
|
+
import { EdgeLayer } from './EdgeLayer.js';
|
|
14
|
+
import { applyNodeChanges, applyEdgeChanges } from './changes.js';
|
|
15
|
+
import { nodeCanvasTheme } from './NodeCanvas.theme.js';
|
|
16
|
+
import { clamp, isEditableTarget } from './utils.js';
|
|
17
|
+
|
|
18
|
+
/** Estimated node footprint used by `fitView` until real sizes are measured (issue 4). */
|
|
19
|
+
const ESTIMATED_NODE_SIZE = { width: 180, height: 80 };
|
|
20
|
+
const FIT_VIEW_PADDING = 0.85;
|
|
21
|
+
/**
|
|
22
|
+
* The node-editor canvas: a pannable / zoomable viewport over an unbounded flow
|
|
23
|
+
* space. Renders nodes from `nodes` (via the `nodeTypes` registry) and edges from
|
|
24
|
+
* `edges`, supports drag-to-connect, and exposes everything through change-object
|
|
25
|
+
* callbacks. `children` are pane-level chrome (e.g. `Background`, controls) drawn
|
|
26
|
+
* over the canvas; nodes + edges live in the transformed world.
|
|
27
|
+
*/
|
|
28
|
+
const NodeCanvas = React__default.forwardRef(function NodeCanvas(props, ref) {
|
|
29
|
+
const {
|
|
30
|
+
// data
|
|
31
|
+
nodes: nodesProp, defaultNodes, edges: edgesProp, defaultEdges, nodeTypes, onNodesChange, onEdgesChange, onConnect, onSelectionChange, onNodeClick, onBeforeDelete, isValidConnection,
|
|
32
|
+
// viewport
|
|
33
|
+
viewport: viewportProp, defaultViewport, onViewportChange,
|
|
34
|
+
// config
|
|
35
|
+
minZoom = 0.25, maxZoom = 2, fitViewOnInit = false, panOnScroll = false, zoomOnScroll = true, zoomActivationKey = null, panOnDrag = true, snapToGrid = false, snapGrid: snapGridProp, autoConnect = true, className, theme: themeOverrides, children, ...rest } = props;
|
|
36
|
+
useIsClient(); // SSR guard parity with the rest of the library
|
|
37
|
+
const snapGrid = snapToGrid
|
|
38
|
+
? (snapGridProp !== null && snapGridProp !== void 0 ? snapGridProp : [16, 16])
|
|
39
|
+
: null;
|
|
40
|
+
const snapGridRef = useRef(snapGrid);
|
|
41
|
+
snapGridRef.current = snapGrid;
|
|
42
|
+
const paneRef = useRef(null);
|
|
43
|
+
React__default.useImperativeHandle(ref, () => paneRef.current, []);
|
|
44
|
+
const theme = { ...nodeCanvasTheme, ...(themeOverrides !== null && themeOverrides !== void 0 ? themeOverrides : {}) };
|
|
45
|
+
const { viewport, setViewport, viewportRef } = useViewport({
|
|
46
|
+
controlled: viewportProp,
|
|
47
|
+
defaultViewport,
|
|
48
|
+
onChange: onViewportChange,
|
|
49
|
+
minZoom,
|
|
50
|
+
maxZoom,
|
|
51
|
+
});
|
|
52
|
+
/* ----- node state ----- */
|
|
53
|
+
const nodesControlled = nodesProp !== undefined;
|
|
54
|
+
const [internalNodes, setInternalNodes] = useState(() => defaultNodes !== null && defaultNodes !== void 0 ? defaultNodes : []);
|
|
55
|
+
const nodes = nodesControlled ? nodesProp : internalNodes;
|
|
56
|
+
const nodesRef = useRef(nodes);
|
|
57
|
+
nodesRef.current = nodes;
|
|
58
|
+
const onNodesChangeRef = useRef(onNodesChange);
|
|
59
|
+
onNodesChangeRef.current = onNodesChange;
|
|
60
|
+
const onSelectionChangeRef = useRef(onSelectionChange);
|
|
61
|
+
onSelectionChangeRef.current = onSelectionChange;
|
|
62
|
+
const emitNodeChanges = useCallback((changes) => {
|
|
63
|
+
var _a;
|
|
64
|
+
if (!nodesControlled) {
|
|
65
|
+
setInternalNodes((prev) => applyNodeChanges(changes, prev));
|
|
66
|
+
}
|
|
67
|
+
(_a = onNodesChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onNodesChangeRef, changes);
|
|
68
|
+
}, [nodesControlled]);
|
|
69
|
+
/* ----- edge state ----- */
|
|
70
|
+
const edgesControlled = edgesProp !== undefined;
|
|
71
|
+
const [internalEdges, setInternalEdges] = useState(() => defaultEdges !== null && defaultEdges !== void 0 ? defaultEdges : []);
|
|
72
|
+
const edges = edgesControlled ? edgesProp : internalEdges;
|
|
73
|
+
const edgesRef = useRef(edges);
|
|
74
|
+
edgesRef.current = edges;
|
|
75
|
+
const onEdgesChangeRef = useRef(onEdgesChange);
|
|
76
|
+
onEdgesChangeRef.current = onEdgesChange;
|
|
77
|
+
const onBeforeDeleteRef = useRef(onBeforeDelete);
|
|
78
|
+
onBeforeDeleteRef.current = onBeforeDelete;
|
|
79
|
+
const emitEdgeChanges = useCallback((changes) => {
|
|
80
|
+
var _a;
|
|
81
|
+
if (!edgesControlled) {
|
|
82
|
+
setInternalEdges((prev) => applyEdgeChanges(changes, prev));
|
|
83
|
+
}
|
|
84
|
+
(_a = onEdgesChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onEdgesChangeRef, changes);
|
|
85
|
+
}, [edgesControlled]);
|
|
86
|
+
/* ----- handle registry ----- */
|
|
87
|
+
const handlesRef = useRef(new Map());
|
|
88
|
+
const [handleVersion, setHandleVersion] = useState(0);
|
|
89
|
+
const registerHandle = useCallback((nodeId, handle) => {
|
|
90
|
+
var _a;
|
|
91
|
+
const list = (_a = handlesRef.current.get(nodeId)) !== null && _a !== void 0 ? _a : [];
|
|
92
|
+
const key = handleKey(handle.type, handle.id);
|
|
93
|
+
handlesRef.current.set(nodeId, list
|
|
94
|
+
.filter((h) => handleKey(h.type, h.id) !== key)
|
|
95
|
+
.concat(handle));
|
|
96
|
+
setHandleVersion((v) => v + 1);
|
|
97
|
+
}, []);
|
|
98
|
+
const unregisterHandle = useCallback((nodeId, key) => {
|
|
99
|
+
const list = handlesRef.current.get(nodeId);
|
|
100
|
+
if (!list)
|
|
101
|
+
return;
|
|
102
|
+
const next = list.filter((h) => handleKey(h.type, h.id) !== key);
|
|
103
|
+
if (next.length)
|
|
104
|
+
handlesRef.current.set(nodeId, next);
|
|
105
|
+
else
|
|
106
|
+
handlesRef.current.delete(nodeId);
|
|
107
|
+
setHandleVersion((v) => v + 1);
|
|
108
|
+
}, []);
|
|
109
|
+
/* ----- coordinate transforms ----- */
|
|
110
|
+
const flowToScreen = useCallback((p) => ({
|
|
111
|
+
x: p.x * viewport.zoom + viewport.x,
|
|
112
|
+
y: p.y * viewport.zoom + viewport.y,
|
|
113
|
+
}), [viewport]);
|
|
114
|
+
const screenToFlow = useCallback((p) => ({
|
|
115
|
+
x: (p.x - viewport.x) / viewport.zoom,
|
|
116
|
+
y: (p.y - viewport.y) / viewport.zoom,
|
|
117
|
+
}), [viewport]);
|
|
118
|
+
const clientToFlow = useCallback((clientX, clientY) => {
|
|
119
|
+
const pane = paneRef.current;
|
|
120
|
+
const v = viewportRef.current;
|
|
121
|
+
if (!pane)
|
|
122
|
+
return { x: 0, y: 0 };
|
|
123
|
+
const r = pane.getBoundingClientRect();
|
|
124
|
+
return { x: (clientX - r.left - v.x) / v.zoom, y: (clientY - r.top - v.y) / v.zoom };
|
|
125
|
+
}, [viewportRef]);
|
|
126
|
+
const fitView = useCallback(() => {
|
|
127
|
+
const pane = paneRef.current;
|
|
128
|
+
if (!pane)
|
|
129
|
+
return;
|
|
130
|
+
const rect = pane.getBoundingClientRect();
|
|
131
|
+
const ns = nodesRef.current;
|
|
132
|
+
if (ns.length === 0) {
|
|
133
|
+
setViewport({ x: 0, y: 0, zoom: 1 });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
let minX = Infinity;
|
|
137
|
+
let minY = Infinity;
|
|
138
|
+
let maxX = -Infinity;
|
|
139
|
+
let maxY = -Infinity;
|
|
140
|
+
for (const n of ns) {
|
|
141
|
+
minX = Math.min(minX, n.position.x);
|
|
142
|
+
minY = Math.min(minY, n.position.y);
|
|
143
|
+
maxX = Math.max(maxX, n.position.x + ESTIMATED_NODE_SIZE.width);
|
|
144
|
+
maxY = Math.max(maxY, n.position.y + ESTIMATED_NODE_SIZE.height);
|
|
145
|
+
}
|
|
146
|
+
const bw = Math.max(maxX - minX, 1);
|
|
147
|
+
const bh = Math.max(maxY - minY, 1);
|
|
148
|
+
// Fit to view but never enlarge past 100% — so the canvas opens at 1:1 for
|
|
149
|
+
// graphs that already fit, and only zooms out for ones that don't.
|
|
150
|
+
const zoom = clamp(Math.min(rect.width / bw, rect.height / bh) * FIT_VIEW_PADDING, minZoom, Math.min(maxZoom, 1));
|
|
151
|
+
const cx = (minX + maxX) / 2;
|
|
152
|
+
const cy = (minY + maxY) / 2;
|
|
153
|
+
setViewport({
|
|
154
|
+
zoom,
|
|
155
|
+
x: rect.width / 2 - cx * zoom,
|
|
156
|
+
y: rect.height / 2 - cy * zoom,
|
|
157
|
+
});
|
|
158
|
+
}, [setViewport, minZoom, maxZoom]);
|
|
159
|
+
/* ----- interaction ----- */
|
|
160
|
+
const interactingRef = useRef(false);
|
|
161
|
+
const setInteracting = useCallback((interacting) => {
|
|
162
|
+
interactingRef.current = interacting;
|
|
163
|
+
}, []);
|
|
164
|
+
const [locked, setLocked] = useState(false);
|
|
165
|
+
const lockedRef = useRef(locked);
|
|
166
|
+
lockedRef.current = locked;
|
|
167
|
+
const [tool, setTool] = useState("select");
|
|
168
|
+
const toolRef = useRef(tool);
|
|
169
|
+
toolRef.current = tool;
|
|
170
|
+
const [liveMessage, setLiveMessage] = useState("");
|
|
171
|
+
/* ----- connection drag ----- */
|
|
172
|
+
const [connection, setConnection] = useState(null);
|
|
173
|
+
const connectionRef = useRef(null);
|
|
174
|
+
const setConn = useCallback((next) => {
|
|
175
|
+
connectionRef.current = next;
|
|
176
|
+
setConnection(next);
|
|
177
|
+
}, []);
|
|
178
|
+
const onConnectRef = useRef(onConnect);
|
|
179
|
+
onConnectRef.current = onConnect;
|
|
180
|
+
const isValidConnectionRef = useRef(isValidConnection);
|
|
181
|
+
isValidConnectionRef.current = isValidConnection;
|
|
182
|
+
const beginConnection = useCallback(({ nodeId, handleId, type, kind, clientX, clientY }) => {
|
|
183
|
+
var _a;
|
|
184
|
+
const node = nodesRef.current.find((n) => n.id === nodeId);
|
|
185
|
+
if (!node)
|
|
186
|
+
return;
|
|
187
|
+
const list = (_a = handlesRef.current.get(nodeId)) !== null && _a !== void 0 ? _a : [];
|
|
188
|
+
const h = handleId
|
|
189
|
+
? list.find((x) => x.id === handleId)
|
|
190
|
+
: list.find((x) => x.type === type);
|
|
191
|
+
const from = h
|
|
192
|
+
? { x: node.position.x + h.offset.x, y: node.position.y + h.offset.y }
|
|
193
|
+
: { ...node.position };
|
|
194
|
+
setConn({
|
|
195
|
+
sourceNodeId: nodeId,
|
|
196
|
+
sourceHandleId: handleId,
|
|
197
|
+
sourceType: type,
|
|
198
|
+
sourceKind: kind,
|
|
199
|
+
from,
|
|
200
|
+
to: clientToFlow(clientX, clientY),
|
|
201
|
+
});
|
|
202
|
+
setInteracting(true);
|
|
203
|
+
}, [clientToFlow, setConn, setInteracting]);
|
|
204
|
+
const updateConnection = useCallback((clientX, clientY) => {
|
|
205
|
+
const cur = connectionRef.current;
|
|
206
|
+
if (!cur)
|
|
207
|
+
return;
|
|
208
|
+
setConn({ ...cur, to: clientToFlow(clientX, clientY) });
|
|
209
|
+
}, [clientToFlow, setConn]);
|
|
210
|
+
const endConnection = useCallback((clientX, clientY) => {
|
|
211
|
+
var _a, _b, _c, _d;
|
|
212
|
+
const cur = connectionRef.current;
|
|
213
|
+
if (cur) {
|
|
214
|
+
const handleEl = (_a = document
|
|
215
|
+
.elementFromPoint(clientX, clientY)) === null || _a === void 0 ? void 0 : _a.closest("[data-flikkui-handle]");
|
|
216
|
+
if (handleEl) {
|
|
217
|
+
const targetNodeId = handleEl.getAttribute("data-node-id");
|
|
218
|
+
const targetType = handleEl.getAttribute("data-handle-type");
|
|
219
|
+
const targetHandleId = handleEl.getAttribute("data-handle-id") || undefined;
|
|
220
|
+
const targetKind = handleEl.getAttribute("data-handle-kind");
|
|
221
|
+
// Omnidirectional ("auto") handles connect any node to any node — direction is
|
|
222
|
+
// the drag order. Explicit ports still require opposite source/target types.
|
|
223
|
+
const isAuto = cur.sourceKind === "auto" || targetKind === "auto";
|
|
224
|
+
const typesOk = isAuto ? true : targetType !== cur.sourceType;
|
|
225
|
+
if (targetNodeId &&
|
|
226
|
+
targetType &&
|
|
227
|
+
typesOk &&
|
|
228
|
+
targetNodeId !== cur.sourceNodeId) {
|
|
229
|
+
const conn = cur.sourceType === "source"
|
|
230
|
+
? {
|
|
231
|
+
source: cur.sourceNodeId,
|
|
232
|
+
sourceHandle: cur.sourceHandleId,
|
|
233
|
+
target: targetNodeId,
|
|
234
|
+
targetHandle: targetHandleId,
|
|
235
|
+
}
|
|
236
|
+
: {
|
|
237
|
+
source: targetNodeId,
|
|
238
|
+
sourceHandle: targetHandleId,
|
|
239
|
+
target: cur.sourceNodeId,
|
|
240
|
+
targetHandle: cur.sourceHandleId,
|
|
241
|
+
};
|
|
242
|
+
if (!isValidConnectionRef.current || isValidConnectionRef.current(conn)) {
|
|
243
|
+
(_b = onConnectRef.current) === null || _b === void 0 ? void 0 : _b.call(onConnectRef, conn);
|
|
244
|
+
if (!edgesControlled) {
|
|
245
|
+
const id = `${conn.source}:${(_c = conn.sourceHandle) !== null && _c !== void 0 ? _c : ""}__${conn.target}:${(_d = conn.targetHandle) !== null && _d !== void 0 ? _d : ""}`;
|
|
246
|
+
emitEdgeChanges([{ type: "add", item: { id, ...conn } }]);
|
|
247
|
+
}
|
|
248
|
+
setLiveMessage(`Connected ${conn.source} to ${conn.target}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
setConn(null);
|
|
254
|
+
setInteracting(false);
|
|
255
|
+
}, [edgesControlled, emitEdgeChanges, setConn, setInteracting]);
|
|
256
|
+
const cancelConnection = useCallback(() => {
|
|
257
|
+
setConn(null);
|
|
258
|
+
setInteracting(false);
|
|
259
|
+
}, [setConn, setInteracting]);
|
|
260
|
+
/* ----- selection + delete ----- */
|
|
261
|
+
const selectNode = useCallback((id, additive) => {
|
|
262
|
+
const nodeChanges = [];
|
|
263
|
+
for (const n of nodesRef.current) {
|
|
264
|
+
const isTarget = n.id === id;
|
|
265
|
+
const next = additive ? (isTarget ? !n.selected : !!n.selected) : isTarget;
|
|
266
|
+
if (!!n.selected !== next) {
|
|
267
|
+
nodeChanges.push({ type: "select", id: n.id, selected: next });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (nodeChanges.length)
|
|
271
|
+
emitNodeChanges(nodeChanges);
|
|
272
|
+
if (!additive) {
|
|
273
|
+
const edgeChanges = edgesRef.current
|
|
274
|
+
.filter((e) => e.selected)
|
|
275
|
+
.map((e) => ({ type: "select", id: e.id, selected: false }));
|
|
276
|
+
if (edgeChanges.length)
|
|
277
|
+
emitEdgeChanges(edgeChanges);
|
|
278
|
+
}
|
|
279
|
+
}, [emitNodeChanges, emitEdgeChanges]);
|
|
280
|
+
const selectEdge = useCallback((id, additive) => {
|
|
281
|
+
var _a;
|
|
282
|
+
const edgeChanges = [];
|
|
283
|
+
for (const e of edgesRef.current) {
|
|
284
|
+
const isTarget = e.id === id;
|
|
285
|
+
const next = additive ? (isTarget ? !e.selected : !!e.selected) : isTarget;
|
|
286
|
+
if (!!e.selected !== next) {
|
|
287
|
+
edgeChanges.push({ type: "select", id: e.id, selected: next });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (edgeChanges.length)
|
|
291
|
+
emitEdgeChanges(edgeChanges);
|
|
292
|
+
if (!additive) {
|
|
293
|
+
const nodeChanges = nodesRef.current
|
|
294
|
+
.filter((n) => n.selected)
|
|
295
|
+
.map((n) => ({ type: "select", id: n.id, selected: false }));
|
|
296
|
+
if (nodeChanges.length)
|
|
297
|
+
emitNodeChanges(nodeChanges);
|
|
298
|
+
}
|
|
299
|
+
(_a = paneRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
300
|
+
}, [emitNodeChanges, emitEdgeChanges]);
|
|
301
|
+
const clearSelection = useCallback(() => {
|
|
302
|
+
const nodeChanges = nodesRef.current
|
|
303
|
+
.filter((n) => n.selected)
|
|
304
|
+
.map((n) => ({ type: "select", id: n.id, selected: false }));
|
|
305
|
+
const edgeChanges = edgesRef.current
|
|
306
|
+
.filter((e) => e.selected)
|
|
307
|
+
.map((e) => ({ type: "select", id: e.id, selected: false }));
|
|
308
|
+
if (nodeChanges.length)
|
|
309
|
+
emitNodeChanges(nodeChanges);
|
|
310
|
+
if (edgeChanges.length)
|
|
311
|
+
emitEdgeChanges(edgeChanges);
|
|
312
|
+
}, [emitNodeChanges, emitEdgeChanges]);
|
|
313
|
+
// Replace node selection with exactly `ids` (used by marquee, live during drag).
|
|
314
|
+
const selectNodesExact = useCallback((ids) => {
|
|
315
|
+
const set = new Set(ids);
|
|
316
|
+
const nodeChanges = nodesRef.current
|
|
317
|
+
.filter((n) => !!n.selected !== set.has(n.id))
|
|
318
|
+
.map((n) => ({ type: "select", id: n.id, selected: set.has(n.id) }));
|
|
319
|
+
if (nodeChanges.length)
|
|
320
|
+
emitNodeChanges(nodeChanges);
|
|
321
|
+
}, [emitNodeChanges]);
|
|
322
|
+
const moveSelectedNodes = useCallback((dx, dy) => {
|
|
323
|
+
const selected = nodesRef.current.filter((n) => n.selected && n.draggable !== false);
|
|
324
|
+
if (!selected.length)
|
|
325
|
+
return;
|
|
326
|
+
const grid = snapGridRef.current;
|
|
327
|
+
const snap = (x, y) => grid
|
|
328
|
+
? { x: Math.round(x / grid[0]) * grid[0], y: Math.round(y / grid[1]) * grid[1] }
|
|
329
|
+
: { x, y };
|
|
330
|
+
emitNodeChanges(selected.map((n) => ({
|
|
331
|
+
type: "position",
|
|
332
|
+
id: n.id,
|
|
333
|
+
position: snap(n.position.x + dx, n.position.y + dy),
|
|
334
|
+
})));
|
|
335
|
+
}, [emitNodeChanges]);
|
|
336
|
+
const deleteSelection = useCallback(() => {
|
|
337
|
+
const selNodes = nodesRef.current.filter((n) => n.selected);
|
|
338
|
+
const selEdges = edgesRef.current.filter((e) => e.selected);
|
|
339
|
+
if (selNodes.length === 0 && selEdges.length === 0)
|
|
340
|
+
return;
|
|
341
|
+
if (onBeforeDeleteRef.current &&
|
|
342
|
+
onBeforeDeleteRef.current({ nodes: selNodes, edges: selEdges }) === false) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const removeIds = new Set(selNodes.map((n) => n.id));
|
|
346
|
+
// Cascade: also remove edges touching a removed node.
|
|
347
|
+
const edgeRemovals = edgesRef.current.filter((e) => e.selected || removeIds.has(e.source) || removeIds.has(e.target));
|
|
348
|
+
if (selNodes.length) {
|
|
349
|
+
emitNodeChanges(selNodes.map((n) => ({ type: "remove", id: n.id })));
|
|
350
|
+
}
|
|
351
|
+
if (edgeRemovals.length) {
|
|
352
|
+
emitEdgeChanges(edgeRemovals.map((e) => ({ type: "remove", id: e.id })));
|
|
353
|
+
}
|
|
354
|
+
const total = selNodes.length + edgeRemovals.length;
|
|
355
|
+
setLiveMessage(`Deleted ${total} item${total === 1 ? "" : "s"}`);
|
|
356
|
+
}, [emitNodeChanges, emitEdgeChanges]);
|
|
357
|
+
const deleteEdgeById = useCallback((id) => {
|
|
358
|
+
const edge = edgesRef.current.find((e) => e.id === id);
|
|
359
|
+
if (!edge)
|
|
360
|
+
return;
|
|
361
|
+
if (onBeforeDeleteRef.current &&
|
|
362
|
+
onBeforeDeleteRef.current({ nodes: [], edges: [edge] }) === false) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
emitEdgeChanges([{ type: "remove", id }]);
|
|
366
|
+
setLiveMessage("Deleted 1 connection");
|
|
367
|
+
}, [emitEdgeChanges]);
|
|
368
|
+
const ARROW_DELTA = {
|
|
369
|
+
ArrowUp: [0, -1],
|
|
370
|
+
ArrowDown: [0, 1],
|
|
371
|
+
ArrowLeft: [-1, 0],
|
|
372
|
+
ArrowRight: [1, 0],
|
|
373
|
+
};
|
|
374
|
+
const onPaneKeyDown = useCallback((e) => {
|
|
375
|
+
var _a, _b;
|
|
376
|
+
if (isEditableTarget(e.target))
|
|
377
|
+
return;
|
|
378
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
deleteSelection();
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Arrow-key node movement — only when a node shell itself is focused.
|
|
384
|
+
const dir = ARROW_DELTA[e.key];
|
|
385
|
+
if (dir && ((_b = (_a = e.target) === null || _a === void 0 ? void 0 : _a.matches) === null || _b === void 0 ? void 0 : _b.call(_a, "[data-flikkui-node]"))) {
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
const grid = snapGridRef.current;
|
|
388
|
+
const base = grid ? grid : [10, 10];
|
|
389
|
+
const mult = e.shiftKey ? 4 : 1;
|
|
390
|
+
moveSelectedNodes(dir[0] * base[0] * mult, dir[1] * base[1] * mult);
|
|
391
|
+
}
|
|
392
|
+
}, [deleteSelection, moveSelectedNodes]);
|
|
393
|
+
const onPanePointerDown = useCallback((e) => {
|
|
394
|
+
if (e.shiftKey)
|
|
395
|
+
return; // shift+drag is marquee — don't clear selection
|
|
396
|
+
const target = e.target;
|
|
397
|
+
if (!(target === null || target === void 0 ? void 0 : target.closest("[data-flikkui-node],[data-flikkui-handle],[data-flikkui-edge],[data-flikkui-controls],[data-flikkui-nopan]"))) {
|
|
398
|
+
clearSelection();
|
|
399
|
+
}
|
|
400
|
+
}, [clearSelection]);
|
|
401
|
+
// Fire onSelectionChange only when the selected id set actually changes.
|
|
402
|
+
const prevSelectionRef = useRef("");
|
|
403
|
+
useEffect(() => {
|
|
404
|
+
var _a;
|
|
405
|
+
const selection = {
|
|
406
|
+
nodes: nodes.filter((n) => n.selected).map((n) => n.id),
|
|
407
|
+
edges: edges.filter((e) => e.selected).map((e) => e.id),
|
|
408
|
+
};
|
|
409
|
+
const sig = JSON.stringify(selection);
|
|
410
|
+
if (sig !== prevSelectionRef.current) {
|
|
411
|
+
prevSelectionRef.current = sig;
|
|
412
|
+
(_a = onSelectionChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onSelectionChangeRef, selection);
|
|
413
|
+
}
|
|
414
|
+
}, [nodes, edges]);
|
|
415
|
+
usePanZoom({
|
|
416
|
+
paneRef,
|
|
417
|
+
setViewport,
|
|
418
|
+
minZoom,
|
|
419
|
+
maxZoom,
|
|
420
|
+
interactingRef,
|
|
421
|
+
lockedRef,
|
|
422
|
+
toolRef,
|
|
423
|
+
panOnDrag,
|
|
424
|
+
panOnScroll,
|
|
425
|
+
zoomOnScroll,
|
|
426
|
+
zoomActivationKey,
|
|
427
|
+
});
|
|
428
|
+
const marqueeRect = useMarquee({
|
|
429
|
+
paneRef,
|
|
430
|
+
lockedRef,
|
|
431
|
+
toolRef,
|
|
432
|
+
interactingRef,
|
|
433
|
+
onSelect: selectNodesExact,
|
|
434
|
+
});
|
|
435
|
+
useEffect(() => {
|
|
436
|
+
if (fitViewOnInit)
|
|
437
|
+
fitView();
|
|
438
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
439
|
+
}, []);
|
|
440
|
+
/* ----- context values ----- */
|
|
441
|
+
const canvasValue = useMemo(() => ({
|
|
442
|
+
nodes,
|
|
443
|
+
viewport,
|
|
444
|
+
viewportRef,
|
|
445
|
+
setViewport,
|
|
446
|
+
minZoom,
|
|
447
|
+
maxZoom,
|
|
448
|
+
paneRef,
|
|
449
|
+
flowToScreen,
|
|
450
|
+
screenToFlow,
|
|
451
|
+
fitView,
|
|
452
|
+
locked,
|
|
453
|
+
setLocked,
|
|
454
|
+
snapGrid,
|
|
455
|
+
tool,
|
|
456
|
+
setTool,
|
|
457
|
+
}), [nodes, viewport, viewportRef, setViewport, minZoom, maxZoom, flowToScreen, screenToFlow, fitView, locked, snapGrid === null || snapGrid === void 0 ? void 0 : snapGrid[0], snapGrid === null || snapGrid === void 0 ? void 0 : snapGrid[1], tool]);
|
|
458
|
+
const interactionValue = useMemo(() => ({ interactingRef, setInteracting }), [setInteracting]);
|
|
459
|
+
const handleRegistryValue = useMemo(() => ({
|
|
460
|
+
register: registerHandle,
|
|
461
|
+
unregister: unregisterHandle,
|
|
462
|
+
handlesRef,
|
|
463
|
+
version: handleVersion,
|
|
464
|
+
}), [registerHandle, unregisterHandle, handleVersion]);
|
|
465
|
+
const connectionActionsValue = useMemo(() => ({ beginConnection, updateConnection, endConnection, cancelConnection }), [beginConnection, updateConnection, endConnection, cancelConnection]);
|
|
466
|
+
return (jsx("div", { ref: paneRef, className: cn(theme.pane, "focus:outline-none", className), tabIndex: -1, onKeyDown: onPaneKeyDown, onPointerDown: onPanePointerDown, ...rest, children: jsx(CanvasProvider, { value: canvasValue, children: jsx(InteractionProvider, { value: interactionValue, children: jsx(HandleRegistryProvider, { value: handleRegistryValue, children: jsxs(ConnectionActionsProvider, { value: connectionActionsValue, children: [children, jsxs("div", { className: theme.world, style: {
|
|
467
|
+
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom})`,
|
|
468
|
+
}, children: [jsx(EdgeLayer, { nodes: nodes, edges: edges, connection: connection, onSelectEdge: selectEdge, onDeleteEdge: deleteEdgeById }), jsx(NodeRenderer, { nodes: nodes, nodeTypes: nodeTypes, autoConnect: autoConnect, onNodeChanges: emitNodeChanges, onSelectNode: selectNode, onNodeClick: onNodeClick })] }), marqueeRect && (jsx("div", { className: "absolute z-30 pointer-events-none rounded-md border border-[var(--color-primary)] bg-[var(--color-primary)]/10 shadow-inner-real-lg", style: {
|
|
469
|
+
left: marqueeRect.x,
|
|
470
|
+
top: marqueeRect.y,
|
|
471
|
+
width: marqueeRect.width,
|
|
472
|
+
height: marqueeRect.height,
|
|
473
|
+
} })), jsx("div", { className: "sr-only", "aria-live": "polite", role: "status", children: liveMessage })] }) }) }) }) }));
|
|
474
|
+
});
|
|
475
|
+
NodeCanvas.displayName = "NodeCanvas";
|
|
476
|
+
|
|
477
|
+
export { NodeCanvas };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const nodeCanvasTheme = {
|
|
2
|
+
// Light: the recessed sunken plane, so cards float above an off-white floor
|
|
3
|
+
// (pure-white --color-background would make the canvas white-on-white).
|
|
4
|
+
// Dark: the true-black floor — sunken would lift the whole canvas to 12%.
|
|
5
|
+
pane: "relative w-full h-full overflow-hidden bg-[var(--color-background)] touch-none select-none cursor-grab [contain:strict]",
|
|
6
|
+
world: "absolute top-0 left-0 z-10 origin-top-left will-change-transform",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export { nodeCanvasTheme };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
/** A point in flow coordinates (the unbounded node space). */
|
|
3
|
+
export interface XYPosition {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
/** A node in the graph. `data` is an arbitrary user payload. */
|
|
8
|
+
export interface NodeCanvasNode<TData = unknown> {
|
|
9
|
+
id: string;
|
|
10
|
+
/** Key into the `nodeTypes` registry. */
|
|
11
|
+
type: string;
|
|
12
|
+
/** Position in flow coordinates. */
|
|
13
|
+
position: XYPosition;
|
|
14
|
+
data: TData;
|
|
15
|
+
selected?: boolean;
|
|
16
|
+
/** Defaults to `true`. */
|
|
17
|
+
draggable?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Edge routing style. */
|
|
20
|
+
export type EdgeType = "bezier" | "step" | "smoothstep";
|
|
21
|
+
/** An edge connecting two node handles. */
|
|
22
|
+
export interface NodeCanvasEdge {
|
|
23
|
+
id: string;
|
|
24
|
+
source: string;
|
|
25
|
+
sourceHandle?: string;
|
|
26
|
+
target: string;
|
|
27
|
+
targetHandle?: string;
|
|
28
|
+
selected?: boolean;
|
|
29
|
+
/** Routing style. Defaults to `bezier`. */
|
|
30
|
+
type?: EdgeType;
|
|
31
|
+
/**
|
|
32
|
+
* Stroke color (any CSS color, e.g. `var(--color-primary-300)`). Selection
|
|
33
|
+
* still strokes `--color-primary`. Defaults to `--color-border`.
|
|
34
|
+
*/
|
|
35
|
+
color?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Emitted by `onConnect` before the consumer materializes an edge. */
|
|
38
|
+
export interface NodeCanvasConnection {
|
|
39
|
+
source: string;
|
|
40
|
+
sourceHandle?: string;
|
|
41
|
+
target: string;
|
|
42
|
+
targetHandle?: string;
|
|
43
|
+
}
|
|
44
|
+
/** The viewport transform mapping flow space → screen space. */
|
|
45
|
+
export interface NodeCanvasViewport {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
zoom: number;
|
|
49
|
+
}
|
|
50
|
+
/** Props passed to a custom node component registered in `nodeTypes`. */
|
|
51
|
+
export interface NodeProps<TData = unknown> {
|
|
52
|
+
id: string;
|
|
53
|
+
type: string;
|
|
54
|
+
data: TData;
|
|
55
|
+
selected: boolean;
|
|
56
|
+
position: XYPosition;
|
|
57
|
+
dragging: boolean;
|
|
58
|
+
}
|
|
59
|
+
export type HandleType = "source" | "target";
|
|
60
|
+
export type HandlePosition = "top" | "right" | "bottom" | "left";
|
|
61
|
+
/** Props for a `Handle` (port) declared inside a custom node. */
|
|
62
|
+
export interface HandleProps {
|
|
63
|
+
type: HandleType;
|
|
64
|
+
id?: string;
|
|
65
|
+
position?: HandlePosition;
|
|
66
|
+
isConnectable?: boolean;
|
|
67
|
+
className?: string;
|
|
68
|
+
style?: React.CSSProperties;
|
|
69
|
+
/**
|
|
70
|
+
* Custom port visual (e.g. an icon badge). When present, the default dot
|
|
71
|
+
* styling is dropped — `className` owns size/shape/color entirely; the
|
|
72
|
+
* engine keeps placement, measurement, and connection behavior.
|
|
73
|
+
*/
|
|
74
|
+
children?: React.ReactNode;
|
|
75
|
+
}
|
|
76
|
+
/** Props for a custom edge component (used from issue 3 onward). */
|
|
77
|
+
export interface EdgeProps {
|
|
78
|
+
id: string;
|
|
79
|
+
source: string;
|
|
80
|
+
target: string;
|
|
81
|
+
selected?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** A registry mapping `node.type` → the component that renders it. */
|
|
84
|
+
export type NodeCanvasNodeTypes = Record<string, React.ComponentType<NodeProps<never>>>;
|
|
85
|
+
export type NodeChange = {
|
|
86
|
+
type: "position";
|
|
87
|
+
id: string;
|
|
88
|
+
position: XYPosition;
|
|
89
|
+
dragging?: boolean;
|
|
90
|
+
} | {
|
|
91
|
+
type: "select";
|
|
92
|
+
id: string;
|
|
93
|
+
selected: boolean;
|
|
94
|
+
} | {
|
|
95
|
+
type: "remove";
|
|
96
|
+
id: string;
|
|
97
|
+
} | {
|
|
98
|
+
type: "add";
|
|
99
|
+
item: NodeCanvasNode;
|
|
100
|
+
} | {
|
|
101
|
+
type: "dimensions";
|
|
102
|
+
id: string;
|
|
103
|
+
dimensions: {
|
|
104
|
+
width: number;
|
|
105
|
+
height: number;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export type EdgeChange = {
|
|
109
|
+
type: "select";
|
|
110
|
+
id: string;
|
|
111
|
+
selected: boolean;
|
|
112
|
+
} | {
|
|
113
|
+
type: "remove";
|
|
114
|
+
id: string;
|
|
115
|
+
} | {
|
|
116
|
+
type: "add";
|
|
117
|
+
item: NodeCanvasEdge;
|
|
118
|
+
};
|
|
119
|
+
/** Theme override slots for the canvas surface. */
|
|
120
|
+
export interface NodeCanvasThemeOverrides {
|
|
121
|
+
/** The clipping pane that captures pan/zoom gestures. */
|
|
122
|
+
pane?: string;
|
|
123
|
+
/** The transformed "world" layer that holds nodes + edges. */
|
|
124
|
+
world?: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Setter accepting either a next viewport or an updater function.
|
|
128
|
+
* Zoom is always clamped to `[minZoom, maxZoom]`.
|
|
129
|
+
*/
|
|
130
|
+
export type SetViewport = (next: NodeCanvasViewport | ((prev: NodeCanvasViewport) => NodeCanvasViewport)) => void;
|
|
131
|
+
export interface NodeCanvasProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect"> {
|
|
132
|
+
/** Controlled nodes. */
|
|
133
|
+
nodes?: NodeCanvasNode[];
|
|
134
|
+
/** Controlled edges. */
|
|
135
|
+
edges?: NodeCanvasEdge[];
|
|
136
|
+
/** Controlled viewport. */
|
|
137
|
+
viewport?: NodeCanvasViewport;
|
|
138
|
+
/** Initial nodes when uncontrolled. */
|
|
139
|
+
defaultNodes?: NodeCanvasNode[];
|
|
140
|
+
/** Initial edges when uncontrolled. */
|
|
141
|
+
defaultEdges?: NodeCanvasEdge[];
|
|
142
|
+
/** Initial viewport when uncontrolled. Defaults to `{ x: 0, y: 0, zoom: 1 }`. */
|
|
143
|
+
defaultViewport?: NodeCanvasViewport;
|
|
144
|
+
/** Maps `node.type` → the component that renders it. */
|
|
145
|
+
nodeTypes: NodeCanvasNodeTypes;
|
|
146
|
+
onNodesChange?: (changes: NodeChange[]) => void;
|
|
147
|
+
onEdgesChange?: (changes: EdgeChange[]) => void;
|
|
148
|
+
onConnect?: (connection: NodeCanvasConnection) => void;
|
|
149
|
+
onViewportChange?: (viewport: NodeCanvasViewport) => void;
|
|
150
|
+
onSelectionChange?: (selection: {
|
|
151
|
+
nodes: string[];
|
|
152
|
+
edges: string[];
|
|
153
|
+
}) => void;
|
|
154
|
+
onNodeClick?: (event: React.MouseEvent, node: NodeCanvasNode) => void;
|
|
155
|
+
onBeforeDelete?: (selection: {
|
|
156
|
+
nodes: NodeCanvasNode[];
|
|
157
|
+
edges: NodeCanvasEdge[];
|
|
158
|
+
}) => boolean;
|
|
159
|
+
/** Reject a candidate connection (e.g. enforce source→target rules). */
|
|
160
|
+
isValidConnection?: (connection: NodeCanvasConnection) => boolean;
|
|
161
|
+
/** Minimum zoom. Defaults to `0.25`. */
|
|
162
|
+
minZoom?: number;
|
|
163
|
+
/** Maximum zoom. Defaults to `2`. */
|
|
164
|
+
maxZoom?: number;
|
|
165
|
+
/** Fit all nodes into view on mount. Defaults to `false`. */
|
|
166
|
+
fitViewOnInit?: boolean;
|
|
167
|
+
/** Scroll the canvas instead of zooming. Defaults to `false`. */
|
|
168
|
+
panOnScroll?: boolean;
|
|
169
|
+
/** Zoom on wheel / pinch. Defaults to `true`. */
|
|
170
|
+
zoomOnScroll?: boolean;
|
|
171
|
+
/** Require a modifier key for wheel-zoom. Defaults to `null` (no key). */
|
|
172
|
+
zoomActivationKey?: "ctrl" | "meta" | "shift" | null;
|
|
173
|
+
/** Pan by dragging the empty canvas. Defaults to `true`. */
|
|
174
|
+
panOnDrag?: boolean;
|
|
175
|
+
/** Snap nodes to a grid while dragging. Defaults to `false`. */
|
|
176
|
+
snapToGrid?: boolean;
|
|
177
|
+
/** Grid step `[x, y]` in flow units when `snapToGrid`. Defaults to `[16, 16]`. */
|
|
178
|
+
snapGrid?: [number, number];
|
|
179
|
+
/**
|
|
180
|
+
* Show omnidirectional "+" connect handles on every node (hover-revealed) so any
|
|
181
|
+
* node can connect to any node from any side. Defaults to `true`. Set `false` to
|
|
182
|
+
* use only explicitly declared `<Handle>` ports.
|
|
183
|
+
*/
|
|
184
|
+
autoConnect?: boolean;
|
|
185
|
+
className?: string;
|
|
186
|
+
theme?: NodeCanvasThemeOverrides;
|
|
187
|
+
}
|