Skip to content
This repository was archived by the owner on Apr 7, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
# React Native Smart Graph

[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
222 changes: 222 additions & 0 deletions src/animations/GraphTimeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import EASING from '@/constants/easings';
import { MinPriorityQueue } from '@/data';
import {
AnimationSettings,
EdgeDataType,
TimelineAnimationSettings
} from '@/types/animations';
import { VertexData } from '@/types/data';
import { isEdgeData, isVertexData } from '@/utils/models';

import { DirectedGraph, UndirectedGraph } from '..';

// TODO - implement config support
type SequenceAnimationConfig = {
delay?: number;
paused?: boolean;
repeat?: number;
repeatDelay?: number;
yoyo?: boolean;
onComplete?: () => void;
onRepeat?: () => void;
onReverseComplete?: () => void;
onStart: () => void;
onUpdate?: () => void;
};

type AnimationStep = {
fn: () => void;
delay: number;
};

export default class GraphTimeline<
V,
E,
G extends DirectedGraph<V, E> | UndirectedGraph<V, E>
> {
private readonly steps: MinPriorityQueue<AnimationStep> =
new MinPriorityQueue();
private maxStartDelay = 0;
private maxEndDelay = 0;

constructor(
private readonly graph: G,
private readonly config?: SequenceAnimationConfig
) {}

insertVertex(
data: VertexData<V>,
duration: number,
settings?: TimelineAnimationSettings
) {
return this.addStep(
this.graph.insertVertex.bind(this.graph),
data,
duration,
this.maxEndDelay,
settings
);
}

removeVertex(
key: string,
duration: number,
settings?: TimelineAnimationSettings
) {
return this.addStep(
this.graph.removeVertex.bind(this.graph),
key,
duration,
this.maxEndDelay,
settings
);
}

insertEdge(
data: EdgeDataType<G, V, E>,
duration: number,
settings?: TimelineAnimationSettings
) {
return this.addStep(
this.graph.insertEdge.bind(this.graph),
data,
duration,
this.maxEndDelay,
settings
);
}

removeEdge(
key: string,
duration: number,
settings?: TimelineAnimationSettings
) {
return this.addStep(
this.graph.removeEdge.bind(this.graph),
key,
duration,
this.maxEndDelay,
settings
);
}

insertSimultaneous(
data: Array<VertexData<V> | EdgeDataType<V, E, G>>,
duration: number,
settings?: TimelineAnimationSettings
) {
const edges = data.filter(isEdgeData);
const vertices = data.filter(isVertexData);
this.addStep(
this.graph.insertBatch.bind(this.graph),
{ edges, vertices },
duration,
this.maxEndDelay,
settings
);
}

insertSequential(
data:
| Array<VertexData<V> | EdgeDataType<V, E, G>>
| Array<Array<VertexData<V> | EdgeDataType<V, E, G>>>,
duration: number,
settings?: TimelineAnimationSettings
) {
const minDelay = this.maxEndDelay;
data.forEach((d, i) => {
if (Array.isArray(d)) {
this.addStep(
this.graph.insertBatch.bind(this.graph),
{
edges: d.filter(isEdgeData),
vertices: d.filter(isVertexData)
},
duration,
minDelay + (i * duration) / data.length,
settings
);
return;
}
if (isEdgeData(d)) {
this.addStep(
this.graph.insertEdge.bind(this.graph),
d,
duration,
minDelay + (i * duration) / data.length,
settings
);
} else {
this.addStep(
this.graph.insertVertex.bind(this.graph),
d,
duration,
minDelay + (i * duration) / data.length,
settings
);
}
});
return this;
}

removeSimultaneous(
keys: Array<string>,
duration: number,
settings?: TimelineAnimationSettings
) {
// TODO - implement
return this;
}

removeSequential(
keys: Array<string>,
duration: number,
settings?: TimelineAnimationSettings
) {
// TODO - implement
return this;
}

async play() {
let currentDelay = 0;
while (!this.steps.isEmpty()) {
const { fn, delay } = this.steps.dequeue()!;
// eslint-disable-next-line no-await-in-loop
await this.sleep(delay - currentDelay);
currentDelay = delay;
fn();
}
}

private sleep(ms: number) {
// eslint-disable-next-line no-promise-executor-return
return new Promise(resolve => setTimeout(resolve, ms));
}

private addStep<D>(
fn: (data: D, settings?: AnimationSettings) => void,
data: D,
duration: number,
delay: number,
settings?: TimelineAnimationSettings
) {
this.maxStartDelay = Math.max(this.maxStartDelay, delay);
this.maxEndDelay = Math.max(this.maxEndDelay, delay + duration);
this.steps.enqueue(
{
fn: () => {
fn(data, {
duration,
easing: settings?.easing || EASING.bounce,
onComplete: settings?.onComplete
});
},
delay
},
delay
);
return this;
}
}
8 changes: 5 additions & 3 deletions src/components/graphs/GraphComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ export default function GraphComponent<
graphEventsContext
}: GraphComponentProps<V, E, S, R> & GraphComponentPrivateProps<V, E>) {
// GRAPH OBSERVER
const [{ vertices, orderedEdges }] = useGraphObserver(graph);
const [{ vertices, orderedEdges, animationSettings }] =
useGraphObserver(graph);

// HELPER REFS
const isFirstRenderRef = useRef(true);
Expand Down Expand Up @@ -293,10 +294,11 @@ export default function GraphComponent<
() => {
animateVerticesToFinalPositions(
animatedVerticesPositions,
memoGraphLayout.verticesPositions
memoGraphLayout.verticesPositions,
animationSettings
);
},
[animatedVerticesPositions, memoGraphLayout]
[animatedVerticesPositions, memoGraphLayout, animationSettings]
);

const handleVertexRender = useCallback(
Expand Down
9 changes: 9 additions & 0 deletions src/constants/animations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AnimationSettingWithDefaults } from '@/types/animations';

import EASING from './easings';

export const DEFAULT_ANIMATION_SETTINGS: AnimationSettingWithDefaults = {
duration: 500,
easing: EASING.bounce,
onComplete: () => undefined
};
3 changes: 3 additions & 0 deletions src/data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as LinkedList } from './lists/LinkedList';
export { default as MinPriorityQueue } from './queues/MinPriorityQueue';
export { default as Queue } from './queues/Queue';
14 changes: 7 additions & 7 deletions src/data/LinkedList.ts → src/data/lists/LinkedList.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
class Node<T> {
public next: Node<T> | null = null;
next: Node<T> | null = null;

constructor(public value: T) {}
}

export default class LinkedList<T> {
public head: Node<T> | null = null;
public tail: Node<T> | null = null;
head: Node<T> | null = null;
tail: Node<T> | null = null;
private length$ = 0;

public get length(): number {
get length(): number {
return this.length$;
}

public append(value: T): void {
append(value: T): void {
const node = new Node(value);

if (!this.tail) {
Expand All @@ -27,7 +27,7 @@ export default class LinkedList<T> {
this.length$++;
}

public prepend(value: T): void {
prepend(value: T): void {
const node = new Node(value);

if (!this.head) {
Expand All @@ -41,7 +41,7 @@ export default class LinkedList<T> {
this.length$++;
}

public popLeft(): T | null {
popLeft(): T | null {
if (!this.head) {
return null;
}
Expand Down
7 changes: 7 additions & 0 deletions src/data/queues/MinPriorityQueue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import PriorityQueue from './PriorityQueue';

export default class MinPriorityQueue<T> extends PriorityQueue<T> {
constructor() {
super((a, b) => a - b);
}
}
Loading