Skip to content

Commit 84e2d95

Browse files
authored
feat(graph-layers): Add zod graph stylesheet schema dependencies (#333)
1 parent 5f02d0a commit 84e2d95

File tree

10 files changed

+579
-54
lines changed

10 files changed

+579
-54
lines changed

modules/editable-layers/src/lib/layers/segments-layer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {ArrowStyles, DEFAULT_STYLE, MAX_ARROWS} from '../style';
66
import {NebulaLayer} from '../nebula-layer';
77
import {toDeckColor} from '../../utils/utils';
88
import {DeckCache} from '../deck-renderer/deck-cache';
9-
import {PathMarkerLayer} from '@deck.gl-community/layers';
9+
import {PathMarkerLayer} from '../../../../layers/src/path-marker-layer/path-marker-layer';
1010

1111
const NEBULA_TO_DECK_DIRECTIONS = {
1212
[ArrowStyles.NONE]: {forward: false, backward: false},

modules/graph-layers/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
"raf": "^3.4.1"
5656
},
5757
"devDependencies": {
58-
"ngraph.generators": "^20.1.0"
58+
"ngraph.generators": "^20.1.0",
59+
"zod": "^4.0.0"
60+
},
61+
"peerDependencies": {
62+
"zod": "^3.23.8 || ^4.0.0"
5963
}
6064
}

