| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix: support escaped fields in point selections (#9904) ## PR Description ### Summary Use `flatAccessWithDatum` when extracting point-selection values. - Escaped fields (ex. `"field": "y\\[foo\\]"`) now access `datum["y[foo]"]`. - This change applies to point-selection and bound input selections. - Existing point-selection behavior remains unchanged for fields without escaped chars. - Added regression testing for point tuples and nearest bound selections. ### Motivation Point selections manually generated datum access with `stringValue`, which quoted escaped field paths without interpreting them: ```js datum["y\\[foo\\]"] ``` Other Vega-Lite compiler paths use shared field-access helpers that correctly generate: ```js datum["y[foo]"] ``` This caused point-selection behavior to differ from rendering, transforms, predicates, and other generated expressions. As a result, point selections did not work as expected for marks using escaped field names. ### History! In 2019, [#5351](https://github.com/vega/vega-lite/pull/5351) changed selection field access to manual bracket lookup to support flattened fields such as `nested.a`. This fixed nested selections but bypassed the shared helper that interprets escaped field names. Escaped bracket support was later added in [#5730](https://github.com/vega/vega-lite/pull/5730), but point selections did not inherit the fix when selection types were consolidated in [#6927](https://github.com/vega/vega-lite/pull/6927). <img alt="2026-07-29-escape-brackets-selection" src="https://github.com/user-attachments/assets/ad6f472c-b3f0-4aa0-8228-c607808c713f" /> | 8 天前 | |
feat: add exponential moving average (#9880) Closes #9225. Thank you @julieg18. Adds exponential moving average aggregate ops (`exponential` and `exponentialb`, backed by Vega's `aggregate_params`) as the first parameterized aggregates in Vega-Lite. ## New syntax At the encoding level: ```json {"y": {"aggregate": {"exponential": 0.5}, "field": "price", "type": "quantitative"}} ``` And in aggregate transforms: ```json {"aggregate": [{"op": {"exponentialb": 0.5}, "field": "price", "as": "ema"}], "groupby": ["symbol"]} ``` Both ops require their rate parameter: bare "aggregate": "exponential" / "exponentialb" are rejected by the TypeScript types and the JSON schema, since they would silently produce NaN. Changes beyond the original PR - exponentialb is now parameterized via ExponentialBDef (in #9225 it was exposed as a bare string with no way to pass the required parameter) - A parameter value of 0 is preserved through merging and assembly (was dropped by falsy checks) - Merging aggregates with conflicting parameters for the same field/op now emits a warning instead of silently picking one - Op/param unwrapping is centralized in getAggregateOp/getAggregateParam (src/aggregate.ts), so a future parameterized op touches one file - Docs (aggregate.md, link.yml), regenerated schema, and the layer_line_exponential example updated to Vega 6 --------- Co-authored-by: julieg18 <juliannagal18@gmail.com> Co-authored-by: Julie G <43496356+julieg18@users.noreply.github.com> Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com> | 1 个月前 | |
fix: update version in example specs (#9573) Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com> | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat: add exponential moving average (#9880) Closes #9225. Thank you @julieg18. Adds exponential moving average aggregate ops (`exponential` and `exponentialb`, backed by Vega's `aggregate_params`) as the first parameterized aggregates in Vega-Lite. ## New syntax At the encoding level: ```json {"y": {"aggregate": {"exponential": 0.5}, "field": "price", "type": "quantitative"}} ``` And in aggregate transforms: ```json {"aggregate": [{"op": {"exponentialb": 0.5}, "field": "price", "as": "ema"}], "groupby": ["symbol"]} ``` Both ops require their rate parameter: bare "aggregate": "exponential" / "exponentialb" are rejected by the TypeScript types and the JSON schema, since they would silently produce NaN. Changes beyond the original PR - exponentialb is now parameterized via ExponentialBDef (in #9225 it was exposed as a bare string with no way to pass the required parameter) - A parameter value of 0 is preserved through merging and assembly (was dropped by falsy checks) - Merging aggregates with conflicting parameters for the same field/op now emits a warning instead of silently picking one - Op/param unwrapping is centralized in getAggregateOp/getAggregateParam (src/aggregate.ts), so a future parameterized op touches one file - Docs (aggregate.md, link.yml), regenerated schema, and the layer_line_exponential example updated to Vega 6 --------- Co-authored-by: julieg18 <juliannagal18@gmail.com> Co-authored-by: Julie G <43496356+julieg18@users.noreply.github.com> Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com> | 1 个月前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
fix: Make ranged marks compatible with offset channels (#9823) In short, this PR changes ranged marks in offset channels so that they compile as true ranged marks with consistent baseline, grouping, and axis alignment. There are still some things to fix with the examples but it would be helpful with high level comments on the overall approach even before then. (Note that all the screenshot are including https://github.com/vega/vega-lite/pull/9817, otherwise they would be upside down) ### Details Currently, ranged marks are not working well in offset channels. For example, a bar mark simply places a rectangle of fixed size at the location of the offset. I think that the desired behavior should mimic how bars work across other quantitative channel, ie start from a baseline and extent to the data value, which is what I have tried to implement here: | Before this PR | After this PR | |-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/931fc2fe-eaac-4299-a9d4-7634c3277960" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/4e164c4b-95cb-4569-8306-611a59670bab" /> | [Open the Chart in the Vega Editor](https://vega.github.io/editor/#/url/vega-lite/N4IgJghgLhIFygG4QDYFcCmBneBtUscIAgiADQgBG8ATABwUDG8IAHueCwLYgC+ZBFgCEO1OAFZxTFuwphufASEIgAwqPgAWAMzSiszkQB2iwUQAiGuAE4AjHrYd5x08pakKYugxDMiATycFfjMQEU94SQdAuWClFXUIuB1ooJcQtwsrKN8WGMMQE14AXX4QLggAJwBreFAofwAHDBZKKsUQDCNGAHswAEsjAHM6xwQQADN+jBRnNzL-AHkJiawMKFGpmbnqCgbmlgBHNAgjKH6Yc8QWhc3p2ZZmXmegA) Similarly, area marks display unusual behavior currently and don't show up at all. With the changes in this PR, they will show similar to how the line mark from #9819 display: | Before this PR | After this PR | Line in #9819 | |-|-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/00872695-fb90-4f8f-8a7b-1d08f31c52ea" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/46df4888-f487-47b4-aa3c-6e3e1f425026" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/22e834b9-0b3c-4f5e-957f-dd496767ca94" /> | [Open the Chart in the Vega Editor](https://vega.github.io/editor/#/url/vega-lite/N4IgJghgLhIFygG4QDYFcCmBneBtUscIAgiADQgBG8ATABwUDG8IAHueCwLYgC+ZBFgCEO1OAFZxTFuwphufASEIgAwqPgAWAMzSiszkQB2iwUQAiGuAE4AjHrYd5x08pakKYugxDMiATycFfjMQEU94SQdAuWClFXUIuB1ooJcQtwsrKN8WGMMQE14AXX4QLggAJwBreFAofwAHDBYqjFgyjCNGAHswAEsjAHM6xwQQADN+jBRnNzL-AHkJiawMKFGpmbnqCgbmlgBHNAgjKH6Yc8QWhc3p2ZZmXmegA) I also chose to align axis/tick/label behavior with the new baseline semantics, anchoring them at the bottom of the bar/area, because I thought it looked odd when they were in the middle of the band: <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/09c013e5-f372-483e-b015-1a69fc18efc2" /> I think that a case could be made for including a grid line by default as a baseline, but I was unsure if this was too automagical, so I left it out in this implementation. It is quite helpful, particularly when the marks have different range, but maybe it should be added manually for consistency: |Without gridline| With gridline | |-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/533c90c6-e9e4-4929-8f4a-b58b6834f15f" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/4449c14f-5d54-4d2d-8978-98d77f4baef2" /> | <details> <summary><h2>Checklist</h2></summary> - [x] This PR is atomic (i.e., it fixes one issue at a time). - [x] The title is a concise [semantic commit message](https://www.conventionalcommits.org/) (e.g. "fix: correctly handle undefined properties"). - [x] `npm test` runs successfully - For new features: - [x] Has unit tests. - [x] Has documentation under `site/docs/` + examples. Tips: - https://medium.com/@greenberg/writing-pull-requests-your-coworkers-might-enjoy-reading-9d0307e93da3 is a nice article about writing a nice PR. - Use draft PR for work in progress PRs / when you want early feedback (https://github.blog/2019-02-14-introducing-draft-pull-requests/). </details> --------- Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com> Co-authored-by: Dominik Moritz <domoritz@gmail.com> | 1 个月前 | |
fix(scale): evaluate signals in unioned domains, upgrade to TypeScript 6 (#9922) Two things, both of which fell out of upgrading `typescript` from `~5.9.3` to `~6.0.3`. ## 1. Bug fix: signals in unioned scale domains were silently dropped Making a type honest (see `VgScaleMultiDataRefWithSort` below) surfaced a real, user-visible bug. ```json {"params": [{"name": "lo", "value": 2}, {"name": "hi", "value": 8}], "layer": [ {"encoding": {"y": {"field": "v", "type": "quantitative", "scale": {"domain": [{"expr": "lo"}, {"expr": "hi"}]}}}, ...}, {"encoding": {"y": {"field": "v", "type": "quantitative", "scale": {"domain": [0, 5]}}}, ...}], "resolve": {"scale": {"y": "shared"}}} ``` compiled to `"domain": {"fields": [[{"signal":"lo"},{"signal":"hi"}], [0,5]]}`. Running that through Vega, the `y` domain resolved to **`[0,5]` instead of `[0,8]`** — the explicit signal domain contributed nothing and was dropped without any warning. The spec also fails validation against Vega's own `vega-schema.json`. **Root cause is an asymmetry in Vega's parser** (`packages/vega-parser/src/parsers/scale.js`). A top-level `domain` array goes through `explicitDomain` → `parseLiteral` per element, which resolves `{signal}` (and errors on any other object). But an array nested in `domain.fields` goes through `multipleDomain` → `fieldRef`, whose array branch does a raw `coll.value = {$ingest: data}` with no `parseLiteral` — so signal objects are ingested as literal data and never evaluated, silently. The neighbouring `d.signal` branch does the right thing via `setdata(...)`. So the same `[{signal: 'lo'}, 5]` syntax means two different things depending on where it appears. **I'm filing a separate PR against vega** to make `fieldRef` resolve signals the way every other literal-array path does. Vega-Lite still needs to emit something that works today: it peer-depends on `vega ^6.0.0`, and the `{signal: "[...]"}` form is valid under Vega's *current* published schema and works on every 6.x. So this is the portable encoding, not a workaround — it stays correct after the Vega fix lands. ```ts /** Vega ingests an array in `domain.fields` as literal data without evaluating signals inside it. */ function unionDomainField(domain: VgNonUnionDomain): VgNonUnionDomain { if (isArray(domain) && domain.some(isSignalRef)) { return {signal: `[${domain.map((v) => (isSignalRef(v) ? v.signal : util.stringify(v))).join(', ')}]`}; } return domain; } ``` Now emits `{"fields": [{"signal":"[lo, hi]"}, [0,5]]}`, which resolves to the correct `[0,8]` and validates. Only arrays that actually contain signals are rewritten, so **all 633 compiled examples are byte-identical**. Two tests added. The single-domain path was always correct and is untouched. ## 2. TypeScript 6.0 **TS 6.0 enables `strict` by default.** Our `tsconfig.json` never set `strict`, so it was off; it explicitly sets `strictNullChecks: false` (still respected), but everything else in the strict family is now on. Two flags did all the damage: | flag | errors | |---|---| | `strictFunctionTypes` — function parameters checked contravariantly instead of bivariantly | 22 | | `strictBindCallApply` — `.call`/`.apply`/`.bind` typed precisely instead of `(thisArg: any, ...args: any[]) => any` | 5 | Confirmed by isolating the flags: `npx tsc --strict false` on the un-fixed tree gives 0 errors. Every one of the 27 was **pre-existing unsoundness that bivariance was hiding**, not a new TS restriction, so the fixes make signatures honest rather than suppressing them. ### Notable fixes - **`ModelWithField` now carries its mapping type** — `forEachFieldDef`/`reduceFieldDef` yield `keyof M`, so a `UnitModel` gives `Channel` and a `FacetModel` gives `FacetChannel`. This replaces an `as Channel` assertion with a guarantee that follows from the arguments. Two signatures were wrong underneath it: `reduce` declared its callback as returning the *mapping* type rather than the accumulator, and `TimeUnitNode` annotated its accumulator as `TimeUnitComponent` when it is a `Dict` of them — which is exactly why it carried a `(timeUnitComponent as any)[…]`. That cast is gone too. - **`reduceFieldDef`** previously declared its callback's channel as `Channel`, but it lives on `ModelWithField` and `FacetModel extends ModelWithField`, so `'row' | 'column' | 'facet'` really did flow through. Now truthful via the generic above. - **`Split` tie-breakers** (`split.ts`, `scale/{domain,parse,properties}.ts`) — `mergeValuesWithExplicit`'s `tieBreaker` parameter declared `propertyOf: string`, but every actual tie-breaker takes the narrower `SplitParentProperty`. Also `domainsTieBreaker` declared `property: 'domains'` while two of its three call sites pass `'domain'` — the type was the lie, not the call, so it's widened to `'domain' | 'domains'`. **No warning message text changes.** - **`encoding.ts` `reduce`** declared it passes `TypedFieldDef<string>` to its callback; the implementation passes whatever is in the mapping. Corrected to `ChannelDef`. `strictBindCallApply` exposed this via the internal `f.call(...)`. - **`VgScaleMultiDataRefWithSort`** (`vega.schema.ts`) — intended to override vega's `fields` with a wider type, but `ScaleMultiDataRef & {fields: ...}` *intersects* rather than overrides, producing a `fields` type nearly nothing could satisfy, so the declared `any[]` never took effect. Changed to `Omit<ScaleMultiDataRef, 'fields'> & {...}`, matching the `Omit` idiom already used further down the same file. This is what surfaced the domain bug above. - **`selectionCompilers`** (`selection/index.ts`) — `SelectionCompiler<T>` uses `T` only in parameter position, so `SelectionCompiler<'point'>` is no longer assignable to `SelectionCompiler<SelectionType>`. All four dispatch loops guard every call behind `c.defined(selCmpt)`, which is exactly the type discriminator. Widened only the array's element type to `SelectionCompiler<any>`, so each compiler module keeps its narrow `<'point'>` / `<'interval'>` annotation and stays fully checked at its definition site. This is the one place the PR loses type safety. - **`Object.assign.apply(null, toMerge)`** (`axis/config.ts`) → `Object.assign({}, ...toMerge)` with the seed `{}` dropped from the array. Equivalent for both the populated and empty cases. - **`tsconfig.build.json`** — added `rootDir: "src"`. TS 6 now emits TS5011 during the rollup build asking for it explicitly. Verified the emitted bundles are byte-identical and the 375 `.d.ts` files land in the same places. ## Verification - `npx tsc` — clean. - `npx vitest run test/` — 3346 passed. - `npx vitest run examples/` — 3277 passed. - `npm run test:runtime -- --browser.headless` — 55 passed, 1 skipped (pre-existing `it.skip`). - `npm run lint` — clean. - `npm run build` — succeeds; TS5011 gone. - `npm run schema` — regenerated `build/vega-lite-schema.json` is **byte-identical** to the committed one. - **All 633 example specs compile to byte-identical Vega output** vs the committed `examples/compiled/*.vg.json` baseline, before and after the domain fix. ## Toolchain compatibility `typescript-eslint@8.64` declares `typescript: ">=4.8.4 <6.1.0"`, so 6.0.3 is in range. `ts-json-schema-generator` pins its own `typescript@5.9.3` as a direct dependency, so schema generation is unaffected either way. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> | 23 天前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
fix: Make ranged marks compatible with offset channels (#9823) In short, this PR changes ranged marks in offset channels so that they compile as true ranged marks with consistent baseline, grouping, and axis alignment. There are still some things to fix with the examples but it would be helpful with high level comments on the overall approach even before then. (Note that all the screenshot are including https://github.com/vega/vega-lite/pull/9817, otherwise they would be upside down) ### Details Currently, ranged marks are not working well in offset channels. For example, a bar mark simply places a rectangle of fixed size at the location of the offset. I think that the desired behavior should mimic how bars work across other quantitative channel, ie start from a baseline and extent to the data value, which is what I have tried to implement here: | Before this PR | After this PR | |-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/931fc2fe-eaac-4299-a9d4-7634c3277960" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/4e164c4b-95cb-4569-8306-611a59670bab" /> | [Open the Chart in the Vega Editor](https://vega.github.io/editor/#/url/vega-lite/N4IgJghgLhIFygG4QDYFcCmBneBtUscIAgiADQgBG8ATABwUDG8IAHueCwLYgC+ZBFgCEO1OAFZxTFuwphufASEIgAwqPgAWAMzSiszkQB2iwUQAiGuAE4AjHrYd5x08pakKYugxDMiATycFfjMQEU94SQdAuWClFXUIuB1ooJcQtwsrKN8WGMMQE14AXX4QLggAJwBreFAofwAHDBZKKsUQDCNGAHswAEsjAHM6xwQQADN+jBRnNzL-AHkJiawMKFGpmbnqCgbmlgBHNAgjKH6Yc8QWhc3p2ZZmXmegA) Similarly, area marks display unusual behavior currently and don't show up at all. With the changes in this PR, they will show similar to how the line mark from #9819 display: | Before this PR | After this PR | Line in #9819 | |-|-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/00872695-fb90-4f8f-8a7b-1d08f31c52ea" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/46df4888-f487-47b4-aa3c-6e3e1f425026" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/22e834b9-0b3c-4f5e-957f-dd496767ca94" /> | [Open the Chart in the Vega Editor](https://vega.github.io/editor/#/url/vega-lite/N4IgJghgLhIFygG4QDYFcCmBneBtUscIAgiADQgBG8ATABwUDG8IAHueCwLYgC+ZBFgCEO1OAFZxTFuwphufASEIgAwqPgAWAMzSiszkQB2iwUQAiGuAE4AjHrYd5x08pakKYugxDMiATycFfjMQEU94SQdAuWClFXUIuB1ooJcQtwsrKN8WGMMQE14AXX4QLggAJwBreFAofwAHDBYqjFgyjCNGAHswAEsjAHM6xwQQADN+jBRnNzL-AHkJiawMKFGpmbnqCgbmlgBHNAgjKH6Yc8QWhc3p2ZZmXmegA) I also chose to align axis/tick/label behavior with the new baseline semantics, anchoring them at the bottom of the bar/area, because I thought it looked odd when they were in the middle of the band: <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/09c013e5-f372-483e-b015-1a69fc18efc2" /> I think that a case could be made for including a grid line by default as a baseline, but I was unsure if this was too automagical, so I left it out in this implementation. It is quite helpful, particularly when the marks have different range, but maybe it should be added manually for consistency: |Without gridline| With gridline | |-|-| | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/533c90c6-e9e4-4929-8f4a-b58b6834f15f" /> | <img width="132" height="99" alt="image" src="https://github.com/user-attachments/assets/4449c14f-5d54-4d2d-8978-98d77f4baef2" /> | <details> <summary><h2>Checklist</h2></summary> - [x] This PR is atomic (i.e., it fixes one issue at a time). - [x] The title is a concise [semantic commit message](https://www.conventionalcommits.org/) (e.g. "fix: correctly handle undefined properties"). - [x] `npm test` runs successfully - For new features: - [x] Has unit tests. - [x] Has documentation under `site/docs/` + examples. Tips: - https://medium.com/@greenberg/writing-pull-requests-your-coworkers-might-enjoy-reading-9d0307e93da3 is a nice article about writing a nice PR. - Use draft PR for work in progress PRs / when you want early feedback (https://github.blog/2019-02-14-introducing-draft-pull-requests/). </details> --------- Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com> Co-authored-by: Dominik Moritz <domoritz@gmail.com> | 1 个月前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 | |
feat!: esm only package (#9527) Part of https://github.com/vega/vega/issues/3990 | 1 年前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 8 天前 | ||
| 1 个月前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 个月前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 个月前 | ||
| 23 天前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 个月前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 1 年前 |