@bruno00o/graph 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +8 -2
- package/dist/db.d.ts +14 -0
- package/dist/db.js +85 -0
- package/dist/db.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/mount-graph-B88WGOQu.d.ts +133 -0
- package/dist/mount-graph-BHwq3LOr.js +3823 -0
- package/dist/mount-graph-BHwq3LOr.js.map +1 -0
- package/dist/query.d.ts +30 -0
- package/dist/query.js +182 -0
- package/dist/query.js.map +1 -0
- package/dist/react.d.ts +35 -0
- package/dist/react.js +132 -0
- package/dist/react.js.map +1 -0
- package/dist/theme.css +90 -0
- package/dist/types-D6b86Rt1.d.ts +156 -0
- package/package.json +104 -4
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { _ as Key, x as LiveSource } from "./types-D6b86Rt1.js";
|
|
2
|
+
import { ReadonlyStore } from "@tanstack/store";
|
|
3
|
+
import { DefaultError, QueryClient, QueryKey, QueryObserverOptions } from "@tanstack/query-core";
|
|
4
|
+
export interface ExplorationData<TNode, TEdge> {
|
|
5
|
+
readonly nodes: readonly TNode[];
|
|
6
|
+
readonly edges: readonly TEdge[];
|
|
7
|
+
}
|
|
8
|
+
export interface ExplorationOptions<TNode, TEdge, TQueryFnData = ExplorationData<TNode, TEdge>, TError = DefaultError, TQueryKey extends QueryKey = QueryKey> {
|
|
9
|
+
readonly roots: Iterable<Key>;
|
|
10
|
+
readonly neighbors: (key: Key) => QueryObserverOptions<TQueryFnData, TError, ExplorationData<TNode, TEdge>, TQueryFnData, TQueryKey>;
|
|
11
|
+
readonly nodeKey?: (node: TNode) => Key;
|
|
12
|
+
readonly edgeKey?: (edge: TEdge) => Key;
|
|
13
|
+
}
|
|
14
|
+
export interface ExplorationState {
|
|
15
|
+
readonly expanded: readonly Key[];
|
|
16
|
+
readonly loading: readonly Key[];
|
|
17
|
+
readonly errors: ReadonlyMap<Key, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface Exploration<TNode, TEdge> {
|
|
20
|
+
readonly nodes: LiveSource<TNode>;
|
|
21
|
+
readonly edges: LiveSource<TEdge>;
|
|
22
|
+
readonly store: ReadonlyStore<ExplorationState>;
|
|
23
|
+
expand(key: Key): void;
|
|
24
|
+
collapse(key: Key): void;
|
|
25
|
+
toggle(key: Key): void;
|
|
26
|
+
reset(): void;
|
|
27
|
+
destroy(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function createExploration<TNode, TEdge, TQueryFnData = ExplorationData<TNode, TEdge>, TError = DefaultError, TQueryKey extends QueryKey = QueryKey>(client: QueryClient, options: ExplorationOptions<TNode, TEdge, TQueryFnData, TError, TQueryKey>): Exploration<TNode, TEdge>;
|
|
30
|
+
//# sourceMappingURL=query.d.ts.map
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createStore } from "@tanstack/store";
|
|
2
|
+
import { QueryObserver } from "@tanstack/query-core";
|
|
3
|
+
// src/query/index.ts
|
|
4
|
+
var ElementSource = class {
|
|
5
|
+
values = /* @__PURE__ */ new Map();
|
|
6
|
+
holders = /* @__PURE__ */ new Map();
|
|
7
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
#onActive;
|
|
9
|
+
constructor(onActive) {
|
|
10
|
+
this.#onActive = onActive;
|
|
11
|
+
}
|
|
12
|
+
subscribeChanges(callback, options) {
|
|
13
|
+
this.#listeners.add(callback);
|
|
14
|
+
if (options?.includeInitialState && this.values.size > 0) callback([...this.values].map(([key, value]) => ({
|
|
15
|
+
type: "insert",
|
|
16
|
+
key,
|
|
17
|
+
value
|
|
18
|
+
})));
|
|
19
|
+
this.#onActive(1);
|
|
20
|
+
let active = true;
|
|
21
|
+
return { unsubscribe: () => {
|
|
22
|
+
if (!active) return;
|
|
23
|
+
active = false;
|
|
24
|
+
this.#listeners.delete(callback);
|
|
25
|
+
this.#onActive(-1);
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
hold(owner, items, changes) {
|
|
29
|
+
for (const [key, value] of items) {
|
|
30
|
+
let holders = this.holders.get(key);
|
|
31
|
+
if (!holders) {
|
|
32
|
+
holders = /* @__PURE__ */ new Set();
|
|
33
|
+
this.holders.set(key, holders);
|
|
34
|
+
}
|
|
35
|
+
holders.add(owner);
|
|
36
|
+
const previous = this.values.get(key);
|
|
37
|
+
if (previous === value) continue;
|
|
38
|
+
this.values.set(key, value);
|
|
39
|
+
changes.push({
|
|
40
|
+
type: previous === void 0 ? "insert" : "update",
|
|
41
|
+
key,
|
|
42
|
+
value
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
release(owner, keys, pinned, changes) {
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
const holders = this.holders.get(key);
|
|
49
|
+
if (!holders?.delete(owner) || holders.size > 0 || pinned.has(key)) continue;
|
|
50
|
+
this.holders.delete(key);
|
|
51
|
+
const value = this.values.get(key);
|
|
52
|
+
this.values.delete(key);
|
|
53
|
+
if (value !== void 0) changes.push({
|
|
54
|
+
type: "delete",
|
|
55
|
+
key,
|
|
56
|
+
value
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
emit(changes) {
|
|
61
|
+
if (changes.length === 0) return;
|
|
62
|
+
for (const listener of [...this.#listeners]) listener(changes);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function defaultKey(item) {
|
|
66
|
+
return item.id;
|
|
67
|
+
}
|
|
68
|
+
function createExploration(client, options) {
|
|
69
|
+
const nodeKey = options.nodeKey ?? defaultKey;
|
|
70
|
+
const edgeKey = options.edgeKey ?? defaultKey;
|
|
71
|
+
const roots = new Set(options.roots);
|
|
72
|
+
const expansions = /* @__PURE__ */ new Map();
|
|
73
|
+
let subscribers = 0;
|
|
74
|
+
let destroyed = false;
|
|
75
|
+
const onActive = (delta) => {
|
|
76
|
+
const before = subscribers;
|
|
77
|
+
subscribers += delta;
|
|
78
|
+
if (before === 0 && subscribers > 0) for (const key of expansions.keys()) start(key);
|
|
79
|
+
if (before > 0 && subscribers === 0) for (const expansion of expansions.values()) halt(expansion);
|
|
80
|
+
};
|
|
81
|
+
const nodes = new ElementSource(onActive);
|
|
82
|
+
const edges = new ElementSource(onActive);
|
|
83
|
+
const store = createStore({
|
|
84
|
+
expanded: [],
|
|
85
|
+
loading: [],
|
|
86
|
+
errors: /* @__PURE__ */ new Map()
|
|
87
|
+
});
|
|
88
|
+
const readonlyStore = createStore(() => store.state);
|
|
89
|
+
function publish() {
|
|
90
|
+
const loading = [];
|
|
91
|
+
const errors = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const [key, expansion] of expansions) {
|
|
93
|
+
const result = expansion.observer.getCurrentResult();
|
|
94
|
+
if (result.isFetching) loading.push(key);
|
|
95
|
+
if (result.error) errors.set(key, result.error);
|
|
96
|
+
}
|
|
97
|
+
store.setState(() => ({
|
|
98
|
+
expanded: [...expansions.keys()],
|
|
99
|
+
loading,
|
|
100
|
+
errors
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
function apply(key, data) {
|
|
104
|
+
const expansion = expansions.get(key);
|
|
105
|
+
if (!expansion || !data) return;
|
|
106
|
+
const nodeItems = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const node of data.nodes) nodeItems.set(nodeKey(node), node);
|
|
108
|
+
const edgeItems = /* @__PURE__ */ new Map();
|
|
109
|
+
for (const edge of data.edges) edgeItems.set(edgeKey(edge), edge);
|
|
110
|
+
const nodeChanges = [];
|
|
111
|
+
const edgeChanges = [];
|
|
112
|
+
nodes.hold(key, nodeItems, nodeChanges);
|
|
113
|
+
edges.hold(key, edgeItems, edgeChanges);
|
|
114
|
+
nodes.holders.get(key)?.add(key);
|
|
115
|
+
edges.release(key, [...expansion.edges].filter((edge) => !edgeItems.has(edge)), /* @__PURE__ */ new Set(), edgeChanges);
|
|
116
|
+
nodes.release(key, [...expansion.nodes].filter((node) => node !== key && !nodeItems.has(node)), roots, nodeChanges);
|
|
117
|
+
expansion.nodes = /* @__PURE__ */ new Set([...nodeItems.keys(), key]);
|
|
118
|
+
expansion.edges = new Set(edgeItems.keys());
|
|
119
|
+
nodes.emit(nodeChanges);
|
|
120
|
+
edges.emit(edgeChanges);
|
|
121
|
+
}
|
|
122
|
+
function start(key) {
|
|
123
|
+
const expansion = expansions.get(key);
|
|
124
|
+
if (!expansion || expansion.stop || destroyed) return;
|
|
125
|
+
const { observer } = expansion;
|
|
126
|
+
expansion.stop = observer.subscribe((result) => {
|
|
127
|
+
apply(key, result.data);
|
|
128
|
+
publish();
|
|
129
|
+
});
|
|
130
|
+
apply(key, observer.getCurrentResult().data);
|
|
131
|
+
publish();
|
|
132
|
+
}
|
|
133
|
+
function halt(expansion) {
|
|
134
|
+
expansion.stop?.();
|
|
135
|
+
expansion.stop = null;
|
|
136
|
+
}
|
|
137
|
+
function expand(key) {
|
|
138
|
+
if (destroyed || expansions.has(key)) return;
|
|
139
|
+
const observer = new QueryObserver(client, options.neighbors(key));
|
|
140
|
+
expansions.set(key, {
|
|
141
|
+
observer,
|
|
142
|
+
stop: null,
|
|
143
|
+
nodes: /* @__PURE__ */ new Set([key]),
|
|
144
|
+
edges: /* @__PURE__ */ new Set()
|
|
145
|
+
});
|
|
146
|
+
if (subscribers > 0) start(key);
|
|
147
|
+
else publish();
|
|
148
|
+
}
|
|
149
|
+
function collapse(key) {
|
|
150
|
+
const expansion = expansions.get(key);
|
|
151
|
+
if (!expansion) return;
|
|
152
|
+
halt(expansion);
|
|
153
|
+
expansions.delete(key);
|
|
154
|
+
const nodeChanges = [];
|
|
155
|
+
const edgeChanges = [];
|
|
156
|
+
edges.release(key, expansion.edges, /* @__PURE__ */ new Set(), edgeChanges);
|
|
157
|
+
nodes.release(key, expansion.nodes, roots, nodeChanges);
|
|
158
|
+
edges.emit(edgeChanges);
|
|
159
|
+
nodes.emit(nodeChanges);
|
|
160
|
+
publish();
|
|
161
|
+
}
|
|
162
|
+
for (const root of roots) expand(root);
|
|
163
|
+
return {
|
|
164
|
+
nodes,
|
|
165
|
+
edges,
|
|
166
|
+
store: readonlyStore,
|
|
167
|
+
expand,
|
|
168
|
+
collapse,
|
|
169
|
+
toggle: (key) => expansions.has(key) ? collapse(key) : expand(key),
|
|
170
|
+
reset() {
|
|
171
|
+
for (const key of [...expansions.keys()]) if (!roots.has(key)) collapse(key);
|
|
172
|
+
for (const root of roots) expand(root);
|
|
173
|
+
},
|
|
174
|
+
destroy() {
|
|
175
|
+
for (const expansion of expansions.values()) halt(expansion);
|
|
176
|
+
destroyed = true;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
export { createExploration };
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","names":[],"sources":["../src/query/index.ts"],"sourcesContent":["import {\n type DefaultError,\n type QueryClient,\n type QueryKey,\n QueryObserver,\n type QueryObserverOptions,\n} from '@tanstack/query-core'\nimport { createStore, type ReadonlyStore } from '@tanstack/store'\nimport type { Key, LiveSource, SourceChange, SourceSubscription } from '../types'\n\nexport interface ExplorationData<TNode, TEdge> {\n readonly nodes: readonly TNode[]\n readonly edges: readonly TEdge[]\n}\n\nexport interface ExplorationOptions<\n TNode,\n TEdge,\n TQueryFnData = ExplorationData<TNode, TEdge>,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n> {\n readonly roots: Iterable<Key>\n readonly neighbors: (\n key: Key,\n ) => QueryObserverOptions<\n TQueryFnData,\n TError,\n ExplorationData<TNode, TEdge>,\n TQueryFnData,\n TQueryKey\n >\n readonly nodeKey?: (node: TNode) => Key\n readonly edgeKey?: (edge: TEdge) => Key\n}\n\nexport interface ExplorationState {\n readonly expanded: readonly Key[]\n readonly loading: readonly Key[]\n readonly errors: ReadonlyMap<Key, unknown>\n}\n\nexport interface Exploration<TNode, TEdge> {\n readonly nodes: LiveSource<TNode>\n readonly edges: LiveSource<TEdge>\n readonly store: ReadonlyStore<ExplorationState>\n expand(key: Key): void\n collapse(key: Key): void\n toggle(key: Key): void\n reset(): void\n destroy(): void\n}\n\ntype Listener<T> = (changes: ReadonlyArray<SourceChange<T>>) => void\n\nclass ElementSource<T> implements LiveSource<T> {\n readonly values = new Map<Key, T>()\n readonly holders = new Map<Key, Set<Key>>()\n readonly #listeners = new Set<Listener<T>>()\n readonly #onActive: (delta: number) => void\n\n constructor(onActive: (delta: number) => void) {\n this.#onActive = onActive\n }\n\n subscribeChanges(\n callback: Listener<T>,\n options?: { includeInitialState?: boolean },\n ): SourceSubscription {\n this.#listeners.add(callback)\n if (options?.includeInitialState && this.values.size > 0) {\n callback([...this.values].map(([key, value]) => ({ type: 'insert', key, value })))\n }\n this.#onActive(1)\n let active = true\n return {\n unsubscribe: () => {\n if (!active) return\n active = false\n this.#listeners.delete(callback)\n this.#onActive(-1)\n },\n }\n }\n\n hold(owner: Key, items: ReadonlyMap<Key, T>, changes: SourceChange<T>[]): void {\n for (const [key, value] of items) {\n let holders = this.holders.get(key)\n if (!holders) {\n holders = new Set()\n this.holders.set(key, holders)\n }\n holders.add(owner)\n const previous = this.values.get(key)\n if (previous === value) continue\n this.values.set(key, value)\n changes.push({ type: previous === undefined ? 'insert' : 'update', key, value })\n }\n }\n\n release(owner: Key, keys: Iterable<Key>, pinned: ReadonlySet<Key>, changes: SourceChange<T>[]) {\n for (const key of keys) {\n const holders = this.holders.get(key)\n if (!holders?.delete(owner) || holders.size > 0 || pinned.has(key)) continue\n this.holders.delete(key)\n const value = this.values.get(key)\n this.values.delete(key)\n if (value !== undefined) changes.push({ type: 'delete', key, value })\n }\n }\n\n emit(changes: ReadonlyArray<SourceChange<T>>): void {\n if (changes.length === 0) return\n for (const listener of [...this.#listeners]) listener(changes)\n }\n}\n\nfunction defaultKey(item: unknown): Key {\n return (item as { readonly id: Key }).id\n}\n\nexport function createExploration<\n TNode,\n TEdge,\n TQueryFnData = ExplorationData<TNode, TEdge>,\n TError = DefaultError,\n TQueryKey extends QueryKey = QueryKey,\n>(\n client: QueryClient,\n options: ExplorationOptions<TNode, TEdge, TQueryFnData, TError, TQueryKey>,\n): Exploration<TNode, TEdge> {\n type Observer = QueryObserver<\n TQueryFnData,\n TError,\n ExplorationData<TNode, TEdge>,\n TQueryFnData,\n TQueryKey\n >\n interface Expansion {\n readonly observer: Observer\n stop: (() => void) | null\n nodes: Set<Key>\n edges: Set<Key>\n }\n const nodeKey = options.nodeKey ?? defaultKey\n const edgeKey = options.edgeKey ?? defaultKey\n const roots = new Set(options.roots)\n const expansions = new Map<Key, Expansion>()\n let subscribers = 0\n let destroyed = false\n\n const onActive = (delta: number) => {\n const before = subscribers\n subscribers += delta\n if (before === 0 && subscribers > 0) for (const key of expansions.keys()) start(key)\n if (before > 0 && subscribers === 0)\n for (const expansion of expansions.values()) halt(expansion)\n }\n const nodes = new ElementSource<TNode>(onActive)\n const edges = new ElementSource<TEdge>(onActive)\n\n const store = createStore<ExplorationState>({ expanded: [], loading: [], errors: new Map() })\n const readonlyStore: ReadonlyStore<ExplorationState> = createStore(() => store.state)\n\n function publish(): void {\n const loading: Key[] = []\n const errors = new Map<Key, unknown>()\n for (const [key, expansion] of expansions) {\n const result = expansion.observer.getCurrentResult()\n if (result.isFetching) loading.push(key)\n if (result.error) errors.set(key, result.error)\n }\n store.setState(() => ({ expanded: [...expansions.keys()], loading, errors }))\n }\n\n function apply(key: Key, data: ExplorationData<TNode, TEdge> | undefined): void {\n const expansion = expansions.get(key)\n if (!expansion || !data) return\n const nodeItems = new Map<Key, TNode>()\n for (const node of data.nodes) nodeItems.set(nodeKey(node), node)\n const edgeItems = new Map<Key, TEdge>()\n for (const edge of data.edges) edgeItems.set(edgeKey(edge), edge)\n const nodeChanges: SourceChange<TNode>[] = []\n const edgeChanges: SourceChange<TEdge>[] = []\n nodes.hold(key, nodeItems, nodeChanges)\n edges.hold(key, edgeItems, edgeChanges)\n nodes.holders.get(key)?.add(key)\n edges.release(\n key,\n [...expansion.edges].filter((edge) => !edgeItems.has(edge)),\n new Set(),\n edgeChanges,\n )\n nodes.release(\n key,\n [...expansion.nodes].filter((node) => node !== key && !nodeItems.has(node)),\n roots,\n nodeChanges,\n )\n expansion.nodes = new Set([...nodeItems.keys(), key])\n expansion.edges = new Set(edgeItems.keys())\n nodes.emit(nodeChanges)\n edges.emit(edgeChanges)\n }\n\n function start(key: Key): void {\n const expansion = expansions.get(key)\n if (!expansion || expansion.stop || destroyed) return\n const { observer } = expansion\n expansion.stop = observer.subscribe((result) => {\n apply(key, result.data)\n publish()\n })\n apply(key, observer.getCurrentResult().data)\n publish()\n }\n\n function halt(expansion: Expansion): void {\n expansion.stop?.()\n expansion.stop = null\n }\n\n function expand(key: Key): void {\n if (destroyed || expansions.has(key)) return\n const observer = new QueryObserver(client, options.neighbors(key))\n expansions.set(key, { observer, stop: null, nodes: new Set([key]), edges: new Set() })\n if (subscribers > 0) start(key)\n else publish()\n }\n\n function collapse(key: Key): void {\n const expansion = expansions.get(key)\n if (!expansion) return\n halt(expansion)\n expansions.delete(key)\n const nodeChanges: SourceChange<TNode>[] = []\n const edgeChanges: SourceChange<TEdge>[] = []\n edges.release(key, expansion.edges, new Set(), edgeChanges)\n nodes.release(key, expansion.nodes, roots, nodeChanges)\n edges.emit(edgeChanges)\n nodes.emit(nodeChanges)\n publish()\n }\n\n for (const root of roots) expand(root)\n\n return {\n nodes,\n edges,\n store: readonlyStore,\n expand,\n collapse,\n toggle: (key) => (expansions.has(key) ? collapse(key) : expand(key)),\n reset() {\n for (const key of [...expansions.keys()]) if (!roots.has(key)) collapse(key)\n for (const root of roots) expand(root)\n },\n destroy() {\n for (const expansion of expansions.values()) halt(expansion)\n destroyed = true\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _ as GraphEventName, c as GraphStore, g as GraphEventHandler, m as ElementKeysInput, o as GraphState, r as GraphInstance } from "./mount-graph-B88WGOQu.js";
|
|
2
|
+
import { m as GraphDefinition, z as View } from "./types-D6b86Rt1.js";
|
|
3
|
+
import { CSSProperties, JSX, ReactNode, Ref } from "react";
|
|
4
|
+
export interface GraphEventProps {
|
|
5
|
+
readonly onClick?: GraphEventHandler<"click">;
|
|
6
|
+
readonly onDoubleClick?: GraphEventHandler<"doubleclick">;
|
|
7
|
+
readonly onContextMenu?: GraphEventHandler<"contextmenu">;
|
|
8
|
+
readonly onPointerEnter?: GraphEventHandler<"pointerenter">;
|
|
9
|
+
readonly onPointerLeave?: GraphEventHandler<"pointerleave">;
|
|
10
|
+
readonly onDragStart?: GraphEventHandler<"dragstart">;
|
|
11
|
+
readonly onDrag?: GraphEventHandler<"drag">;
|
|
12
|
+
readonly onDragEnd?: GraphEventHandler<"dragend">;
|
|
13
|
+
readonly onPositionChange?: GraphEventHandler<"positionchange">;
|
|
14
|
+
readonly onSelectionChange?: GraphEventHandler<"selectionchange">;
|
|
15
|
+
readonly onViewChange?: GraphEventHandler<"viewchange">;
|
|
16
|
+
}
|
|
17
|
+
export interface GraphProps<S extends string = string> extends GraphEventProps {
|
|
18
|
+
readonly definition: GraphDefinition<S>;
|
|
19
|
+
readonly ariaLabel: string;
|
|
20
|
+
readonly initialView?: View;
|
|
21
|
+
readonly selection?: ElementKeysInput;
|
|
22
|
+
readonly className?: string;
|
|
23
|
+
readonly style?: CSSProperties;
|
|
24
|
+
readonly ref?: Ref<GraphInstance<S> | null>;
|
|
25
|
+
readonly children?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
export declare function Graph<S extends string = string>(props: GraphProps<S>): JSX.Element;
|
|
28
|
+
export declare function useGraph(): GraphInstance;
|
|
29
|
+
export interface UseGraphStateOptions<T> {
|
|
30
|
+
readonly compare?: (a: T, b: T) => boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function useGraphState<T>(selector: (state: GraphState) => T, options?: UseGraphStateOptions<T>): T;
|
|
33
|
+
export declare function useGraphEvent<K extends GraphEventName>(name: K, handler: GraphEventHandler<K>): void;
|
|
34
|
+
export type { GraphInstance, GraphState, GraphStore };
|
|
35
|
+
//# sourceMappingURL=react.d.ts.map
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { t as mountGraph } from "./mount-graph-BHwq3LOr.js";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
// src/react/index.tsx
|
|
5
|
+
const eventProps = {
|
|
6
|
+
click: "onClick",
|
|
7
|
+
doubleclick: "onDoubleClick",
|
|
8
|
+
contextmenu: "onContextMenu",
|
|
9
|
+
pointerenter: "onPointerEnter",
|
|
10
|
+
pointerleave: "onPointerLeave",
|
|
11
|
+
dragstart: "onDragStart",
|
|
12
|
+
drag: "onDrag",
|
|
13
|
+
dragend: "onDragEnd",
|
|
14
|
+
positionchange: "onPositionChange",
|
|
15
|
+
selectionchange: "onSelectionChange",
|
|
16
|
+
viewchange: "onViewChange"
|
|
17
|
+
};
|
|
18
|
+
const GraphContext = createContext(null);
|
|
19
|
+
const fill = {
|
|
20
|
+
position: "absolute",
|
|
21
|
+
inset: 0
|
|
22
|
+
};
|
|
23
|
+
function useLatest(value) {
|
|
24
|
+
const latest = useRef(value);
|
|
25
|
+
useLayoutEffect(() => {
|
|
26
|
+
latest.current = value;
|
|
27
|
+
});
|
|
28
|
+
return latest;
|
|
29
|
+
}
|
|
30
|
+
function Graph(props) {
|
|
31
|
+
const { definition, ariaLabel, selection, className, style, ref, children } = props;
|
|
32
|
+
const host = useRef(null);
|
|
33
|
+
const [graph, setGraph] = useState(null);
|
|
34
|
+
const latest = useLatest(props);
|
|
35
|
+
useLayoutEffect(() => {
|
|
36
|
+
const element = host.current;
|
|
37
|
+
if (!element) return;
|
|
38
|
+
const initial = latest.current;
|
|
39
|
+
const instance = mountGraph(element, {
|
|
40
|
+
definition: initial.definition,
|
|
41
|
+
ariaLabel: initial.ariaLabel,
|
|
42
|
+
...initial.initialView ? { initialView: initial.initialView } : {},
|
|
43
|
+
...initial.selection !== void 0 ? {
|
|
44
|
+
selection: initial.selection,
|
|
45
|
+
onSelectionChange: (next) => latest.current.onSelectionChange?.(next)
|
|
46
|
+
} : {}
|
|
47
|
+
});
|
|
48
|
+
setGraph(instance);
|
|
49
|
+
return () => {
|
|
50
|
+
instance.destroy();
|
|
51
|
+
setGraph(null);
|
|
52
|
+
};
|
|
53
|
+
}, [latest]);
|
|
54
|
+
useLayoutEffect(() => {
|
|
55
|
+
graph?.update({
|
|
56
|
+
definition,
|
|
57
|
+
ariaLabel,
|
|
58
|
+
...selection !== void 0 ? { selection } : {}
|
|
59
|
+
});
|
|
60
|
+
}, [
|
|
61
|
+
graph,
|
|
62
|
+
definition,
|
|
63
|
+
ariaLabel,
|
|
64
|
+
selection
|
|
65
|
+
]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!graph) return;
|
|
68
|
+
const stops = Object.keys(eventProps).map((name) => graph.on(name, (event) => {
|
|
69
|
+
if (name === "selectionchange" && latest.current.selection !== void 0) return;
|
|
70
|
+
const handler = latest.current[eventProps[name]];
|
|
71
|
+
handler?.(event);
|
|
72
|
+
}));
|
|
73
|
+
return () => {
|
|
74
|
+
for (const stop of stops) stop();
|
|
75
|
+
};
|
|
76
|
+
}, [graph, latest]);
|
|
77
|
+
useImperativeHandle(ref, () => graph, [graph]);
|
|
78
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
79
|
+
className,
|
|
80
|
+
style: {
|
|
81
|
+
position: "relative",
|
|
82
|
+
...style
|
|
83
|
+
},
|
|
84
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
85
|
+
ref: host,
|
|
86
|
+
style: fill
|
|
87
|
+
}), graph ? /* @__PURE__ */ jsx(GraphContext, {
|
|
88
|
+
value: graph,
|
|
89
|
+
children
|
|
90
|
+
}) : null]
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function useGraph() {
|
|
94
|
+
const graph = useContext(GraphContext);
|
|
95
|
+
if (!graph) throw new Error("useGraph: call it inside <Graph>");
|
|
96
|
+
return graph;
|
|
97
|
+
}
|
|
98
|
+
function useGraphState(selector, options = {}) {
|
|
99
|
+
const compare = options.compare ?? Object.is;
|
|
100
|
+
const graph = useGraph();
|
|
101
|
+
const cache = useRef(null);
|
|
102
|
+
const subscribe = useCallback((notify) => {
|
|
103
|
+
const subscription = graph.store.subscribe(() => notify());
|
|
104
|
+
return () => subscription.unsubscribe();
|
|
105
|
+
}, [graph]);
|
|
106
|
+
const snapshot = () => {
|
|
107
|
+
const state = graph.store.get();
|
|
108
|
+
const cached = cache.current;
|
|
109
|
+
if (cached && cached.state === state && cached.selector === selector) return cached.value;
|
|
110
|
+
const value = selector(state);
|
|
111
|
+
const kept = cached && compare(cached.value, value) ? cached.value : value;
|
|
112
|
+
cache.current = {
|
|
113
|
+
state,
|
|
114
|
+
selector,
|
|
115
|
+
value: kept
|
|
116
|
+
};
|
|
117
|
+
return kept;
|
|
118
|
+
};
|
|
119
|
+
return useSyncExternalStore(subscribe, snapshot, snapshot);
|
|
120
|
+
}
|
|
121
|
+
function useGraphEvent(name, handler) {
|
|
122
|
+
const graph = useGraph();
|
|
123
|
+
const latest = useLatest(handler);
|
|
124
|
+
useEffect(() => graph.on(name, (event) => latest.current(event)), [
|
|
125
|
+
graph,
|
|
126
|
+
name,
|
|
127
|
+
latest
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
export { Graph, useGraph, useGraphEvent, useGraphState };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","names":[],"sources":["../src/react/index.tsx"],"sourcesContent":["import {\n type CSSProperties,\n createContext,\n type JSX,\n type ReactNode,\n type Ref,\n useCallback,\n useContext,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react'\nimport type {\n ElementKeysInput,\n GraphEventHandler,\n GraphEventName,\n GraphEvents,\n} from '../host/events'\nimport {\n type GraphInstance,\n type GraphState,\n type GraphStore,\n mountGraph,\n} from '../host/mount-graph'\nimport type { GraphDefinition, View } from '../types'\n\nexport interface GraphEventProps {\n readonly onClick?: GraphEventHandler<'click'>\n readonly onDoubleClick?: GraphEventHandler<'doubleclick'>\n readonly onContextMenu?: GraphEventHandler<'contextmenu'>\n readonly onPointerEnter?: GraphEventHandler<'pointerenter'>\n readonly onPointerLeave?: GraphEventHandler<'pointerleave'>\n readonly onDragStart?: GraphEventHandler<'dragstart'>\n readonly onDrag?: GraphEventHandler<'drag'>\n readonly onDragEnd?: GraphEventHandler<'dragend'>\n readonly onPositionChange?: GraphEventHandler<'positionchange'>\n readonly onSelectionChange?: GraphEventHandler<'selectionchange'>\n readonly onViewChange?: GraphEventHandler<'viewchange'>\n}\n\nconst eventProps: Readonly<Record<GraphEventName, keyof GraphEventProps>> = {\n click: 'onClick',\n doubleclick: 'onDoubleClick',\n contextmenu: 'onContextMenu',\n pointerenter: 'onPointerEnter',\n pointerleave: 'onPointerLeave',\n dragstart: 'onDragStart',\n drag: 'onDrag',\n dragend: 'onDragEnd',\n positionchange: 'onPositionChange',\n selectionchange: 'onSelectionChange',\n viewchange: 'onViewChange',\n}\n\nexport interface GraphProps<S extends string = string> extends GraphEventProps {\n readonly definition: GraphDefinition<S>\n readonly ariaLabel: string\n readonly initialView?: View\n readonly selection?: ElementKeysInput\n readonly className?: string\n readonly style?: CSSProperties\n readonly ref?: Ref<GraphInstance<S> | null>\n readonly children?: ReactNode\n}\n\nconst GraphContext = createContext<GraphInstance | null>(null)\n\nconst fill: CSSProperties = { position: 'absolute', inset: 0 }\n\nfunction useLatest<T>(value: T): { readonly current: T } {\n const latest = useRef(value)\n useLayoutEffect(() => {\n latest.current = value\n })\n return latest\n}\n\nexport function Graph<S extends string = string>(props: GraphProps<S>): JSX.Element {\n const { definition, ariaLabel, selection, className, style, ref, children } = props\n const host = useRef<HTMLDivElement>(null)\n const [graph, setGraph] = useState<GraphInstance<S> | null>(null)\n const latest = useLatest(props)\n\n useLayoutEffect(() => {\n const element = host.current\n if (!element) return\n const initial = latest.current\n const instance = mountGraph(element, {\n definition: initial.definition,\n ariaLabel: initial.ariaLabel,\n ...(initial.initialView ? { initialView: initial.initialView } : {}),\n ...(initial.selection !== undefined\n ? {\n selection: initial.selection,\n onSelectionChange: (next) => latest.current.onSelectionChange?.(next),\n }\n : {}),\n })\n setGraph(instance)\n return () => {\n instance.destroy()\n setGraph(null)\n }\n }, [latest])\n\n useLayoutEffect(() => {\n graph?.update({ definition, ariaLabel, ...(selection !== undefined ? { selection } : {}) })\n }, [graph, definition, ariaLabel, selection])\n\n useEffect(() => {\n if (!graph) return\n const stops = (Object.keys(eventProps) as GraphEventName[]).map((name) =>\n graph.on(name, (event) => {\n if (name === 'selectionchange' && latest.current.selection !== undefined) return\n const handler = latest.current[eventProps[name]] as ((event: unknown) => void) | undefined\n handler?.(event)\n }),\n )\n return () => {\n for (const stop of stops) stop()\n }\n }, [graph, latest])\n\n useImperativeHandle<GraphInstance<S> | null, GraphInstance<S> | null>(ref, () => graph, [graph])\n\n return (\n <div className={className} style={{ position: 'relative', ...style }}>\n <div ref={host} style={fill} />\n {graph ? <GraphContext value={graph}>{children}</GraphContext> : null}\n </div>\n )\n}\n\nexport function useGraph(): GraphInstance {\n const graph = useContext(GraphContext)\n if (!graph) throw new Error('useGraph: call it inside <Graph>')\n return graph\n}\n\ninterface Selected<T> {\n readonly state: GraphState\n readonly selector: (state: GraphState) => T\n readonly value: T\n}\n\nexport interface UseGraphStateOptions<T> {\n readonly compare?: (a: T, b: T) => boolean\n}\n\nexport function useGraphState<T>(\n selector: (state: GraphState) => T,\n options: UseGraphStateOptions<T> = {},\n): T {\n const compare = options.compare ?? Object.is\n const graph = useGraph()\n const cache = useRef<Selected<T> | null>(null)\n const subscribe = useCallback(\n (notify: () => void) => {\n const subscription = graph.store.subscribe(() => notify())\n return () => subscription.unsubscribe()\n },\n [graph],\n )\n const snapshot = (): T => {\n const state = graph.store.get()\n const cached = cache.current\n if (cached && cached.state === state && cached.selector === selector) return cached.value\n const value = selector(state)\n const kept = cached && compare(cached.value, value) ? cached.value : value\n cache.current = { state, selector, value: kept }\n return kept\n }\n return useSyncExternalStore(subscribe, snapshot, snapshot)\n}\n\nexport function useGraphEvent<K extends GraphEventName>(\n name: K,\n handler: GraphEventHandler<K>,\n): void {\n const graph = useGraph()\n const latest = useLatest(handler)\n useEffect(\n () => graph.on(name, (event: GraphEvents[K]) => latest.current(event)),\n [graph, name, latest],\n )\n}\n\nexport type { GraphInstance, GraphState, GraphStore }\n"],"mappings":"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"}
|
package/dist/theme.css
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--graph-paper: #f7f7f4;
|
|
3
|
+
--graph-paper-raised: #ffffff;
|
|
4
|
+
--graph-grid: #c4e0dd;
|
|
5
|
+
--graph-ink: #191d22;
|
|
6
|
+
--graph-ink-muted: #5b6167;
|
|
7
|
+
--graph-node: #474e55;
|
|
8
|
+
--graph-edge: #7f858c;
|
|
9
|
+
--graph-group-fill: #e7efee;
|
|
10
|
+
--graph-group-line: #6e8d8b;
|
|
11
|
+
--graph-accent: #c93700;
|
|
12
|
+
--graph-on-accent: #ffffff;
|
|
13
|
+
--graph-focus-ring: #c93700;
|
|
14
|
+
--graph-halo: #c9370047;
|
|
15
|
+
--graph-label-halo: #f7f7f4;
|
|
16
|
+
--graph-node-ring: #f7f7f4;
|
|
17
|
+
--graph-badge-fill: #191d22;
|
|
18
|
+
--graph-badge-ink: #f7f7f4;
|
|
19
|
+
--graph-cat-1: #2769b7;
|
|
20
|
+
--graph-cat-2: #a47c13;
|
|
21
|
+
--graph-cat-3: #12867c;
|
|
22
|
+
--graph-cat-4: #9b357f;
|
|
23
|
+
--graph-cat-5: #4189ad;
|
|
24
|
+
--graph-cat-6: #448335;
|
|
25
|
+
--graph-cat-7: #5d4099;
|
|
26
|
+
--graph-cat-8: #745338;
|
|
27
|
+
--graph-font: "Atkinson Hyperlegible Next", system-ui, sans-serif;
|
|
28
|
+
color-scheme: light;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.dark,
|
|
32
|
+
[data-theme="dark"] {
|
|
33
|
+
--graph-paper: #0f1113;
|
|
34
|
+
--graph-paper-raised: #171a1d;
|
|
35
|
+
--graph-grid: #1e3233;
|
|
36
|
+
--graph-ink: #efeeeb;
|
|
37
|
+
--graph-ink-muted: #a6abb1;
|
|
38
|
+
--graph-node: #b7bfc7;
|
|
39
|
+
--graph-edge: #6f757b;
|
|
40
|
+
--graph-group-fill: #131b1c;
|
|
41
|
+
--graph-group-line: #5d7b7b;
|
|
42
|
+
--graph-accent: #fa8054;
|
|
43
|
+
--graph-on-accent: #0f1113;
|
|
44
|
+
--graph-focus-ring: #fa8054;
|
|
45
|
+
--graph-halo: #fa805447;
|
|
46
|
+
--graph-label-halo: #0f1113;
|
|
47
|
+
--graph-node-ring: #0f1113;
|
|
48
|
+
--graph-badge-fill: #efeeeb;
|
|
49
|
+
--graph-badge-ink: #0f1113;
|
|
50
|
+
--graph-cat-1: #6fa7ee;
|
|
51
|
+
--graph-cat-2: #e7bf57;
|
|
52
|
+
--graph-cat-3: #5cc6b9;
|
|
53
|
+
--graph-cat-4: #e17eb9;
|
|
54
|
+
--graph-cat-5: #9cd3f1;
|
|
55
|
+
--graph-cat-6: #81ce70;
|
|
56
|
+
--graph-cat-7: #9e87e0;
|
|
57
|
+
--graph-cat-8: #aa8b74;
|
|
58
|
+
color-scheme: dark;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@media (prefers-color-scheme: dark) {
|
|
62
|
+
:root:not(.light):not([data-theme="light"]) {
|
|
63
|
+
--graph-paper: #0f1113;
|
|
64
|
+
--graph-paper-raised: #171a1d;
|
|
65
|
+
--graph-grid: #1e3233;
|
|
66
|
+
--graph-ink: #efeeeb;
|
|
67
|
+
--graph-ink-muted: #a6abb1;
|
|
68
|
+
--graph-node: #b7bfc7;
|
|
69
|
+
--graph-edge: #6f757b;
|
|
70
|
+
--graph-group-fill: #131b1c;
|
|
71
|
+
--graph-group-line: #5d7b7b;
|
|
72
|
+
--graph-accent: #fa8054;
|
|
73
|
+
--graph-on-accent: #0f1113;
|
|
74
|
+
--graph-focus-ring: #fa8054;
|
|
75
|
+
--graph-halo: #fa805447;
|
|
76
|
+
--graph-label-halo: #0f1113;
|
|
77
|
+
--graph-node-ring: #0f1113;
|
|
78
|
+
--graph-badge-fill: #efeeeb;
|
|
79
|
+
--graph-badge-ink: #0f1113;
|
|
80
|
+
--graph-cat-1: #6fa7ee;
|
|
81
|
+
--graph-cat-2: #e7bf57;
|
|
82
|
+
--graph-cat-3: #5cc6b9;
|
|
83
|
+
--graph-cat-4: #e17eb9;
|
|
84
|
+
--graph-cat-5: #9cd3f1;
|
|
85
|
+
--graph-cat-6: #81ce70;
|
|
86
|
+
--graph-cat-7: #9e87e0;
|
|
87
|
+
--graph-cat-8: #aa8b74;
|
|
88
|
+
color-scheme: dark;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
type Key = string | number;
|
|
2
|
+
interface Point {
|
|
3
|
+
readonly x: number;
|
|
4
|
+
readonly y: number;
|
|
5
|
+
}
|
|
6
|
+
interface View {
|
|
7
|
+
readonly x: number;
|
|
8
|
+
readonly y: number;
|
|
9
|
+
readonly zoom: number;
|
|
10
|
+
}
|
|
11
|
+
type Accessor<T, V> = (datum: T) => V;
|
|
12
|
+
type FieldOf<T, V> = { [K in keyof T]-?: T[K] extends V | null | undefined ? K : never; }[keyof T] & string;
|
|
13
|
+
type KeyFieldOf<T> = { [K in keyof T]-?: T[K] extends Key ? (Partial<Pick<T, K>> extends Pick<T, K> ? never : K) : never; }[keyof T] & string;
|
|
14
|
+
type Channel<T, V> = FieldOf<T, V> | Accessor<T, V | null | undefined>;
|
|
15
|
+
type KeyChannel<T> = KeyFieldOf<T> | Accessor<T, Key>;
|
|
16
|
+
type Style<T, V> = V | Accessor<T, V>;
|
|
17
|
+
interface SourceChange<T> {
|
|
18
|
+
readonly type: "insert" | "update" | "delete";
|
|
19
|
+
readonly key: Key;
|
|
20
|
+
readonly value: T;
|
|
21
|
+
}
|
|
22
|
+
interface SourceSubscription {
|
|
23
|
+
unsubscribe(): void;
|
|
24
|
+
}
|
|
25
|
+
interface LiveSource<T> {
|
|
26
|
+
subscribeChanges(callback: (changes: ReadonlyArray<SourceChange<T>>) => void, options?: {
|
|
27
|
+
includeInitialState?: boolean;
|
|
28
|
+
}): SourceSubscription;
|
|
29
|
+
}
|
|
30
|
+
type GraphData<T> = Iterable<T> | LiveSource<T>;
|
|
31
|
+
type NodeShape = "circle" | "square" | "diamond";
|
|
32
|
+
type EdgeDash = "solid" | "dashed" | "dotted";
|
|
33
|
+
type EdgeArrow = "none" | "source" | "target" | "both";
|
|
34
|
+
type BadgeCorner = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
|
|
35
|
+
interface Badge {
|
|
36
|
+
readonly text?: string;
|
|
37
|
+
}
|
|
38
|
+
interface LabelOptions<T> {
|
|
39
|
+
readonly text: Channel<T, string>;
|
|
40
|
+
readonly secondary?: Channel<T, string>;
|
|
41
|
+
}
|
|
42
|
+
interface NodeOptions<T> {
|
|
43
|
+
readonly key?: KeyChannel<T>;
|
|
44
|
+
readonly color?: Channel<T, string | number | boolean>;
|
|
45
|
+
readonly size?: Channel<T, number>;
|
|
46
|
+
readonly label?: Channel<T, string> | LabelOptions<T>;
|
|
47
|
+
readonly x?: Channel<T, number>;
|
|
48
|
+
readonly y?: Channel<T, number>;
|
|
49
|
+
readonly shape?: Style<T, NodeShape>;
|
|
50
|
+
readonly fill?: Style<T, string>;
|
|
51
|
+
readonly stroke?: Style<T, string>;
|
|
52
|
+
readonly strokeWidth?: Style<T, number>;
|
|
53
|
+
readonly opacity?: Style<T, number>;
|
|
54
|
+
readonly image?: Style<T, string | null>;
|
|
55
|
+
readonly badges?: Readonly<Partial<Record<BadgeCorner, Accessor<T, Badge | null | undefined>>>>;
|
|
56
|
+
readonly units?: "world" | "screen";
|
|
57
|
+
}
|
|
58
|
+
interface EdgeOptions<T> {
|
|
59
|
+
readonly key?: KeyChannel<T>;
|
|
60
|
+
readonly source: KeyChannel<T>;
|
|
61
|
+
readonly target: KeyChannel<T>;
|
|
62
|
+
readonly color?: Channel<T, string | number | boolean>;
|
|
63
|
+
readonly width?: Channel<T, number>;
|
|
64
|
+
readonly label?: Channel<T, string>;
|
|
65
|
+
readonly dash?: Style<T, EdgeDash>;
|
|
66
|
+
readonly arrow?: Style<T, EdgeArrow>;
|
|
67
|
+
readonly stroke?: Style<T, string>;
|
|
68
|
+
readonly opacity?: Style<T, number>;
|
|
69
|
+
}
|
|
70
|
+
interface Mark {
|
|
71
|
+
readonly type: "nodes" | "edges";
|
|
72
|
+
}
|
|
73
|
+
interface NodeMark<T> extends Mark {
|
|
74
|
+
readonly type: "nodes";
|
|
75
|
+
readonly data: GraphData<T>;
|
|
76
|
+
readonly options: NodeOptions<T>;
|
|
77
|
+
}
|
|
78
|
+
interface EdgeMark<T> extends Mark {
|
|
79
|
+
readonly type: "edges";
|
|
80
|
+
readonly data: GraphData<T>;
|
|
81
|
+
readonly options: EdgeOptions<T>;
|
|
82
|
+
}
|
|
83
|
+
type ColorScale = ((value: unknown) => string) | {
|
|
84
|
+
readonly type?: "categorical";
|
|
85
|
+
readonly domain?: readonly unknown[];
|
|
86
|
+
readonly range?: readonly string[];
|
|
87
|
+
} | {
|
|
88
|
+
readonly type: "sequential";
|
|
89
|
+
readonly domain?: readonly [number, number];
|
|
90
|
+
readonly range?: readonly string[];
|
|
91
|
+
};
|
|
92
|
+
type NumberScaleType = "linear" | "sqrt";
|
|
93
|
+
type NumberScale = ((value: number) => number) | {
|
|
94
|
+
readonly type?: NumberScaleType;
|
|
95
|
+
readonly domain?: readonly [number, number];
|
|
96
|
+
readonly range?: readonly [number, number];
|
|
97
|
+
};
|
|
98
|
+
interface ScalesOptions {
|
|
99
|
+
readonly color?: ColorScale;
|
|
100
|
+
readonly size?: NumberScale;
|
|
101
|
+
readonly width?: NumberScale;
|
|
102
|
+
}
|
|
103
|
+
interface StateStyle {
|
|
104
|
+
readonly opacity?: number;
|
|
105
|
+
readonly fill?: string;
|
|
106
|
+
readonly stroke?: string;
|
|
107
|
+
readonly strokeWidth?: number;
|
|
108
|
+
readonly halo?: boolean;
|
|
109
|
+
readonly label?: boolean;
|
|
110
|
+
}
|
|
111
|
+
type SelectMode = "single" | "multiple" | false;
|
|
112
|
+
interface InteractionOptions {
|
|
113
|
+
readonly pan?: boolean;
|
|
114
|
+
readonly zoom?: boolean | {
|
|
115
|
+
readonly speed?: number;
|
|
116
|
+
};
|
|
117
|
+
readonly select?: SelectMode;
|
|
118
|
+
readonly drag?: boolean;
|
|
119
|
+
readonly lasso?: boolean;
|
|
120
|
+
readonly keyboard?: boolean;
|
|
121
|
+
}
|
|
122
|
+
interface ResolvedInteraction {
|
|
123
|
+
readonly pan: boolean;
|
|
124
|
+
readonly zoom: false | {
|
|
125
|
+
readonly speed: number;
|
|
126
|
+
};
|
|
127
|
+
readonly select: SelectMode;
|
|
128
|
+
readonly drag: boolean;
|
|
129
|
+
readonly lasso: boolean;
|
|
130
|
+
readonly keyboard: boolean;
|
|
131
|
+
}
|
|
132
|
+
interface TransitionOptions {
|
|
133
|
+
readonly positions?: number;
|
|
134
|
+
}
|
|
135
|
+
interface ResolvedTransitions {
|
|
136
|
+
readonly positions: number;
|
|
137
|
+
}
|
|
138
|
+
type BuiltInState = "selected" | "hovered" | "focused";
|
|
139
|
+
type FreeState<S extends string> = Exclude<S, BuiltInState>;
|
|
140
|
+
type StateStyles<S extends string> = { readonly [K in S]: StateStyle; };
|
|
141
|
+
interface GraphDefinitionOptions<S extends string = string> {
|
|
142
|
+
readonly marks: readonly Mark[];
|
|
143
|
+
readonly scales?: ScalesOptions;
|
|
144
|
+
readonly states?: StateStyles<S>;
|
|
145
|
+
readonly interaction?: InteractionOptions;
|
|
146
|
+
readonly transitions?: TransitionOptions;
|
|
147
|
+
}
|
|
148
|
+
interface GraphDefinition<S extends string = string> {
|
|
149
|
+
readonly marks: readonly Mark[];
|
|
150
|
+
readonly scales: ScalesOptions;
|
|
151
|
+
readonly states: StateStyles<S>;
|
|
152
|
+
readonly interaction: ResolvedInteraction;
|
|
153
|
+
readonly transitions: ResolvedTransitions;
|
|
154
|
+
}
|
|
155
|
+
export { ResolvedTransitions as A, NodeMark as C, NumberScaleType as D, NumberScale as E, StateStyle as F, StateStyles as I, Style as L, SelectMode as M, SourceChange as N, Point as O, SourceSubscription as P, TransitionOptions as R, Mark as S, NodeShape as T, Key as _, Channel as a, LabelOptions as b, EdgeDash as c, FieldOf as d, FreeState as f, InteractionOptions as g, GraphDefinitionOptions as h, BuiltInState as i, ScalesOptions as j, ResolvedInteraction as k, EdgeMark as l, GraphDefinition as m, Badge as n, ColorScale as o, GraphData as p, BadgeCorner as r, EdgeArrow as s, Accessor as t, EdgeOptions as u, KeyChannel as v, NodeOptions as w, LiveSource as x, KeyFieldOf as y, View as z };
|
|
156
|
+
//# sourceMappingURL=types-D6b86Rt1.d.ts.map
|