@astryxdesign/vega 0.5.2-canary.c9c8564 → 0.5.2-canary.cb13c1e
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/README.md +44 -1
- package/dist/VegaChart.d.ts +13 -6
- package/dist/VegaChart.d.ts.map +1 -1
- package/dist/VegaChart.test.d.ts +2 -0
- package/dist/VegaChart.test.d.ts.map +1 -0
- package/dist/index.js +91 -10
- package/dist/index.mjs +92 -11
- package/dist/schema.test.d.ts +2 -0
- package/dist/schema.test.d.ts.map +1 -0
- package/dist/vegaLiteConfig.test.d.ts +2 -0
- package/dist/vegaLiteConfig.test.d.ts.map +1 -0
- package/dist/viewInputs.d.ts +47 -0
- package/dist/viewInputs.d.ts.map +1 -0
- package/dist/viewInputs.test.d.ts +2 -0
- package/dist/viewInputs.test.d.ts.map +1 -0
- package/package.json +3 -1
- package/src/VegaChart.test.tsx +560 -0
- package/src/VegaChart.tsx +62 -16
- package/src/schema.test.ts +85 -0
- package/src/vegaLiteConfig.test.ts +94 -0
- package/src/viewInputs.test.ts +297 -0
- package/src/viewInputs.ts +187 -0
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Renders [Vega](https://vega.github.io/vega/) and [Vega-Lite](https://vega.github
|
|
|
17
17
|
| `tsup.config.ts` | Config | Build config: CJS + ESM + `.d.ts` outputs |
|
|
18
18
|
| `src/index.ts` | Barrel | Public API surface |
|
|
19
19
|
| `src/VegaChart.tsx` | Component | Inspects `$schema`, compiles or renders, owns View lifecycle |
|
|
20
|
+
| `src/viewInputs.ts` | Utility | Detects value changes in the props that own the View |
|
|
20
21
|
| `src/schema.ts` | Utility | Parses and validates Vega/Vega-Lite `$schema` URLs |
|
|
21
22
|
| `src/types.ts` | Types | Shared TypeScript types for this package |
|
|
22
23
|
|
|
@@ -133,9 +134,51 @@ import {VegaChart} from '@astryxdesign/vega';
|
|
|
133
134
|
| -------------------- | --------- | --------------------------------------------------------- |
|
|
134
135
|
| `ast` | `boolean` | Retain expression AST in the runtime (useful for tooling) |
|
|
135
136
|
|
|
137
|
+
### View lifecycle
|
|
138
|
+
|
|
139
|
+
`<VegaChart>` builds a Vega `View` on mount and finalizes it on unmount. In
|
|
140
|
+
between, it rebuilds the View only when `spec`, `compileOptions`,
|
|
141
|
+
`parseConfig`, `parseOptions`, or `viewOptions` **changes value** — these props
|
|
142
|
+
are compared by value against the values the live View was built from, not by
|
|
143
|
+
reference. Two consequences, and you need neither `useMemo` nor a discipline
|
|
144
|
+
about object identity for either:
|
|
145
|
+
|
|
146
|
+
- an object literal rebuilt inline on every render does **not** tear the chart
|
|
147
|
+
down;
|
|
148
|
+
- a spec you edit **in place** — held in a ref, a module constant, or shared
|
|
149
|
+
between renders — **is** picked up, because the comparison is against a copy
|
|
150
|
+
taken when the View was built, not against the previous props.
|
|
151
|
+
|
|
152
|
+
Functions inside those props (a `tooltip` handler, `logger`, `loader`, `expr`,
|
|
153
|
+
or `fieldTitle`) and class instances are compared by reference, since their
|
|
154
|
+
behavior lives in methods a copy cannot capture: replace the value rather than
|
|
155
|
+
mutating it.
|
|
156
|
+
|
|
157
|
+
Two shapes cannot be copied for comparison: a subtree that re-enters an object
|
|
158
|
+
already on its own path (a reference cycle), and one nested deeper than 100
|
|
159
|
+
levels. Those parts are compared **by reference**, so passing the **same**
|
|
160
|
+
object again is stable — a chart built from a cyclic spec is not rebuilt on
|
|
161
|
+
every render — while passing a **new** object rebuilds the View even when it
|
|
162
|
+
holds equal values.
|
|
163
|
+
|
|
164
|
+
What that costs differs between the two:
|
|
165
|
+
|
|
166
|
+
- **A cycle hides nothing.** Its re-entry edge points back at an object the
|
|
167
|
+
copy already walked, so an in-place edit anywhere in a cyclic spec is still
|
|
168
|
+
detected normally.
|
|
169
|
+
- **Past 100 levels, an in-place edit is invisible** — the reference did not
|
|
170
|
+
change and the values below were never copied. Replace the object, or drive
|
|
171
|
+
the View through `onReady`, to update from down there.
|
|
172
|
+
|
|
173
|
+
Everything the copy did reach is compared by value either way, so a mutation
|
|
174
|
+
elsewhere in the spec is picked up even when part of it is opaque.
|
|
175
|
+
|
|
176
|
+
Everything else is inert to the lifecycle: `data`, `className`, `style`,
|
|
177
|
+
`onReady`, and `onError` never rebuild the View.
|
|
178
|
+
|
|
136
179
|
### Data loading
|
|
137
180
|
|
|
138
|
-
`data` maps dataset names to tuple arrays and is applied via `view.data(name, tuples)` during View initialization, before the first render. It is _not reactive_; changes after mount are ignored.
|
|
181
|
+
`data` maps dataset names to tuple arrays and is applied via `view.data(name, tuples)` during View initialization, before the first render. It is _not reactive_; changes after mount are ignored, and a new `data` object never rebuilds the View by itself. (When something else rebuilds the View, the new View loads whatever `data` holds at that moment.)
|
|
139
182
|
|
|
140
183
|
To update data dynamically after render, use `onReady` to get the live View and drive it yourself:
|
|
141
184
|
|
package/dist/VegaChart.d.ts
CHANGED
|
@@ -23,17 +23,24 @@ import type { VegaChartProps } from './types';
|
|
|
23
23
|
* new vega.View(runtime, { ...viewOptions, container })
|
|
24
24
|
*
|
|
25
25
|
* Initial dataset values can be provided via `data`. They are loaded once
|
|
26
|
-
* during View initialization, before the first render, and are not reactive
|
|
26
|
+
* during View initialization, before the first render, and are not reactive:
|
|
27
|
+
* a change to `data` alone never rebuilds the View, and is not applied to the
|
|
28
|
+
* live one. Use `onReady` to drive data after mount.
|
|
27
29
|
*
|
|
28
30
|
* It owns the full `View` lifecycle: creates the view on mount, re-creates
|
|
29
|
-
* it when `spec`, `parseConfig`, `parseOptions`, or
|
|
30
|
-
* and calls `view.finalize()` on cleanup to release
|
|
31
|
+
* it when `spec`, `compileOptions`, `parseConfig`, `parseOptions`, or
|
|
32
|
+
* `viewOptions` changes, and calls `view.finalize()` on cleanup to release
|
|
33
|
+
* all runtime resources.
|
|
34
|
+
*
|
|
35
|
+
* "Changes" means the value changed, not the reference: those props are
|
|
36
|
+
* compared by value against a copy taken when the live View was built, so an
|
|
37
|
+
* inline spec or options object rebuilt on every render keeps the live View
|
|
38
|
+
* (and its zoom, hover, and signal state), while a spec mutated in place is
|
|
39
|
+
* still picked up. Nothing needs to be memoized for either to hold.
|
|
31
40
|
*
|
|
32
41
|
* Callbacks (`onReady`, `onError`) are non-reactive Effect Events -- they
|
|
33
42
|
* always see the latest props and never re-run the View lifecycle, so you
|
|
34
|
-
* don't need to memoize them.
|
|
35
|
-
* for `parseConfig`, `parseOptions`, `viewOptions`, and `data` to avoid
|
|
36
|
-
* unnecessary re-renders.
|
|
43
|
+
* don't need to memoize them.
|
|
37
44
|
*
|
|
38
45
|
* Note: this component does not accept `xstyle` because `@astryxdesign/vega` does not
|
|
39
46
|
* depend on StyleX. Use `className` or `style` for layout overrides.
|
package/dist/VegaChart.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaChart.d.ts","sourceRoot":"","sources":["../src/VegaChart.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"VegaChart.d.ts","sourceRoot":"","sources":["../src/VegaChart.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAoD,MAAM,OAAO,CAAC;AAMzE,OAAO,KAAK,EAAC,cAAc,EAAyB,MAAM,SAAS,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,IAAI,EACJ,cAAc,EACd,WAAW,EACX,YAAY,EACZ,WAAW,EACX,SAAS,EACT,KAAK,EACL,GAAG,EACH,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,WAAW,EACpB,GAAG,KAAK,EACT,EAAE,cAAc,qBAsIhB;yBAnJe,SAAS"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VegaChart.test.d.ts","sourceRoot":"","sources":["../src/VegaChart.test.tsx"],"names":[],"mappings":""}
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,67 @@ function parseSchema(schema) {
|
|
|
29
29
|
return { ok: true, library, version };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// src/viewInputs.ts
|
|
33
|
+
var MAX_DEPTH = 100;
|
|
34
|
+
var OPAQUE = /* @__PURE__ */ Symbol("astryx.vega.opaque");
|
|
35
|
+
function opaque(value) {
|
|
36
|
+
return { [OPAQUE]: value };
|
|
37
|
+
}
|
|
38
|
+
function isOpaque(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && OPAQUE in value;
|
|
40
|
+
}
|
|
41
|
+
function latchViewInputs(inputs) {
|
|
42
|
+
return { inputs, snapshot: snapshotValue(inputs, 0, /* @__PURE__ */ new Set()) };
|
|
43
|
+
}
|
|
44
|
+
function latchIsCurrent(latch, inputs) {
|
|
45
|
+
return matchesSnapshot(latch.snapshot, inputs);
|
|
46
|
+
}
|
|
47
|
+
function snapshotValue(value, depth, path) {
|
|
48
|
+
if (!isWalkable(value)) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (depth >= MAX_DEPTH || path.has(value)) {
|
|
52
|
+
return opaque(value);
|
|
53
|
+
}
|
|
54
|
+
path.add(value);
|
|
55
|
+
const copy = Array.isArray(value) ? value.map((item) => snapshotValue(item, depth + 1, path)) : Object.fromEntries(
|
|
56
|
+
Object.entries(value).map(([key, entry]) => [
|
|
57
|
+
key,
|
|
58
|
+
snapshotValue(entry, depth + 1, path)
|
|
59
|
+
])
|
|
60
|
+
);
|
|
61
|
+
path.delete(value);
|
|
62
|
+
return copy;
|
|
63
|
+
}
|
|
64
|
+
function matchesSnapshot(snapshot, value) {
|
|
65
|
+
if (isOpaque(snapshot)) {
|
|
66
|
+
return Object.is(snapshot[OPAQUE], value);
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(snapshot)) {
|
|
69
|
+
return Array.isArray(value) && snapshot.length === value.length && snapshot.every((item, index) => matchesSnapshot(item, value[index]));
|
|
70
|
+
}
|
|
71
|
+
if (isPlainObject(snapshot)) {
|
|
72
|
+
if (!isPlainObject(value)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const keys = Object.keys(snapshot);
|
|
76
|
+
return keys.length === Object.keys(value).length && keys.every(
|
|
77
|
+
(key) => Object.prototype.hasOwnProperty.call(value, key) && matchesSnapshot(snapshot[key], value[key])
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return Object.is(snapshot, value);
|
|
81
|
+
}
|
|
82
|
+
function isWalkable(value) {
|
|
83
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
84
|
+
}
|
|
85
|
+
function isPlainObject(value) {
|
|
86
|
+
if (typeof value !== "object" || value === null) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const proto = Object.getPrototypeOf(value);
|
|
90
|
+
return proto === Object.prototype || proto === null;
|
|
91
|
+
}
|
|
92
|
+
|
|
32
93
|
// src/VegaChart.tsx
|
|
33
94
|
var _jsxruntime = require('react/jsx-runtime');
|
|
34
95
|
function VegaChart({
|
|
@@ -46,17 +107,37 @@ function VegaChart({
|
|
|
46
107
|
...props
|
|
47
108
|
}) {
|
|
48
109
|
const containerRef = _react.useRef.call(void 0, null);
|
|
110
|
+
const inputs = {
|
|
111
|
+
spec,
|
|
112
|
+
compileOptions,
|
|
113
|
+
parseConfig,
|
|
114
|
+
parseOptions,
|
|
115
|
+
viewOptions
|
|
116
|
+
};
|
|
117
|
+
const [latch, setLatch] = _react.useState.call(void 0, () => latchViewInputs(inputs));
|
|
118
|
+
if (!latchIsCurrent(latch, inputs)) {
|
|
119
|
+
setLatch(latchViewInputs(inputs));
|
|
120
|
+
}
|
|
49
121
|
const onReady = _react.useEffectEvent.call(void 0, (view) => {
|
|
50
122
|
_optionalChain([onReadyProp, 'optionalCall', _ => _(view)]);
|
|
51
123
|
});
|
|
52
124
|
const onError = _react.useEffectEvent.call(void 0, (error) => {
|
|
53
125
|
_optionalChain([onErrorProp, 'optionalCall', _2 => _2(error)]);
|
|
54
126
|
});
|
|
127
|
+
const loadInitialData = _react.useEffectEvent.call(void 0, (view) => {
|
|
128
|
+
if (!data) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const [name, tuples] of Object.entries(data)) {
|
|
132
|
+
view.data(name, tuples);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
55
135
|
_react.useEffect.call(void 0, () => {
|
|
56
136
|
const container = containerRef.current;
|
|
57
137
|
if (!container) {
|
|
58
138
|
return;
|
|
59
139
|
}
|
|
140
|
+
const { spec: spec2 } = latch.inputs;
|
|
60
141
|
let cancelled = false;
|
|
61
142
|
let view = null;
|
|
62
143
|
const fail = (err) => {
|
|
@@ -65,23 +146,23 @@ function VegaChart({
|
|
|
65
146
|
}
|
|
66
147
|
};
|
|
67
148
|
try {
|
|
68
|
-
const schemaResult = parseSchema(
|
|
149
|
+
const schemaResult = parseSchema(spec2.$schema);
|
|
69
150
|
if (!schemaResult.ok) {
|
|
70
151
|
fail(new Error(schemaResult.error));
|
|
71
152
|
return;
|
|
72
153
|
}
|
|
73
|
-
const vegaSpec = schemaResult.library === "vega-lite" ? _vegalite.compile.call(void 0,
|
|
74
|
-
const runtime = _vega.parse.call(void 0,
|
|
154
|
+
const vegaSpec = schemaResult.library === "vega-lite" ? _vegalite.compile.call(void 0, spec2, latch.inputs.compileOptions).spec : spec2;
|
|
155
|
+
const runtime = _vega.parse.call(void 0,
|
|
156
|
+
vegaSpec,
|
|
157
|
+
latch.inputs.parseConfig,
|
|
158
|
+
latch.inputs.parseOptions
|
|
159
|
+
);
|
|
75
160
|
view = new (0, _vega.View)(runtime, {
|
|
76
161
|
hover: true,
|
|
77
|
-
...viewOptions,
|
|
162
|
+
...latch.inputs.viewOptions,
|
|
78
163
|
container
|
|
79
164
|
});
|
|
80
|
-
|
|
81
|
-
for (const [name, tuples] of Object.entries(data)) {
|
|
82
|
-
view.data(name, tuples);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
165
|
+
loadInitialData(view);
|
|
85
166
|
view.runAsync().then(() => {
|
|
86
167
|
if (cancelled) {
|
|
87
168
|
_optionalChain([view, 'optionalAccess', _3 => _3.finalize, 'call', _4 => _4()]);
|
|
@@ -98,7 +179,7 @@ function VegaChart({
|
|
|
98
179
|
cancelled = true;
|
|
99
180
|
_optionalChain([view, 'optionalAccess', _5 => _5.finalize, 'call', _6 => _6()]);
|
|
100
181
|
};
|
|
101
|
-
}, [
|
|
182
|
+
}, [latch]);
|
|
102
183
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
103
184
|
"div",
|
|
104
185
|
{
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/VegaChart.tsx
|
|
2
|
-
import { useEffect, useEffectEvent, useRef } from "react";
|
|
2
|
+
import { useEffect, useEffectEvent, useRef, useState } from "react";
|
|
3
3
|
import { parse, View } from "vega";
|
|
4
4
|
import { compile } from "vega-lite";
|
|
5
5
|
|
|
@@ -29,6 +29,67 @@ function parseSchema(schema) {
|
|
|
29
29
|
return { ok: true, library, version };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// src/viewInputs.ts
|
|
33
|
+
var MAX_DEPTH = 100;
|
|
34
|
+
var OPAQUE = /* @__PURE__ */ Symbol("astryx.vega.opaque");
|
|
35
|
+
function opaque(value) {
|
|
36
|
+
return { [OPAQUE]: value };
|
|
37
|
+
}
|
|
38
|
+
function isOpaque(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && OPAQUE in value;
|
|
40
|
+
}
|
|
41
|
+
function latchViewInputs(inputs) {
|
|
42
|
+
return { inputs, snapshot: snapshotValue(inputs, 0, /* @__PURE__ */ new Set()) };
|
|
43
|
+
}
|
|
44
|
+
function latchIsCurrent(latch, inputs) {
|
|
45
|
+
return matchesSnapshot(latch.snapshot, inputs);
|
|
46
|
+
}
|
|
47
|
+
function snapshotValue(value, depth, path) {
|
|
48
|
+
if (!isWalkable(value)) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (depth >= MAX_DEPTH || path.has(value)) {
|
|
52
|
+
return opaque(value);
|
|
53
|
+
}
|
|
54
|
+
path.add(value);
|
|
55
|
+
const copy = Array.isArray(value) ? value.map((item) => snapshotValue(item, depth + 1, path)) : Object.fromEntries(
|
|
56
|
+
Object.entries(value).map(([key, entry]) => [
|
|
57
|
+
key,
|
|
58
|
+
snapshotValue(entry, depth + 1, path)
|
|
59
|
+
])
|
|
60
|
+
);
|
|
61
|
+
path.delete(value);
|
|
62
|
+
return copy;
|
|
63
|
+
}
|
|
64
|
+
function matchesSnapshot(snapshot, value) {
|
|
65
|
+
if (isOpaque(snapshot)) {
|
|
66
|
+
return Object.is(snapshot[OPAQUE], value);
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(snapshot)) {
|
|
69
|
+
return Array.isArray(value) && snapshot.length === value.length && snapshot.every((item, index) => matchesSnapshot(item, value[index]));
|
|
70
|
+
}
|
|
71
|
+
if (isPlainObject(snapshot)) {
|
|
72
|
+
if (!isPlainObject(value)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const keys = Object.keys(snapshot);
|
|
76
|
+
return keys.length === Object.keys(value).length && keys.every(
|
|
77
|
+
(key) => Object.prototype.hasOwnProperty.call(value, key) && matchesSnapshot(snapshot[key], value[key])
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return Object.is(snapshot, value);
|
|
81
|
+
}
|
|
82
|
+
function isWalkable(value) {
|
|
83
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
84
|
+
}
|
|
85
|
+
function isPlainObject(value) {
|
|
86
|
+
if (typeof value !== "object" || value === null) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const proto = Object.getPrototypeOf(value);
|
|
90
|
+
return proto === Object.prototype || proto === null;
|
|
91
|
+
}
|
|
92
|
+
|
|
32
93
|
// src/VegaChart.tsx
|
|
33
94
|
import { jsx } from "react/jsx-runtime";
|
|
34
95
|
function VegaChart({
|
|
@@ -46,17 +107,37 @@ function VegaChart({
|
|
|
46
107
|
...props
|
|
47
108
|
}) {
|
|
48
109
|
const containerRef = useRef(null);
|
|
110
|
+
const inputs = {
|
|
111
|
+
spec,
|
|
112
|
+
compileOptions,
|
|
113
|
+
parseConfig,
|
|
114
|
+
parseOptions,
|
|
115
|
+
viewOptions
|
|
116
|
+
};
|
|
117
|
+
const [latch, setLatch] = useState(() => latchViewInputs(inputs));
|
|
118
|
+
if (!latchIsCurrent(latch, inputs)) {
|
|
119
|
+
setLatch(latchViewInputs(inputs));
|
|
120
|
+
}
|
|
49
121
|
const onReady = useEffectEvent((view) => {
|
|
50
122
|
onReadyProp?.(view);
|
|
51
123
|
});
|
|
52
124
|
const onError = useEffectEvent((error) => {
|
|
53
125
|
onErrorProp?.(error);
|
|
54
126
|
});
|
|
127
|
+
const loadInitialData = useEffectEvent((view) => {
|
|
128
|
+
if (!data) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const [name, tuples] of Object.entries(data)) {
|
|
132
|
+
view.data(name, tuples);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
55
135
|
useEffect(() => {
|
|
56
136
|
const container = containerRef.current;
|
|
57
137
|
if (!container) {
|
|
58
138
|
return;
|
|
59
139
|
}
|
|
140
|
+
const { spec: spec2 } = latch.inputs;
|
|
60
141
|
let cancelled = false;
|
|
61
142
|
let view = null;
|
|
62
143
|
const fail = (err) => {
|
|
@@ -65,23 +146,23 @@ function VegaChart({
|
|
|
65
146
|
}
|
|
66
147
|
};
|
|
67
148
|
try {
|
|
68
|
-
const schemaResult = parseSchema(
|
|
149
|
+
const schemaResult = parseSchema(spec2.$schema);
|
|
69
150
|
if (!schemaResult.ok) {
|
|
70
151
|
fail(new Error(schemaResult.error));
|
|
71
152
|
return;
|
|
72
153
|
}
|
|
73
|
-
const vegaSpec = schemaResult.library === "vega-lite" ? compile(
|
|
74
|
-
const runtime = parse(
|
|
154
|
+
const vegaSpec = schemaResult.library === "vega-lite" ? compile(spec2, latch.inputs.compileOptions).spec : spec2;
|
|
155
|
+
const runtime = parse(
|
|
156
|
+
vegaSpec,
|
|
157
|
+
latch.inputs.parseConfig,
|
|
158
|
+
latch.inputs.parseOptions
|
|
159
|
+
);
|
|
75
160
|
view = new View(runtime, {
|
|
76
161
|
hover: true,
|
|
77
|
-
...viewOptions,
|
|
162
|
+
...latch.inputs.viewOptions,
|
|
78
163
|
container
|
|
79
164
|
});
|
|
80
|
-
|
|
81
|
-
for (const [name, tuples] of Object.entries(data)) {
|
|
82
|
-
view.data(name, tuples);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
165
|
+
loadInitialData(view);
|
|
85
166
|
view.runAsync().then(() => {
|
|
86
167
|
if (cancelled) {
|
|
87
168
|
view?.finalize();
|
|
@@ -98,7 +179,7 @@ function VegaChart({
|
|
|
98
179
|
cancelled = true;
|
|
99
180
|
view?.finalize();
|
|
100
181
|
};
|
|
101
|
-
}, [
|
|
182
|
+
}, [latch]);
|
|
102
183
|
return /* @__PURE__ */ jsx(
|
|
103
184
|
"div",
|
|
104
185
|
{
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../src/schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vegaLiteConfig.test.d.ts","sourceRoot":"","sources":["../src/vegaLiteConfig.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file viewInputs.ts
|
|
3
|
+
* @input The VegaChart props that own the Vega View lifecycle
|
|
4
|
+
* @output The ViewInputs type plus latch/compare helpers VegaChart rebuilds on
|
|
5
|
+
* @position Internal utility; used by VegaChart to decide when to rebuild
|
|
6
|
+
*
|
|
7
|
+
* SYNC: When modified, update /packages/vega/README.md
|
|
8
|
+
*/
|
|
9
|
+
import type { VegaChartProps } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* The props the Vega runtime is built from. A change to any of them produces
|
|
12
|
+
* a different runtime, so the View is torn down and rebuilt. `data`,
|
|
13
|
+
* `className`, `style`, and the callbacks are absent because none of them
|
|
14
|
+
* change the runtime.
|
|
15
|
+
*/
|
|
16
|
+
export type ViewInputs = Pick<VegaChartProps, 'spec' | 'compileOptions' | 'parseConfig' | 'parseOptions' | 'viewOptions'>;
|
|
17
|
+
/**
|
|
18
|
+
* The inputs the live View was built from, beside a structural copy of their
|
|
19
|
+
* values at that moment.
|
|
20
|
+
*
|
|
21
|
+
* The copy is the whole point. Comparing the incoming props against the
|
|
22
|
+
* *previous props* cannot see a spec mutated in place — both sides are the
|
|
23
|
+
* same object, so the old value is already gone and the chart silently goes
|
|
24
|
+
* stale. Comparing against a copy taken when the View was built can.
|
|
25
|
+
*
|
|
26
|
+
* The copy is also compared during render, so its verdict must be stable for
|
|
27
|
+
* an unchanged input: a part that always reported "changed" would loop React
|
|
28
|
+
* forever. See {@link Opaque} for the parts that cannot be copied.
|
|
29
|
+
*/
|
|
30
|
+
export type ViewInputsLatch = {
|
|
31
|
+
/** The caller's own objects, handed to Vega untouched. */
|
|
32
|
+
readonly inputs: ViewInputs;
|
|
33
|
+
readonly snapshot: unknown;
|
|
34
|
+
};
|
|
35
|
+
export declare function latchViewInputs(inputs: ViewInputs): ViewInputsLatch;
|
|
36
|
+
/**
|
|
37
|
+
* Whether `inputs` still carries the values the latched View was built from.
|
|
38
|
+
*
|
|
39
|
+
* Plain objects and arrays are compared entry by entry, so a spec or options
|
|
40
|
+
* bag rebuilt inline on every render counts as unchanged. Everything else --
|
|
41
|
+
* functions (tooltip handlers, loggers, loaders, field-title formatters),
|
|
42
|
+
* class instances, dates -- compares by reference, because two instances can
|
|
43
|
+
* behave differently even when they look alike, and the View built from them
|
|
44
|
+
* would too.
|
|
45
|
+
*/
|
|
46
|
+
export declare function latchIsCurrent(latch: ViewInputsLatch, inputs: ViewInputs): boolean;
|
|
47
|
+
//# sourceMappingURL=viewInputs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewInputs.d.ts","sourceRoot":"","sources":["../src/viewInputs.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,CAC3B,cAAc,EACd,MAAM,GAAG,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,CAC3E,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B,CAAC;AAuCF,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAEnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,UAAU,GACjB,OAAO,CAET"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewInputs.test.d.ts","sourceRoot":"","sources":["../src/viewInputs.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/vega",
|
|
3
|
-
"version": "0.5.2-canary.
|
|
3
|
+
"version": "0.5.2-canary.cb13c1e",
|
|
4
4
|
"description": "Astryx Vega wrapper — chart and data visualization components. Published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
|
|
5
5
|
"author": "Meta Open Source",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup && tsc --project tsconfig.json --emitDeclarationOnly --declaration --noEmit false --outDir dist",
|
|
59
59
|
"dev": "tsup --watch",
|
|
60
|
+
"test": "vitest run --root ../..",
|
|
61
|
+
"test:watch": "vitest --root ../..",
|
|
60
62
|
"typecheck": "tsc --noEmit"
|
|
61
63
|
}
|
|
62
64
|
}
|