modules/graph-layers/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ export {StyleEngine} from './style/style-engine';
3030
export {GraphStyleEngine} from './style/graph-style-engine';
3131
export type {
3232
GraphStylesheet,
33+
GraphStylesheetInput,
34+
GraphStylesheetParsed,
3335
GraphStyleAttributeReference,
3436
GraphStyleScale,
3537
GraphStyleScaleType,
3638
GraphStyleValue
3739
} from './style/graph-style-engine';
40+
export {GraphStylesheetSchema} from './style/graph-style-engine';
3841
export {
3942
DEFAULT_GRAPH_LAYER_STYLESHEET,
4043
type GraphLayerStylesheet,

modules/graph-layers/src/layers/graph-layer.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,17 @@ export class GraphLayer extends CompositeLayer<GraphLayerProps> {
270270
const engine = this.state.graphEngine;
271271
const {edges: edgeStyles} = this._getResolvedStylesheet();
272272

273-
if (!engine || !edgeStyles || edgeStyles.length === 0) {
273+
if (!engine || !edgeStyles) {
274274
return [];
275275
}
276276

277-
return edgeStyles
277+
const edgeStyleArray = Array.isArray(edgeStyles) ? edgeStyles : [edgeStyles];
278+
279+
if (edgeStyleArray.length === 0) {
280+
return [];
281+
}
282+
283+
return edgeStyleArray
278284
.filter(Boolean)
279285
.flatMap((style, idx) => {
280286
const {decorators, data = (edges) => edges, visible = true, ...restEdgeStyle} = style;

modules/graph-layers/src/style/graph-layer-stylesheet.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: MIT
33
// Copyright (c) vis.gl contributors
44

5-
import type {GraphStylesheet, GraphStyleValue, GraphStyleType} from './graph-style-engine';
5+
import type {GraphStylesheet, GraphStyleType} from './graph-style-engine';
66

77
export type GraphNodeStyleType = Exclude<
88
GraphStyleType,
@@ -35,13 +35,13 @@ export type GraphLayerStylesheet = {
3535
edges?: GraphLayerEdgeStyle | GraphLayerEdgeStyle[];
3636
};
3737

38+
export type GraphLayerStylesheetInput = GraphLayerStylesheet | null | undefined;
39+
3840
export type NormalizedGraphLayerStylesheet = {
3941
nodes: GraphLayerNodeStyle[];
4042
edges: GraphLayerEdgeStyle[];
4143
};
4244

43-
export type GraphLayerStylesheetInput = GraphLayerStylesheet | null | undefined;
44-
4545
const DEFAULT_EDGE_STYLE: GraphLayerEdgeStyle = {
4646
type: 'edge',
4747
stroke: 'black',
@@ -76,18 +76,18 @@ export function normalizeGraphLayerStylesheet({
7676
? resolvedNodeStyles.filter(Boolean)
7777
: [...DEFAULT_GRAPH_LAYER_STYLESHEET.nodes];
7878

79-
const edgesArray = Array.isArray(resolvedEdgeStyles)
79+
const edgeEntries = Array.isArray(resolvedEdgeStyles)
8080
? resolvedEdgeStyles
8181
: resolvedEdgeStyles
8282
? [resolvedEdgeStyles]
8383
: DEFAULT_GRAPH_LAYER_STYLESHEET.edges;
8484

85-
const edges = edgesArray
85+
const edges: GraphLayerEdgeStyle[] = (edgeEntries)
8686
.filter(Boolean)
8787
.map((edgeStyleEntry) => ({
8888
...edgeStyleEntry,
89-
type: ((edgeStyleEntry as GraphLayerEdgeStyle).type ?? 'edge') as EdgeStyleType,
90-
decorators: (edgeStyleEntry as GraphLayerEdgeStyle).decorators ?? []
89+
type: ((edgeStyleEntry).type ?? 'edge'),
90+
decorators: (edgeStyleEntry).decorators ?? []
9191
})) as GraphLayerEdgeStyle[];
9292

9393
return {
@@ -96,8 +96,4 @@ export function normalizeGraphLayerStylesheet({
9696
};
9797
}
9898

99-
export type {
100-
GraphStyleValue,
101-
GraphStylesheet,
102-
GraphStyleType
103-
} from './graph-style-engine';
99+
export type {GraphStyleValue, GraphStylesheet, GraphStyleType} from './graph-style-engine';

modules/graph-layers/src/style/graph-style-engine.ts

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,50 @@
22
// SPDX-License-Identifier: MIT
33
// Copyright (c) vis.gl contributors
44

5-
import {StyleEngine, type DeckGLAccessorMap, type DeckGLUpdateTriggers} from './style-engine';
5+
import {ZodError} from 'zod';
6+
7+
/** Supported scale families for attribute references. *
8+
export type GraphStyleScaleType
69
7-
/** Supported scale families for attribute references. */
8-
export type GraphStyleScaleType =
9-
| 'linear'
1010
| 'log'
1111
| 'pow'
1212
| 'sqrt'
1313
| 'quantize'
1414
| 'quantile'
1515
| 'ordinal';
16+
/** Configuration for attribute scale mapping. *
17+
export type GraphStyleScale =
1618
17-
/** Configuration for attribute scale mapping. */
18-
export type GraphStyleScale = {
19-
type?: GraphStyleScaleType;
2019
domain?: (number | string)[];
2120
range?: any[];
2221
clamp?: boolean;
2322
nice?: boolean | number;
2423
base?: number;
2524
exponent?: number;
26-
unknown?: unknown;
2725
};
2826
29-
/** Declares that a style property should derive its value from a graph attribute. */
30-
export type GraphStyleAttributeReference<TValue = unknown> =
31-
| `@${string}`
27+
/** Declares that a style property should derive its value from a graph attribute. *
28+
export type GraphStyleAttributeReference<TValue = unknown>
29+
3230
| {
3331
attribute: string;
3432
fallback?: TValue;
3533
scale?: GraphStyleScale | ((value: unknown) => unknown);
3634
};
3735
38-
/** Acceptable value for a single style state or accessor. */
39-
export type GraphStyleLeafValue<TValue = unknown> =
40-
| TValue
36+
export type GraphStyleLeafValue<TValue = unknown>
37+
4138
| GraphStyleAttributeReference<TValue>
4239
| ((datum: unknown) => TValue);
4340
44-
/** Acceptable value for a style property, including optional interaction states. */
45-
export type GraphStyleValue<TValue = unknown> =
46-
| GraphStyleLeafValue<TValue>
47-
| {[state: string]: GraphStyleLeafValue<TValue>};
41+
/** Acceptable value for a style property, including optional interaction states. *
42+
export type GraphStyleValue<TValue = unknown>
43+
4844
4945
const COMMON_DECKGL_PROPS = {
5046
getOffset: 'offset',
5147
opacity: 'opacity'
5248
} as const;
53-
5449
const GRAPH_DECKGL_ACCESSOR_MAP = {
5550
circle: {
5651
...COMMON_DECKGL_PROPS,
@@ -164,6 +159,15 @@ export type GraphStylesheet<
164159
> = {type: TType} &
165160
GraphStylePropertyMap<TType, TValue> &
166161
Partial<Record<GraphStyleSelector, GraphStylePropertyMap<TType, TValue>>>;
162+
*/
163+
164+
import {StyleEngine, type DeckGLUpdateTriggers} from './style-engine';
165+
import {
166+
GraphStylesheetSchema,
167+
GRAPH_DECKGL_ACCESSOR_MAP,
168+
type GraphStylesheet,
169+
type GraphStylesheetParsed
170+
} from './graph-stylesheet.schema';
167171

168172
const GRAPH_DECKGL_UPDATE_TRIGGERS: DeckGLUpdateTriggers = {
169173
circle: ['getFillColor', 'getRadius', 'getLineColor', 'getLineWidth'],
@@ -179,12 +183,55 @@ const GRAPH_DECKGL_UPDATE_TRIGGERS: DeckGLUpdateTriggers = {
179183
arrow: ['getColor', 'getSize', 'getOffset']
180184
};
181185

186+
function formatStylesheetError(error: ZodError) {
187+
const details = error.issues
188+
.map((issue) => {
189+
const path = issue.path.length ? issue.path.join('.') : 'root';
190+
return ` • ${path}: ${issue.message}`;
191+
})
192+
.join('\n');
193+
return `Invalid graph stylesheet:\n${details}`;
194+
}
195+
182196
export class GraphStyleEngine extends StyleEngine {
183197
constructor(style: GraphStylesheet, {stateUpdateTrigger}: {stateUpdateTrigger?: unknown} = {}) {
184-
super(style, {
198+
let parsedStyle: GraphStylesheetParsed;
199+
try {
200+
parsedStyle = GraphStylesheetSchema.parse(style);
201+
} catch (error) {
202+
if (error instanceof ZodError) {
203+
throw new Error(formatStylesheetError(error));
204+
}
205+
throw error;
206+
}
207+
208+
super(parsedStyle as GraphStylesheet, {
185209
deckglAccessorMap: GRAPH_DECKGL_ACCESSOR_MAP,
186210
deckglUpdateTriggers: GRAPH_DECKGL_UPDATE_TRIGGERS,
187211
stateUpdateTrigger
188212
});
189213
}
190214
}
215+
216+
export {
217+
GraphStyleScaleTypeEnum,
218+
GraphStyleScaleSchema,
219+
GraphStyleAttributeReferenceSchema,
220+
GraphStyleLeafValueSchema,
221+
GraphStyleStateMapSchema,
222+
GraphStyleValueSchema,
223+
GraphStylesheetSchema
224+
} from './graph-stylesheet.schema';
225+
226+
export type {
227+
GraphStyleAttributeReference,
228+
GraphStyleLeafValue,
229+
GraphStyleScale,
230+
GraphStyleScaleType,
231+
GraphStyleSelector,
232+
GraphStyleType,
233+
GraphStyleValue,
234+
GraphStylesheet,
235+
GraphStylesheetInput,
236+
GraphStylesheetParsed
237+
} from './graph-stylesheet.schema';

0 commit comments

Comments
 (0)