@dxos/app-graph 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/chunk-graph-builder.mjs +1901 -0
- package/dist/lib/chunk-graph-builder.mjs.map +1 -0
- package/dist/lib/index.mjs +281 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/scheduler.browser.mjs +2 -0
- package/dist/lib/scheduler.mjs +12 -0
- package/dist/lib/scheduler.mjs.map +1 -0
- package/dist/lib/testing.mjs +28 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/atoms.d.ts +1 -1
- package/dist/types/src/atoms.d.ts.map +1 -1
- package/dist/types/src/graph-builder.d.ts +128 -9
- package/dist/types/src/graph-builder.d.ts.map +1 -1
- package/dist/types/src/graph.d.ts +4 -1
- package/dist/types/src/graph.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/node-matcher.d.ts +1 -1
- package/dist/types/src/node-matcher.d.ts.map +1 -1
- package/dist/types/src/node.d.ts +14 -2
- package/dist/types/src/node.d.ts.map +1 -1
- package/dist/types/src/path-resolution.d.ts +69 -0
- package/dist/types/src/path-resolution.d.ts.map +1 -0
- package/dist/types/src/path-resolution.test.d.ts +2 -0
- package/dist/types/src/path-resolution.test.d.ts.map +1 -0
- package/dist/types/src/stories/EchoGraph.stories.d.ts.map +1 -1
- package/dist/types/src/testing/setup-graph-builder.d.ts +1 -1
- package/dist/types/src/testing/setup-graph-builder.d.ts.map +1 -1
- package/dist/types/src/util.test.d.ts +2 -0
- package/dist/types/src/util.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -21
- package/src/atoms.ts +1 -1
- package/src/graph-builder.test.ts +21 -1
- package/src/graph-builder.ts +327 -58
- package/src/graph.test.ts +1 -1
- package/src/graph.ts +22 -16
- package/src/index.ts +1 -0
- package/src/node-matcher.test.ts +1 -1
- package/src/node-matcher.ts +1 -1
- package/src/node.ts +19 -2
- package/src/path-resolution.test.ts +520 -0
- package/src/path-resolution.ts +355 -0
- package/src/stories/EchoGraph.stories.tsx +97 -121
- package/src/testing/setup-graph-builder.ts +1 -1
- package/src/util.test.ts +85 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-YMTZ7MPW.mjs +0 -1509
- package/dist/lib/neutral/chunk-YMTZ7MPW.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -40
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/scheduler.mjs +0 -15
- package/dist/lib/neutral/scheduler.mjs.map +0 -7
- package/dist/lib/neutral/testing/index.mjs +0 -40
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
package/src/graph.ts
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Atom, Registry } from '@effect-atom/atom
|
|
5
|
+
import { Atom, Registry } from '@effect-atom/atom';
|
|
6
6
|
import * as Function from 'effect/Function';
|
|
7
7
|
import * as Option from 'effect/Option';
|
|
8
8
|
import * as Pipeable from 'effect/Pipeable';
|
|
9
|
-
import * as Record from 'effect/Record';
|
|
10
9
|
|
|
11
10
|
import { Event, Trigger } from '@dxos/async';
|
|
12
11
|
import { todo } from '@dxos/debug';
|
|
@@ -144,11 +143,11 @@ class GraphImpl implements WritableGraph {
|
|
|
144
143
|
readonly _onRemoveNode?: GraphProps['onRemoveNode'];
|
|
145
144
|
|
|
146
145
|
readonly _registry: Registry.Registry;
|
|
147
|
-
readonly _expanded =
|
|
146
|
+
readonly _expanded = new Set<string>();
|
|
148
147
|
readonly _pendingExpands = new Set<string>();
|
|
149
|
-
readonly _initialized =
|
|
150
|
-
readonly _initialEdges =
|
|
151
|
-
readonly _initialNodes =
|
|
148
|
+
readonly _initialized = new Set<string>();
|
|
149
|
+
readonly _initialEdges = new Map<string, Edges>();
|
|
150
|
+
readonly _initialNodes = new Map<string, Option.Option<Node.Node>>([
|
|
152
151
|
[
|
|
153
152
|
Node.RootId,
|
|
154
153
|
this._constructNode({
|
|
@@ -162,7 +161,7 @@ class GraphImpl implements WritableGraph {
|
|
|
162
161
|
|
|
163
162
|
/** @internal */
|
|
164
163
|
readonly _node = Atom.family<string, Atom.Writable<Option.Option<Node.Node>>>((id) => {
|
|
165
|
-
const initial =
|
|
164
|
+
const initial = this._initialNodes.get(id) ?? Option.none();
|
|
166
165
|
return Atom.make<Option.Option<Node.Node>>(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:node:${id}`));
|
|
167
166
|
});
|
|
168
167
|
|
|
@@ -175,7 +174,7 @@ class GraphImpl implements WritableGraph {
|
|
|
175
174
|
});
|
|
176
175
|
|
|
177
176
|
readonly _edges = Atom.family<string, Atom.Writable<Edges>>((id) => {
|
|
178
|
-
const initial =
|
|
177
|
+
const initial = this._initialEdges.get(id) ?? ({} as Edges);
|
|
179
178
|
return Atom.make<Edges>(initial).pipe(Atom.keepAlive, Atom.withLabel(`graph:edges:${id}`));
|
|
180
179
|
});
|
|
181
180
|
|
|
@@ -243,13 +242,13 @@ class GraphImpl implements WritableGraph {
|
|
|
243
242
|
|
|
244
243
|
if (nodes) {
|
|
245
244
|
nodes.forEach((node) => {
|
|
246
|
-
|
|
245
|
+
this._initialNodes.set(node.id, this._constructNode(node));
|
|
247
246
|
});
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
if (edges) {
|
|
251
250
|
Object.entries(edges).forEach(([source, edges]) => {
|
|
252
|
-
|
|
251
|
+
this._initialEdges.set(source, edges);
|
|
253
252
|
});
|
|
254
253
|
}
|
|
255
254
|
}
|
|
@@ -667,10 +666,10 @@ export function waitForPath(
|
|
|
667
666
|
*/
|
|
668
667
|
const initializeImpl = async <T extends ExpandableGraph | WritableGraph>(graph: T, id: string): Promise<T> => {
|
|
669
668
|
const internal = getInternal(graph);
|
|
670
|
-
const initialized =
|
|
669
|
+
const initialized = internal._initialized.has(id);
|
|
671
670
|
log('initialize', { id, initialized });
|
|
672
671
|
if (!initialized) {
|
|
673
|
-
|
|
672
|
+
internal._initialized.add(id);
|
|
674
673
|
await internal._onInitialize?.(id);
|
|
675
674
|
}
|
|
676
675
|
return graph;
|
|
@@ -680,6 +679,9 @@ const initializeImpl = async <T extends ExpandableGraph | WritableGraph>(graph:
|
|
|
680
679
|
* Initialize a node in the graph.
|
|
681
680
|
*
|
|
682
681
|
* Fires the `onInitialize` callback to provide initial data for a node.
|
|
682
|
+
*
|
|
683
|
+
* TODO(wittjosiah): Remove? No graph-builder extension declares a `resolver`, so `onInitialize` has
|
|
684
|
+
* nothing to run; callers expand the nodes they need explicitly.
|
|
683
685
|
*/
|
|
684
686
|
export function initialize<T extends ExpandableGraph | WritableGraph>(graph: T, id: string): Promise<T>;
|
|
685
687
|
export function initialize(id: string): <T extends ExpandableGraph | WritableGraph>(graph: T) => Promise<T>;
|
|
@@ -718,10 +720,10 @@ const expandImpl = <T extends ExpandableGraph | WritableGraph>(
|
|
|
718
720
|
return graph;
|
|
719
721
|
}
|
|
720
722
|
|
|
721
|
-
const expanded =
|
|
723
|
+
const expanded = internal._expanded.has(key);
|
|
722
724
|
log('expand', { key, expanded });
|
|
723
725
|
if (!expanded) {
|
|
724
|
-
|
|
726
|
+
internal._expanded.add(key);
|
|
725
727
|
internal._onExpand?.(id, normalizedRelation);
|
|
726
728
|
}
|
|
727
729
|
return graph;
|
|
@@ -878,13 +880,17 @@ const addNodeImpl = <T extends WritableGraph>(graph: T, nodeArg: Node.NodeArg<an
|
|
|
878
880
|
const typeChanged = existing.type !== type;
|
|
879
881
|
const dataChanged = !shallowEqual(existing.data, data);
|
|
880
882
|
const propertiesChanged = Object.keys(properties).some((key) => existing.properties[key] !== properties[key]);
|
|
883
|
+
// `changed` is on the visit log because counting `existing node` lines alone measures how often a
|
|
884
|
+
// node was re-offered, not how often it actually changed — two very different costs.
|
|
885
|
+
const changed = typeChanged || dataChanged || propertiesChanged;
|
|
881
886
|
log('existing node', {
|
|
882
887
|
id,
|
|
888
|
+
changed,
|
|
883
889
|
typeChanged,
|
|
884
890
|
dataChanged,
|
|
885
891
|
propertiesChanged,
|
|
886
892
|
});
|
|
887
|
-
if (
|
|
893
|
+
if (changed) {
|
|
888
894
|
log('updating node', { id, type, data, properties });
|
|
889
895
|
const newNode = Option.some({
|
|
890
896
|
...existing,
|
|
@@ -908,7 +914,7 @@ const addNodeImpl = <T extends WritableGraph>(graph: T, nodeArg: Node.NodeArg<an
|
|
|
908
914
|
for (const pendingKey of toApply) {
|
|
909
915
|
internal._pendingExpands.delete(pendingKey);
|
|
910
916
|
const relation = relationFromKey(primaryParts(pendingKey)[1]);
|
|
911
|
-
|
|
917
|
+
internal._expanded.add(pendingKey);
|
|
912
918
|
internal._onExpand?.(id, relation);
|
|
913
919
|
}
|
|
914
920
|
},
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * as Graph from './graph';
|
|
|
8
8
|
export * as GraphBuilder from './graph-builder';
|
|
9
9
|
export * as Node from './node';
|
|
10
10
|
export * as NodeMatcher from './node-matcher';
|
|
11
|
+
export * as PathResolution from './path-resolution';
|
|
11
12
|
export { getParentId, getSegmentId, qualifyId } from './util';
|
|
12
13
|
|
|
13
14
|
// TODO(wittjosiah): Direct re-export needed for portable type references.
|
package/src/node-matcher.test.ts
CHANGED
package/src/node-matcher.ts
CHANGED
package/src/node.ts
CHANGED
|
@@ -95,8 +95,9 @@ export type NodeArg<TData, TProperties extends Record<string, any> = Record<stri
|
|
|
95
95
|
/** Will automatically add nodes with an edge from this node to each. */
|
|
96
96
|
nodes?: NodeArg<unknown>[];
|
|
97
97
|
|
|
98
|
-
/** Will automatically add actions with an edge from this node to each.
|
|
99
|
-
|
|
98
|
+
/** Will automatically add actions with an edge from this node to each. An action child may itself
|
|
99
|
+
* be an action group (e.g. a toolbar dropdown group), so groups are accepted alongside actions. */
|
|
100
|
+
actions?: NodeArg<ActionData<any> | typeof actionGroupSymbol>[];
|
|
100
101
|
|
|
101
102
|
/** Will automatically add specified edges. */
|
|
102
103
|
edges?: [string, RelationInput][];
|
|
@@ -114,6 +115,9 @@ export type InvokeProps = {
|
|
|
114
115
|
path?: string[];
|
|
115
116
|
|
|
116
117
|
caller?: string;
|
|
118
|
+
|
|
119
|
+
/** Input modifiers held during the gesture that triggered the action (e.g. shift-clicking a menu item). */
|
|
120
|
+
modifiers?: { shift?: boolean };
|
|
117
121
|
};
|
|
118
122
|
|
|
119
123
|
/**
|
|
@@ -154,6 +158,19 @@ export type ActionLike = Action | ActionGroup;
|
|
|
154
158
|
|
|
155
159
|
export const isActionLike = (data: unknown): data is Action | ActionGroup => isAction(data) || isActionGroup(data);
|
|
156
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Tests whether a node's `disposition` property (a single string or an array, letting one node opt
|
|
163
|
+
* into multiple surfaces at once) includes any of `key`. Every surface that routes nodes/actions by
|
|
164
|
+
* disposition (toolbar, nav-tree list-item, sigil menu, …) should filter through this rather than
|
|
165
|
+
* comparing `properties.disposition` directly, so a node can multi-target surfaces.
|
|
166
|
+
*/
|
|
167
|
+
export const hasDisposition = (node: Pick<Node, 'properties'>, key: string | string[]): boolean => {
|
|
168
|
+
const disposition = node.properties.disposition;
|
|
169
|
+
const dispositions = Array.isArray(disposition) ? disposition : disposition !== undefined ? [disposition] : [];
|
|
170
|
+
const keys = Array.isArray(key) ? key : [key];
|
|
171
|
+
return dispositions.some((candidate) => keys.includes(candidate));
|
|
172
|
+
};
|
|
173
|
+
|
|
157
174
|
//
|
|
158
175
|
// Node Factories
|
|
159
176
|
//
|