feat(svelte): add controls
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { PanelPosition } from '@reactflow/system';
|
||||||
|
import cc from 'classcat';
|
||||||
|
|
||||||
|
export let position: PanelPosition = 'top-right';
|
||||||
|
export { className as class }
|
||||||
|
|
||||||
|
let className: string;
|
||||||
|
|
||||||
|
$: positionClasses = `${position}`.split('-');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cc(['react-flow__panel', className, ...positionClasses])}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.react-flow__panel {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__panel.top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__panel.bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__panel.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__panel.right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__panel.center {
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -47,6 +47,8 @@
|
|||||||
<NodeRenderer />
|
<NodeRenderer />
|
||||||
<EdgeRenderer />
|
<EdgeRenderer />
|
||||||
</Viewport>
|
</Viewport>
|
||||||
|
|
||||||
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
import SvelteFlow from '$lib/container/SvelteFlow.svelte';
|
import SvelteFlow from '$lib/container/SvelteFlow.svelte';
|
||||||
|
export { default as Controls } from '$lib/plugins/Controls/index.svelte';
|
||||||
|
|
||||||
export default SvelteFlow;
|
export default SvelteFlow;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import cc from 'classcat';
|
||||||
|
|
||||||
|
let className: string;
|
||||||
|
|
||||||
|
export { className as class }
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button type="button" on:click class={cc(['react-flow__controls-button', className])} {...$$restProps} >
|
||||||
|
<slot class="button-svg" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.react-flow__controls-button {
|
||||||
|
border: none;
|
||||||
|
background: #fefefe;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
box-sizing: content-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__controls-button :global(svg) {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 12px;
|
||||||
|
max-height: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
|
||||||
|
<path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 462 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||||
|
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 529 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5">
|
||||||
|
<path d="M0 0h32v4.2H0z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 96 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 152 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||||
|
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 471 B |
@@ -0,0 +1,89 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Panel from '$lib/container/Panel.svelte';
|
||||||
|
import type { PanelPosition } from '@reactflow/system';
|
||||||
|
|
||||||
|
import ControlButton from './ControlButton.svelte';
|
||||||
|
import PlusIcon from './Icons/Plus.svelte';
|
||||||
|
import MinusIcon from './Icons/Minus.svelte';
|
||||||
|
import FitViewIcon from './Icons/Fit.svelte';
|
||||||
|
import LockIcon from './Icons/Lock.svelte';
|
||||||
|
import UnlockIcon from './Icons/Unlock.svelte';
|
||||||
|
|
||||||
|
export let position: PanelPosition = 'bottom-left';
|
||||||
|
export let showZoom = true;
|
||||||
|
export let showFitView = true;
|
||||||
|
export let showInteractive = true;
|
||||||
|
|
||||||
|
import { useStore } from '$lib/store';
|
||||||
|
|
||||||
|
const { zoomIn, zoomOut, fitView } = useStore();
|
||||||
|
|
||||||
|
const isInteractive = true;
|
||||||
|
|
||||||
|
const onZoomInHandler = () => {
|
||||||
|
zoomIn();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onZoomOutHandler = () => {
|
||||||
|
zoomOut();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFitViewHandler = () => {
|
||||||
|
fitView();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onToggleInteractivity = () => {
|
||||||
|
// store.setState({
|
||||||
|
// nodesDraggable: !isInteractive,
|
||||||
|
// nodesConnectable: !isInteractive,
|
||||||
|
// elementsSelectable: !isInteractive,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// onInteractiveChange?.(!isInteractive);
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Panel class="react-flow__controls" position={position}>
|
||||||
|
{#if showZoom}
|
||||||
|
<ControlButton
|
||||||
|
on:click={onZoomInHandler}
|
||||||
|
class="react-flow__controls-zoomin"
|
||||||
|
title="zoom in"
|
||||||
|
aria-label="zoom in"
|
||||||
|
>
|
||||||
|
<PlusIcon />
|
||||||
|
</ControlButton>
|
||||||
|
<svelte:component
|
||||||
|
this={ControlButton}
|
||||||
|
on:click={onZoomOutHandler}
|
||||||
|
class="react-flow__controls-zoomout"
|
||||||
|
title="zoom out"
|
||||||
|
aria-label="zoom out"
|
||||||
|
>
|
||||||
|
<MinusIcon />
|
||||||
|
</svelte:component>
|
||||||
|
{/if}
|
||||||
|
{#if showFitView}
|
||||||
|
<svelte:component
|
||||||
|
this={ControlButton}
|
||||||
|
class="react-flow__controls-fitview"
|
||||||
|
on:click={onFitViewHandler}
|
||||||
|
title="fit view"
|
||||||
|
aria-label="fit view"
|
||||||
|
>
|
||||||
|
<FitViewIcon />
|
||||||
|
</svelte:component>
|
||||||
|
{/if}
|
||||||
|
{#if showInteractive}
|
||||||
|
<svelte:component
|
||||||
|
this={ControlButton}
|
||||||
|
class="react-flow__controls-interactive"
|
||||||
|
on:click={onToggleInteractivity}
|
||||||
|
title="toggle interactivity"
|
||||||
|
aria-label="toggle interactivity"
|
||||||
|
>
|
||||||
|
{#if isInteractive}<UnlockIcon />{:else}<LockIcon />{/if}
|
||||||
|
</svelte:component>
|
||||||
|
{/if}
|
||||||
|
</Panel>
|
||||||
@@ -10,9 +10,10 @@ import {
|
|||||||
internalsSymbol,
|
internalsSymbol,
|
||||||
type NodeOrigin,
|
type NodeOrigin,
|
||||||
type D3ZoomInstance,
|
type D3ZoomInstance,
|
||||||
type D3SelectionInstance
|
type D3SelectionInstance,
|
||||||
|
type ViewportHelperFunctionOptions
|
||||||
} from '@reactflow/system';
|
} from '@reactflow/system';
|
||||||
import { fitView } from '@reactflow/utils';
|
import { fitView, getD3Transition } from '@reactflow/utils';
|
||||||
|
|
||||||
import { getDimensions, getHandleBounds } from '../../utils';
|
import { getDimensions, getHandleBounds } from '../../utils';
|
||||||
import {
|
import {
|
||||||
@@ -48,6 +49,9 @@ type SvelteFlowStore = {
|
|||||||
}>;
|
}>;
|
||||||
transformStore: Writable<Transform>;
|
transformStore: Writable<Transform>;
|
||||||
edgesWithDataStore: Readable<EdgeWithData[]>;
|
edgesWithDataStore: Readable<EdgeWithData[]>;
|
||||||
|
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
||||||
|
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
||||||
|
fitView: (options?: ViewportHelperFunctionOptions) => void;
|
||||||
updateNodePositions: (
|
updateNodePositions: (
|
||||||
nodeDragItems: NodeDragItem[],
|
nodeDragItems: NodeDragItem[],
|
||||||
positionChanged?: boolean,
|
positionChanged?: boolean,
|
||||||
@@ -68,7 +72,10 @@ export function createStore({
|
|||||||
const heightStore = writable(500);
|
const heightStore = writable(500);
|
||||||
const widthStore = writable(500);
|
const widthStore = writable(500);
|
||||||
const nodeOriginStore = writable(nodeOrigin);
|
const nodeOriginStore = writable(nodeOrigin);
|
||||||
const d3Store = writable({ zoom: null, selection: null });
|
const d3Store = writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>({
|
||||||
|
zoom: null,
|
||||||
|
selection: null
|
||||||
|
});
|
||||||
|
|
||||||
let fitViewOnInitDone = false;
|
let fitViewOnInitDone = false;
|
||||||
|
|
||||||
@@ -205,6 +212,40 @@ export function createStore({
|
|||||||
nodesStore.set(nextNodes);
|
nodesStore.set(nextNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zoomIn(options?: ViewportHelperFunctionOptions) {
|
||||||
|
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||||
|
|
||||||
|
if (d3Zoom && d3Selection) {
|
||||||
|
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomOut(options?: ViewportHelperFunctionOptions) {
|
||||||
|
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||||
|
if (d3Zoom && d3Selection) {
|
||||||
|
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1 / 1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _fitView() {
|
||||||
|
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||||
|
if (d3Zoom && d3Selection) {
|
||||||
|
fitView(
|
||||||
|
{
|
||||||
|
nodes: get(nodesStore),
|
||||||
|
width: get(widthStore),
|
||||||
|
height: get(heightStore),
|
||||||
|
minZoom: 0.2,
|
||||||
|
maxZoom: 2,
|
||||||
|
d3Selection,
|
||||||
|
d3Zoom,
|
||||||
|
nodeOrigin: get(nodeOriginStore)
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodesStore,
|
nodesStore,
|
||||||
edgesStore,
|
edgesStore,
|
||||||
@@ -214,7 +255,10 @@ export function createStore({
|
|||||||
widthStore,
|
widthStore,
|
||||||
edgesWithDataStore,
|
edgesWithDataStore,
|
||||||
updateNodePositions,
|
updateNodePositions,
|
||||||
updateNodeDimensions
|
updateNodeDimensions,
|
||||||
|
zoomIn,
|
||||||
|
zoomOut,
|
||||||
|
fitView: _fitView
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Node, Edge } from '@reactflow/system';
|
import type { Node, Edge } from '@reactflow/system';
|
||||||
|
|
||||||
import SvelteFlow from '../lib/index';
|
import SvelteFlow, { Controls } from '../lib/index';
|
||||||
|
|
||||||
const yNodes = 10;
|
const yNodes = 10;
|
||||||
const xNodes = 10;
|
const xNodes = 10;
|
||||||
@@ -57,7 +57,9 @@
|
|||||||
// }]
|
// }]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteFlow {nodes} {edges} fitView />
|
<SvelteFlow {nodes} {edges} fitView >
|
||||||
|
<Controls />
|
||||||
|
</SvelteFlow>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
Reference in New Issue
Block a user