vendor/assets/javascripts/vega-lite.js in vega-0.3.2 vs vendor/assets/javascripts/vega-lite.js in vega-0.4.0

- old
+ new

@@ -4,11 +4,11 @@ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.vegaLite = {}, global.vega)); })(this, (function (exports, vega) { 'use strict'; var name = "vega-lite"; var author = "Dominik Moritz, Kanit \"Ham\" Wongsuphasawat, Arvind Satyanarayan, Jeffrey Heer"; - var version$1 = "5.19.0"; + var version$1 = "5.21.0"; var collaborators = [ "Kanit Wongsuphasawat (http://kanitw.yellowpigz.com)", "Dominik Moritz (https://www.domoritz.de)", "Arvind Satyanarayan (https://arvindsatya.com)", "Jeffrey Heer (https://jheer.org)" @@ -82,62 +82,62 @@ var license = "BSD-3-Clause"; var bugs = { url: "https://github.com/vega/vega-lite/issues" }; var devDependencies = { - "@babel/core": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/core": "^7.24.9", + "@babel/preset-env": "^7.25.0", "@babel/preset-typescript": "^7.24.7", "@release-it/conventional-changelog": "^8.0.1", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-terser": "^0.4.4", "@types/d3": "^7.4.3", "@types/jest": "^29.5.12", "@types/pako": "^2.0.3", - "@typescript-eslint/eslint-plugin": "^7.13.0", - "@typescript-eslint/parser": "^7.13.0", - ajv: "^8.16.0", - "ajv-formats": "^2.1.1", + "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/parser": "^7.17.0", + ajv: "^8.17.1", + "ajv-formats": "^3.0.1", cheerio: "^1.0.0-rc.12", - "conventional-changelog-cli": "^4.1.0", + "conventional-changelog-cli": "^5.0.0", d3: "^7.9.0", "del-cli": "^5.1.0", eslint: "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^27.9.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-prettier": "^5.2.1", "fast-json-stable-stringify": "~2.1.0", - "highlight.js": "^11.9.0", + "highlight.js": "^11.10.0", jest: "^29.7.0", "jest-dev-server": "^10.0.0", mkdirp: "^3.0.1", pako: "^2.1.0", - prettier: "^3.3.2", + prettier: "^3.3.3", puppeteer: "^15.0.0", - "release-it": "17.2.1", - rollup: "^4.18.0", + "release-it": "17.6.0", + rollup: "^4.19.1", "rollup-plugin-bundle-size": "^1.0.3", serve: "^14.2.3", - terser: "^5.31.1", - "ts-jest": "^29.1.4", - "ts-json-schema-generator": "^1.5.0", - typescript: "~5.4.5", + terser: "^5.31.3", + "ts-jest": "^29.2.3", + "ts-json-schema-generator": "^2.3.0", + typescript: "~5.5.4", "vega-cli": "^5.28.0", "vega-datasets": "^2.8.1", - "vega-embed": "^6.25.0", + "vega-embed": "^6.26.0", "vega-tooltip": "^0.34.0", "yaml-front-matter": "^4.1.1" }; var dependencies = { "json-stringify-pretty-compact": "~3.0.0", tslib: "~2.6.3", "vega-event-selector": "~3.0.1", - "vega-expression": "~5.1.0", + "vega-expression": "~5.1.1", "vega-util": "~1.17.2", yargs: "~17.7.2" }; var peerDependencies = { vega: "^5.24.0" @@ -171,17 +171,17 @@ engines: engines, packageManager: packageManager }; function isLogicalOr(op) { - return !!op.or; + return hasProperty(op, 'or'); } function isLogicalAnd(op) { - return !!op.and; + return hasProperty(op, 'and'); } function isLogicalNot(op) { - return !!op.not; + return hasProperty(op, 'not'); } function forEachLeaf(op, fn) { if (isLogicalNot(op)) { forEachLeaf(op.not, fn); } else if (isLogicalAnd(op)) { @@ -415,11 +415,15 @@ return keys(obj).length === 0; } // This is a stricter version of Object.keys but with better types. See https://github.com/Microsoft/TypeScript/pull/12253#issuecomment-263132208 const keys = Object.keys; + + // Stricter version from https://github.com/microsoft/TypeScript/issues/51572#issuecomment-1319153323 const vals = Object.values; + + // Stricter version from https://github.com/microsoft/TypeScript/issues/51572#issuecomment-1319153323 const entries$1 = Object.entries; // Using mapped type to declare a collect of flags for a string literal type S // https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types @@ -538,16 +542,11 @@ */ function getFirstDefined() { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } - for (const arg of args) { - if (arg !== undefined) { - return arg; - } - } - return undefined; + return args.find(a => a !== undefined); } // variable used to generate id let idCounter = 42; @@ -613,17 +612,17 @@ for (i = length; i-- !== 0;) if (!deepEqual(a[i], b[i])) return false; return true; } if (a instanceof Map && b instanceof Map) { if (a.size !== b.size) return false; - for (i of a.entries()) if (!b.has(i[0])) return false; - for (i of a.entries()) if (!deepEqual(i[1], b.get(i[0]))) return false; + for (const e of a.entries()) if (!b.has(e[0])) return false; + for (const e of a.entries()) if (!deepEqual(e[1], b.get(e[0]))) return false; return true; } if (a instanceof Set && b instanceof Set) { if (a.size !== b.size) return false; - for (i of a.entries()) if (!b.has(i[0])) return false; + for (const e of a.entries()) if (!b.has(e[0])) return false; return true; } if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) { length = a.length; if (length != b.length) return false; @@ -689,10 +688,21 @@ seen.splice(seenIndex, 1); return `{${out}}`; }(data); } + /** + * Check if the input object has the property and it's not undefined. + * + * @param object the object + * @param property the property to search + * @returns if the object has the property and it's not undefined. + */ + function hasProperty(obj, key) { + return vega.isObject(obj) && vega.hasOwnProperty(obj, key) && obj[key] !== undefined; + } + /* * Constants and utilities for encoding channels (Visual variables) * such as 'x', 'y', 'color'. */ @@ -756,11 +766,11 @@ theta2: 1, radius: 1, radius2: 1 }; function isPolarPositionChannel(c) { - return c in POLAR_POSITION_CHANNEL_INDEX; + return vega.hasOwnProperty(POLAR_POSITION_CHANNEL_INDEX, c); } const GEO_POSIITON_CHANNEL_INDEX = { longitude: 1, longitude2: 1, latitude: 1, @@ -777,11 +787,11 @@ case LONGITUDE2: return 'x2'; } } function isGeoPositionChannel(c) { - return c in GEO_POSIITON_CHANNEL_INDEX; + return vega.hasOwnProperty(GEO_POSIITON_CHANNEL_INDEX, c); } const GEOPOSITION_CHANNELS = keys(GEO_POSIITON_CHANNEL_INDEX); const UNIT_CHANNEL_INDEX = { ...POSITION_CHANNEL_INDEX, ...POLAR_POSITION_CHANNEL_INDEX, @@ -836,14 +846,14 @@ column: _c, facet: _f, ...SINGLE_DEF_UNIT_CHANNEL_INDEX } = SINGLE_DEF_CHANNEL_INDEX; function isSingleDefUnitChannel(str) { - return !!SINGLE_DEF_UNIT_CHANNEL_INDEX[str]; + return vega.hasOwnProperty(SINGLE_DEF_UNIT_CHANNEL_INDEX, str); } function isChannel(str) { - return !!CHANNEL_INDEX[str]; + return vega.hasOwnProperty(CHANNEL_INDEX, str); } const SECONDARY_RANGE_CHANNEL = [X2, Y2, LATITUDE2, LONGITUDE2, THETA2, RADIUS2]; function isSecondaryRangeChannel(c) { const main = getMainRangeChannel(c); return main !== c; @@ -991,11 +1001,11 @@ x: 1, y: 1 }; const POSITION_SCALE_CHANNELS = keys(POSITION_SCALE_CHANNEL_INDEX); function isXorY(channel) { - return channel in POSITION_SCALE_CHANNEL_INDEX; + return vega.hasOwnProperty(POSITION_SCALE_CHANNEL_INDEX, channel); } const POLAR_POSITION_SCALE_CHANNEL_INDEX = { theta: 1, radius: 1 }; @@ -1006,11 +1016,11 @@ const OFFSET_SCALE_CHANNEL_INDEX = { xOffset: 1, yOffset: 1 }; function isXorYOffset(channel) { - return channel in OFFSET_SCALE_CHANNEL_INDEX; + return vega.hasOwnProperty(OFFSET_SCALE_CHANNEL_INDEX, channel); } // NON_POSITION_SCALE_CHANNEL = SCALE_CHANNELS without position / offset const { // x2 and y2 share the same scale as x and y @@ -1027,11 +1037,11 @@ order: _oo, ...NONPOSITION_SCALE_CHANNEL_INDEX } = NONPOSITION_CHANNEL_INDEX; const NONPOSITION_SCALE_CHANNELS = keys(NONPOSITION_SCALE_CHANNEL_INDEX); function isNonPositionScaleChannel(channel) { - return !!NONPOSITION_CHANNEL_INDEX[channel]; + return vega.hasOwnProperty(NONPOSITION_CHANNEL_INDEX, channel); } /** * @returns whether Vega supports legends for a particular channel */ @@ -1062,11 +1072,11 @@ }; /** List of channels with scales */ const SCALE_CHANNELS = keys(SCALE_CHANNEL_INDEX); function isScaleChannel(channel) { - return !!SCALE_CHANNEL_INDEX[channel]; + return vega.hasOwnProperty(SCALE_CHANNEL_INDEX, channel); } /** * Return whether a channel supports a particular mark type. * @param channel channel name * @param mark the mark type @@ -1294,17 +1304,17 @@ count: 1, min: 1, max: 1 }; function isArgminDef(a) { - return !!a && !!a['argmin']; + return hasProperty(a, 'argmin'); } function isArgmaxDef(a) { - return !!a && !!a['argmax']; + return hasProperty(a, 'argmax'); } function isAggregateOp(a) { - return vega.isString(a) && !!AGGREGATE_OP_INDEX[a]; + return vega.isString(a) && vega.hasOwnProperty(AGGREGATE_OP_INDEX, a); } const COUNTING_OPS = new Set(['count', 'valid', 'missing', 'distinct']); function isCountingAggregateOp(aggregate) { return vega.isString(aggregate) && COUNTING_OPS.has(aggregate); } @@ -1349,11 +1359,11 @@ } function isBinParams(bin) { return vega.isObject(bin); } function isParameterExtent(extent) { - return extent?.['param']; + return hasProperty(extent, 'param'); } function autoMaxBins(channel) { switch (channel) { case ROW: case COLUMN: @@ -1377,11 +1387,11 @@ return 10; } } function isExprRef(o) { - return !!o?.expr; + return hasProperty(o, 'expr'); } function replaceExprRef(index) { let { level } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { @@ -1487,42 +1497,42 @@ // TODO: make recursive (e.g. with https://stackoverflow.com/a/64900252/214950 but needs https://github.com/vega/ts-json-schema-generator/issues/568) // Remove ValueRefs from mapped types function isSignalRef(o) { - return !!o?.signal; + return hasProperty(o, 'signal'); } // TODO: add type of value (Make it VgValueRef<V extends ValueOrGradient> {value?:V ...}) // TODO: add vg prefix function isVgRangeStep(range) { - return !!range['step']; + return hasProperty(range, 'step'); } // Domains that are not a union of domains /** * A combined type for any Vega scales that Vega-Lite can generate */ function isDataRefUnionedDomain(domain) { if (!vega.isArray(domain)) { - return 'fields' in domain && !('data' in domain); + return hasProperty(domain, 'fields') && !hasProperty(domain, 'data'); } return false; } function isFieldRefUnionDomain(domain) { if (!vega.isArray(domain)) { - return 'fields' in domain && 'data' in domain; + return hasProperty(domain, 'fields') && hasProperty(domain, 'data'); } return false; } function isDataRefDomain(domain) { if (!vega.isArray(domain)) { - return 'field' in domain && 'data' in domain; + return hasProperty(domain, 'field') && hasProperty(domain, 'data'); } return false; } // TODO: make export interface VgEncodeEntry { @@ -1707,11 +1717,11 @@ let opt = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; const { vgChannel, ignoreVgConfig } = opt; - if (vgChannel && mark[vgChannel] !== undefined) { + if (vgChannel && hasProperty(mark, vgChannel)) { return mark[vgChannel]; } else if (mark[channel] !== undefined) { return mark[channel]; } else if (ignoreVgConfig && (!vgChannel || vgChannel === channel)) { return undefined; @@ -1725,13 +1735,14 @@ */ function getMarkConfig(channel, mark, config) { let { vgChannel } = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + const cfg = getMarkStyleConfig(channel, mark, config.style); return getFirstDefined( // style config has highest precedence - vgChannel ? getMarkStyleConfig(channel, mark, config.style) : undefined, getMarkStyleConfig(channel, mark, config.style), + vgChannel ? cfg : undefined, cfg, // then mark-specific config vgChannel ? config[mark.type][vgChannel] : undefined, config[mark.type][channel], // Need to cast because MarkDef doesn't perfectly match with AnyMarkConfig, but if the type isn't available, we'll get nothing here, which is fine // If there is vgChannel, skip vl channel. @@ -1745,11 +1756,11 @@ function getStyleConfig(p, styles, styleConfigIndex) { styles = vega.array(styles); let value; for (const style of styles) { const styleConfig = styleConfigIndex[style]; - if (styleConfig && styleConfig[p] !== undefined) { + if (hasProperty(styleConfig, p)) { value = styleConfig[p]; } } return value; } @@ -2131,11 +2142,11 @@ */ function isDateTime(o) { if (o && vega.isObject(o)) { for (const part of TIMEUNIT_PARTS) { - if (part in o) { + if (hasProperty(o, part)) { return true; } } } return false; @@ -2315,11 +2326,11 @@ seconds: 1, milliseconds: 1 }; const TIMEUNIT_PARTS = keys(LOCAL_SINGLE_TIMEUNIT_INDEX); function isLocalSingleTimeUnit(timeUnit) { - return !!LOCAL_SINGLE_TIMEUNIT_INDEX[timeUnit]; + return vega.hasOwnProperty(LOCAL_SINGLE_TIMEUNIT_INDEX, timeUnit); } function isBinnedTimeUnit(timeUnit) { if (vega.isObject(timeUnit)) { return timeUnit.binned; } @@ -2510,11 +2521,11 @@ minutes: 1, seconds: 1, milliseconds: 1 }; function isDatePart(timeUnit) { - return !!DATE_PARTS[timeUnit]; + return vega.hasOwnProperty(DATE_PARTS, timeUnit); } function getDateTimePartAndStep(timeUnit) { let step = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; if (isDatePart(timeUnit)) { return { @@ -2541,11 +2552,11 @@ }; } } function isSelectionPredicate(predicate) { - return predicate?.['param']; + return hasProperty(predicate, 'param'); } function isFieldEqualPredicate(predicate) { return !!predicate?.field && predicate.equal !== undefined; } function isFieldLTPredicate(predicate) { @@ -2628,11 +2639,11 @@ } else if (isFieldValidPredicate(predicate)) { return fieldValidPredicate(fieldExpr$1, predicate.valid); } else if (isFieldRangePredicate(predicate)) { const { range - } = predicate; + } = replaceExprRef(predicate); const lower = isSignalRef(range) ? { signal: `${range.signal}[0]` } : range[0]; const upper = isSignalRef(range) ? { signal: `${range.signal}[1]` @@ -2832,10 +2843,11 @@ } const defaultScaleConfig = { pointPadding: 0.5, barBandPaddingInner: 0.1, rectBandPaddingInner: 0, + tickBandPaddingInner: 0.25, bandWithNestedOffsetPaddingInner: 0.2, bandWithNestedOffsetPaddingOuter: 0.2, minBandSize: 2, minFontSize: 8, maxFontSize: 40, @@ -2850,17 +2862,17 @@ quantileCount: 4, quantizeCount: 4, zero: true }; function isExtendedScheme(scheme) { - return !vega.isString(scheme) && !!scheme['name']; + return !vega.isString(scheme) && hasProperty(scheme, 'name'); } function isParameterDomain(domain) { - return domain?.['param']; + return hasProperty(domain, 'param'); } function isDomainUnionWith(domain) { - return domain?.['unionWith']; + return hasProperty(domain, 'unionWith'); } function isFieldRange(range) { return vega.isObject(range) && 'field' in range; } const SCALE_PROPERTY_INDEX = { @@ -3091,15 +3103,15 @@ const GEOSHAPE = Mark.geoshape; function isPathMark(m) { return ['line', 'area', 'trail'].includes(m); } function isRectBasedMark(m) { - return ['rect', 'bar', 'image', 'arc' /* arc is rect/interval in polar coordinate */].includes(m); + return ['rect', 'bar', 'image', 'arc', 'tick' /* arc is rect/interval in polar coordinate */].includes(m); } const PRIMITIVE_MARKS = new Set(keys(Mark)); function isMarkDef(mark) { - return mark['type']; + return hasProperty(mark, 'type'); } const STROKE_CONFIG = ['stroke', 'strokeWidth', 'strokeDash', 'strokeDashOffset', 'strokeOpacity', 'strokeJoin', 'strokeMiterLimit']; const FILL_CONFIG = ['fill', 'fillOpacity']; const FILL_STROKE_CONFIG = [...STROKE_CONFIG, ...FILL_CONFIG]; const VL_ONLY_MARK_CONFIG_INDEX = { @@ -3111,16 +3123,17 @@ theta2: 1, timeUnitBandSize: 1, timeUnitBandPosition: 1 }; const VL_ONLY_MARK_CONFIG_PROPERTIES = keys(VL_ONLY_MARK_CONFIG_INDEX); + const VL_ONLY_RECT_CONFIG = ['binSpacing', 'continuousBandSize', 'discreteBandSize', 'minBandSize']; const VL_ONLY_MARK_SPECIFIC_CONFIG_PROPERTY_INDEX = { area: ['line', 'point'], - bar: ['binSpacing', 'continuousBandSize', 'discreteBandSize', 'minBandSize'], - rect: ['binSpacing', 'continuousBandSize', 'discreteBandSize', 'minBandSize'], + bar: VL_ONLY_RECT_CONFIG, + rect: VL_ONLY_RECT_CONFIG, line: ['point'], - tick: ['bandSize', 'thickness'] + tick: ['bandSize', 'thickness', ...VL_ONLY_RECT_CONFIG] }; const defaultMarkConfig = { color: '#4c78a8', invalid: 'break-paths-show-path-domains', timeUnitBandSize: 1 @@ -3145,33 +3158,32 @@ trail: 1, geoshape: 1 }; const MARK_CONFIGS = keys(MARK_CONFIG_INDEX); function isRelativeBandSize(o) { - return o && o['band'] != undefined; + return hasProperty(o, 'band'); } const BAR_CORNER_RADIUS_INDEX = { horizontal: ['cornerRadiusTopRight', 'cornerRadiusBottomRight'], vertical: ['cornerRadiusTopLeft', 'cornerRadiusTopRight'] }; // Point/Line OverlayMixins are only for area, line, and trail but we don't want to declare multiple types of MarkDef const DEFAULT_RECT_BAND_SIZE = 5; - const defaultBarConfig = { - binSpacing: 1, - continuousBandSize: DEFAULT_RECT_BAND_SIZE, - minBandSize: 0.25, - timeUnitBandPosition: 0.5 - }; const defaultRectConfig = { binSpacing: 0, continuousBandSize: DEFAULT_RECT_BAND_SIZE, minBandSize: 0.25, timeUnitBandPosition: 0.5 }; + const defaultBarConfig = { + ...defaultRectConfig, + binSpacing: 1 + }; const defaultTickConfig = { + ...defaultRectConfig, thickness: 1 }; function getMarkType(m) { return isMarkDef(m) ? m.type : m; } @@ -3846,32 +3858,32 @@ strokeOpacity: 1, opacity: 1, text: 1 }; function isSortByChannel(c) { - return c in SORT_BY_CHANNEL_INDEX; + return vega.hasOwnProperty(SORT_BY_CHANNEL_INDEX, c); } function isSortByEncoding(sort) { - return !!sort?.['encoding']; + return hasProperty(sort, 'encoding'); } function isSortField(sort) { - return sort && (sort['op'] === 'count' || !!sort['field']); + return sort && (sort.op === 'count' || hasProperty(sort, 'field')); } function isSortArray(sort) { return sort && vega.isArray(sort); } function isFacetMapping(f) { - return 'row' in f || 'column' in f; + return hasProperty(f, 'row') || hasProperty(f, 'column'); } /** * Facet mapping for encoding macro */ function isFacetFieldDef(channelDef) { - return !!channelDef && 'header' in channelDef; + return hasProperty(channelDef, 'header'); } /** * Base interface for a facet specification. */ @@ -3879,11 +3891,11 @@ /** * A facet specification without any shortcut / expansion syntax */ function isFacetSpec(spec) { - return 'facet' in spec; + return hasProperty(spec, 'facet'); } /** * Definition object for a constant value (primitive value or gradient definition) of an encoding channel. */ @@ -3899,11 +3911,11 @@ /** * @minProperties 1 */ function isConditionalParameter(c) { - return c['param']; + return hasProperty(c, 'param'); } /** * A FieldDef with Condition<ValueDef> * { @@ -3924,11 +3936,11 @@ /** * Reference to a repeated value. */ function isRepeatRef(field) { - return field && !vega.isString(field) && 'repeat' in field; + return !vega.isString(field) && hasProperty(field, 'repeat'); } /** @@hidden */ function toFieldDefBase(fieldDef) { @@ -3955,11 +3967,11 @@ /** * Definition object for a data field, its type and transformation of an encoding channel. */ function isSortableFieldDef(fieldDef) { - return 'sort' in fieldDef; + return hasProperty(fieldDef, 'sort'); } /** * A field definition of a secondary channel that shares a scale with another primary channel. For example, `x2`, `xError` and `xError2` share the same scale with `x`. */ @@ -4061,14 +4073,14 @@ // Detail // Order Path have no scale function isOrderOnlyDef(orderDef) { - return orderDef && !!orderDef.sort && !orderDef['field']; + return hasProperty(orderDef, 'sort') && !hasProperty(orderDef, 'field'); } function isConditionalDef(channelDef) { - return channelDef && 'condition' in channelDef; + return hasProperty(channelDef, 'condition'); } /** * Return if a channelDef is a ConditionalValueDef with ConditionFieldDef */ @@ -4083,18 +4095,17 @@ function hasConditionalValueDef(channelDef) { const condition = channelDef?.['condition']; return !!condition && (vega.isArray(condition) || isValueDef(condition)); } function isFieldDef(channelDef) { - // TODO: we can't use field in channelDef here as it's somehow failing runtime test - return channelDef && (!!channelDef['field'] || channelDef['aggregate'] === 'count'); + return hasProperty(channelDef, 'field') || channelDef?.aggregate === 'count'; } function channelDefType(channelDef) { return channelDef?.['type']; } function isDatumDef(channelDef) { - return channelDef && 'datum' in channelDef; + return hasProperty(channelDef, 'datum'); } function isContinuousFieldOrDatumDef(cd) { // TODO: make datum support DateTime object return isTypedFieldDef(cd) && !isDiscrete(cd) || isNumericDataDef(cd); } @@ -4107,33 +4118,33 @@ } function isFieldOrDatumDef(channelDef) { return isFieldDef(channelDef) || isDatumDef(channelDef); } function isTypedFieldDef(channelDef) { - return channelDef && ('field' in channelDef || channelDef['aggregate'] === 'count') && 'type' in channelDef; + return channelDef && (hasProperty(channelDef, 'field') || channelDef['aggregate'] === 'count') && hasProperty(channelDef, 'type'); } function isValueDef(channelDef) { - return channelDef && 'value' in channelDef && 'value' in channelDef; + return hasProperty(channelDef, 'value'); } function isScaleFieldDef(channelDef) { - return channelDef && ('scale' in channelDef || 'sort' in channelDef); + return hasProperty(channelDef, 'scale') || hasProperty(channelDef, 'sort'); } function isPositionFieldOrDatumDef(channelDef) { - return channelDef && ('axis' in channelDef || 'stack' in channelDef || 'impute' in channelDef); + return hasProperty(channelDef, 'axis') || hasProperty(channelDef, 'stack') || hasProperty(channelDef, 'impute'); } function isMarkPropFieldOrDatumDef(channelDef) { - return channelDef && 'legend' in channelDef; + return hasProperty(channelDef, 'legend'); } function isStringFieldOrDatumDef(channelDef) { - return channelDef && ('format' in channelDef || 'formatType' in channelDef); + return hasProperty(channelDef, 'format') || hasProperty(channelDef, 'formatType'); } function toStringFieldDef(fieldDef) { // omit properties that don't exist in string field defs return omit(fieldDef, ['legend', 'axis', 'header', 'scale']); } function isOpFieldDef(fieldDef) { - return 'op' in fieldDef; + return hasProperty(fieldDef, 'op'); } /** * Get a Vega field reference from a Vega-Lite field def. */ @@ -4254,15 +4265,11 @@ } else if (isArgminDef(aggregate)) { return `${field} for argmin(${aggregate.argmin})`; } const timeUnitParams = timeUnit && !isBinnedTimeUnit(timeUnit) ? normalizeTimeUnit(timeUnit) : undefined; const fn = aggregate || timeUnitParams?.unit || timeUnitParams?.maxbins && 'timeunit' || isBinning(bin) && 'bin'; - if (fn) { - return `${fn.toUpperCase()}(${field})`; - } else { - return field; - } + return fn ? `${fn.toUpperCase()}(${field})` : field; } const defaultTitleFormatter = (fieldDef, config) => { switch (config.fieldTitle) { case 'plain': return fieldDef.field; @@ -4541,11 +4548,11 @@ sort: { encoding: sort } }; } - const sub = sort.substr(1); + const sub = sort.substring(1); if (sort.charAt(0) === '-' && isSortByChannel(sub)) { return { ...fieldDef, sort: { encoding: sub, @@ -5041,11 +5048,11 @@ style: 1, labelExpr: 1, encoding: 1 }; function isAxisProperty(prop) { - return !!AXIS_PROPERTIES_INDEX[prop]; + return vega.hasOwnProperty(AXIS_PROPERTIES_INDEX, prop); } const AXIS_CONFIGS_INDEX = { axis: 1, axisBand: 1, axisBottom: 1, @@ -5086,11 +5093,11 @@ /** * Unit spec that can have a composite mark and row or column channels (shorthand for a facet spec). */ function isUnitSpec(spec) { - return 'mark' in spec; + return hasProperty(spec, 'mark'); } // TODO: replace string with Mark class CompositeMarkNormalizer { @@ -5574,11 +5581,11 @@ } if (customTooltipWithAggregatedField) { filteredEncoding.tooltip = customTooltipWithAggregatedField; } } else { - if (tooltip['aggregate']) { + if (tooltip.aggregate) { filteredEncoding.tooltip = tooltip; } else { customTooltipWithoutAggregatedField = tooltip; } } @@ -6163,11 +6170,11 @@ ticksOrient, markDef, outerSpec, tooltipEncoding } = errorBarParams(spec, ERRORBAR, config); - delete encodingWithoutContinuousAxis['size']; + delete encodingWithoutContinuousAxis.size; const makeErrorBarPart = makeCompositeAggregatePartFactory(markDef, continuousAxis, continuousAxisChannelDef, encodingWithoutContinuousAxis, config.errorbar); const thickness = markDef.thickness; const size = markDef.size; const tick = { type: 'tick', @@ -6846,17 +6853,17 @@ function isAnyConcatSpec(spec) { return isVConcatSpec(spec) || isHConcatSpec(spec) || isConcatSpec(spec); } function isConcatSpec(spec) { - return 'concat' in spec; + return hasProperty(spec, 'concat'); } function isVConcatSpec(spec) { - return 'vconcat' in spec; + return hasProperty(spec, 'vconcat'); } function isHConcatSpec(spec) { - return 'hconcat' in spec; + return hasProperty(spec, 'hconcat'); } /** * Common properties for all types of specification */ @@ -6871,20 +6878,20 @@ } else { return 'position'; } } function isStep(size) { - return vega.isObject(size) && size['step'] !== undefined; + return hasProperty(size, 'step'); } // TODO(https://github.com/vega/vega-lite/issues/2503): Make this generic so we can support some form of top-down sizing. /** * Common properties for specifying width and height of unit and layer specifications. */ function isFrameMixins(o) { - return o['view'] || o['width'] || o['height']; + return hasProperty(o, 'view') || hasProperty(o, 'width') || hasProperty(o, 'height'); } /** * Base layout for FacetSpec and RepeatSpec. * This is named "GenericComposition" layout as ConcatLayout is a GenericCompositionLayout too @@ -7367,11 +7374,13 @@ } redirectTitleConfig(config); // Remove empty config objects. for (const prop in config) { + // @ts-ignore if (vega.isObject(config[prop]) && isEmpty(config[prop])) { + // @ts-ignore delete config[prop]; } } return isEmpty(config) ? undefined : config; } @@ -7444,22 +7453,22 @@ /** * A layered specification without any shortcut/expansion syntax. */ function isLayerSpec(spec) { - return 'layer' in spec; + return hasProperty(spec, 'layer'); } /** * Base interface for a repeat specification. */ function isRepeatSpec(spec) { - return 'repeat' in spec; + return hasProperty(spec, 'repeat'); } function isLayerRepeatSpec(spec) { - return !vega.isArray(spec.repeat) && spec.repeat['layer']; + return !vega.isArray(spec.repeat) && hasProperty(spec.repeat, 'layer'); } class SpecMapper { map(spec, params) { if (isFacetSpec(spec)) { @@ -7533,11 +7542,11 @@ zero: 1, center: 1, normalize: 1 }; function isStackOffset(s) { - return s in STACK_OFFSET_INDEX; + return vega.hasOwnProperty(STACK_OFFSET_INDEX, s); } const STACKABLE_MARKS = new Set([ARC, BAR, AREA, RULE, POINT, CIRCLE, SQUARE, LINE, TEXT, TICK]); const STACK_BY_DEFAULT_MARKS = new Set([BAR, AREA, ARC]); function isUnbinnedQuantitative(channelDef) { return isFieldDef(channelDef) && channelDefType(channelDef) === 'quantitative' && !channelDef.bin; @@ -8186,11 +8195,11 @@ return undefined; } function replaceRepeaterInMapping(mapping, repeater) { const out = {}; for (const channel in mapping) { - if (vega.hasOwnProperty(mapping, channel)) { + if (hasProperty(mapping, channel)) { const channelDef = mapping[channel]; if (vega.isArray(channelDef)) { // array cannot have condition out[channel] = channelDef // somehow we need to cast it here .map(cd => replaceRepeaterInChannelDef(cd, repeater)).filter(cd => cd); @@ -8242,11 +8251,11 @@ }, params); } } class CoreNormalizer extends SpecMapper { - nonFacetUnitNormalizers = [boxPlotNormalizer, errorBarNormalizer, errorBandNormalizer, new PathOverlayNormalizer(), new RuleForRangedLineNormalizer()]; + nonFacetUnitNormalizers = (() => [boxPlotNormalizer, errorBarNormalizer, errorBandNormalizer, new PathOverlayNormalizer(), new RuleForRangedLineNormalizer()])(); map(spec, params) { // Special handling for a faceted unit spec as it can return a facet spec, not just a layer or unit spec like a normal unit spec. if (isUnitSpec(spec)) { const hasRow = channelHasField(spec.encoding, ROW); const hasColumn = channelHasField(spec.encoding, COLUMN); @@ -8643,74 +8652,74 @@ } return projection ?? parentProjection; } function isFilter(t) { - return 'filter' in t; + return hasProperty(t, 'filter'); } function isImputeSequence(t) { - return t?.['stop'] !== undefined; + return hasProperty(t, 'stop'); } function isLookup(t) { - return 'lookup' in t; + return hasProperty(t, 'lookup'); } function isLookupData(from) { - return 'data' in from; + return hasProperty(from, 'data'); } function isLookupSelection(from) { - return 'param' in from; + return hasProperty(from, 'param'); } function isPivot(t) { - return 'pivot' in t; + return hasProperty(t, 'pivot'); } function isDensity(t) { - return 'density' in t; + return hasProperty(t, 'density'); } function isQuantile(t) { - return 'quantile' in t; + return hasProperty(t, 'quantile'); } function isRegression(t) { - return 'regression' in t; + return hasProperty(t, 'regression'); } function isLoess(t) { - return 'loess' in t; + return hasProperty(t, 'loess'); } function isSample(t) { - return 'sample' in t; + return hasProperty(t, 'sample'); } function isWindow(t) { - return 'window' in t; + return hasProperty(t, 'window'); } function isJoinAggregate(t) { - return 'joinaggregate' in t; + return hasProperty(t, 'joinaggregate'); } function isFlatten(t) { - return 'flatten' in t; + return hasProperty(t, 'flatten'); } function isCalculate(t) { - return 'calculate' in t; + return hasProperty(t, 'calculate'); } function isBin(t) { - return 'bin' in t; + return hasProperty(t, 'bin'); } function isImpute(t) { - return 'impute' in t; + return hasProperty(t, 'impute'); } function isTimeUnit(t) { - return 'timeUnit' in t; + return hasProperty(t, 'timeUnit'); } function isAggregate(t) { - return 'aggregate' in t; + return hasProperty(t, 'aggregate'); } function isStack(t) { - return 'stack' in t; + return hasProperty(t, 'stack'); } function isFold(t) { - return 'fold' in t; + return hasProperty(t, 'fold'); } function isExtent(t) { - return 'extent' in t && !('density' in t) && !('regression' in t); + return hasProperty(t, 'extent') && !hasProperty(t, 'density') && !hasProperty(t, 'regression'); } function normalizeTransform(transform) { return transform.map(t => { if (isFilter(t)) { return { @@ -9063,11 +9072,11 @@ /** * Shared properties between Top-Level specs and Config */ function isFitType(autoSizeType) { - return autoSizeType === 'fit' || autoSizeType === 'fit-x' || autoSizeType === 'fit-y'; + return ['fit', 'fit-x', 'fit-y'].includes(autoSizeType); } function getFitType(sizeType) { return sizeType ? `fit-${getPositionScaleChannel(sizeType)}` : 'fit'; } const TOP_LEVEL_PROPERTIES = ['background', 'padding' @@ -9247,32 +9256,33 @@ /* * Constants and utilities for data. */ + // eslint-disable-next-line @typescript-eslint/ban-types function isUrlData(data) { - return 'url' in data; + return hasProperty(data, 'url'); } function isInlineData(data) { - return 'values' in data; + return hasProperty(data, 'values'); } function isNamedData(data) { - return 'name' in data && !isUrlData(data) && !isInlineData(data) && !isGenerator(data); + return hasProperty(data, 'name') && !isUrlData(data) && !isInlineData(data) && !isGenerator(data); } function isGenerator(data) { return data && (isSequenceGenerator(data) || isSphereGenerator(data) || isGraticuleGenerator(data)); } function isSequenceGenerator(data) { - return 'sequence' in data; + return hasProperty(data, 'sequence'); } function isSphereGenerator(data) { - return 'sphere' in data; + return hasProperty(data, 'sphere'); } function isGraticuleGenerator(data) { - return 'graticule' in data; + return hasProperty(data, 'graticule'); } let DataSourceType = /*#__PURE__*/function (DataSourceType) { DataSourceType[DataSourceType["Raw"] = 0] = "Raw"; DataSourceType[DataSourceType["Main"] = 1] = "Main"; DataSourceType[DataSourceType["Row"] = 2] = "Row"; @@ -10117,13 +10127,13 @@ // multiview displays. Each unit view adds a tuple to the store, but the // state of the selection is the unit selection most recently updated. This // state is captured by the top-level signals that we insert and "push // outer" to from within the units. We need to reassemble this state into // the top-level named signal, except no single selCmpt has a global view. - const namedSg = signals.filter(s => s.name === selCmpt.name)[0]; + const namedSg = signals.find(s => s.name === selCmpt.name); let update = namedSg.update; - if (update.indexOf(VL_SELECTION_RESOLVE) >= 0) { + if (update.includes(VL_SELECTION_RESOLVE)) { namedSg.update = `{${bound.map(proj => `${vega.stringValue(replacePathInField(proj.field))}: ${proj.signals.data}`).join(', ')}}`; } else { for (const proj of bound) { const mapping = `${vega.stringValue(replacePathInField(proj.field))}: ${proj.signals.data}`; if (!update.includes(mapping)) { @@ -10188,11 +10198,11 @@ if (!evt.between) { warn(`${evt} is not an ordered event stream for interval selections.`); continue; } const filters = vega.array(evt.between[0].filter ??= []); - if (filters.indexOf(filterExpr) < 0) { + if (!filters.includes(filterExpr)) { filters.push(filterExpr); } } } }, @@ -10721,11 +10731,11 @@ } = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; const formatConfig = { ...config, ...config.tooltipFormat }; - const toSkip = {}; + const toSkip = new Set(); const expr = reactiveGeom ? 'datum.datum' : 'datum'; const tuples = []; function add(fDef, channel) { const mainChannel = getMainRangeChannel(channel); const fieldDef = isTypedFieldDef(fDef) ? fDef : { @@ -10748,11 +10758,11 @@ const { format, formatType } = getFormatMixins(fieldDef); value = binFormatExpression(startField, endField, format, formatType, formatConfig); - toSkip[channel2] = true; + toSkip.add(channel2); } } if ((isXorY(channel) || channel === THETA || channel === RADIUS) && stack && stack.fieldChannel === channel && stack.offset === 'normalize') { const { format, @@ -10785,11 +10795,11 @@ for (const { channel, key, value } of tuples) { - if (!toSkip[channel] && !out[key]) { + if (!toSkip.has(channel) && !out[key]) { out[key] = value; } } return out; } @@ -10844,11 +10854,11 @@ ariaRoleDescription: { value: ariaRoleDesc } }; } - return mark in VG_MARK_INDEX ? {} : { + return vega.hasOwnProperty(VG_MARK_INDEX, mark) ? {} : { ariaRoleDescription: { value: mark } }; } @@ -10910,21 +10920,23 @@ } = opt; let { defaultRef, defaultValue } = opt; + const channelDef = encoding[channel]; if (defaultRef === undefined) { // prettier-ignore defaultValue ??= getMarkPropOrConfig(channel, markDef, config, { vgChannel, - ignoreVgConfig: true + // If there is no conditonal def, we ignore vgConfig so the output spec is concise. + // However, if there is a conditional def, we must include vgConfig so the default is respected. + ignoreVgConfig: !isConditionalDef(channelDef) }); if (defaultValue !== undefined) { defaultRef = signalOrValueRef(defaultValue); } } - const channelDef = encoding[channel]; const commonProps = { markDef, config, scaleName: model.scaleName(channel), scale: model.getScaleComponent(channel) @@ -11045,10 +11057,11 @@ bandPosition } = _ref; const channel = `${baseChannel}Offset`; // Need to cast as the type can't be inferred automatically const defaultValue = markDef[channel]; + // FIXME: remove as any const channelDef = encoding[channel]; if ((channel === 'xOffset' || channel === 'yOffset') && channelDef) { const ref = midPoint({ channel: channel, channelDef, @@ -11312,10 +11325,12 @@ warn(rangeMarkAlignmentCannotBeExpression(alignChannel)); alignExcludingSignal = undefined; } else { alignExcludingSignal = align; } + + // FIXME: remove as any if (channel === 'x') { return ALIGNED_X_CHANNEL[alignExcludingSignal || (defaultAlign === 'top' ? 'left' : 'center')]; } else { return BASELINED_Y_CHANNEL[alignExcludingSignal || defaultAlign]; } @@ -11525,21 +11540,21 @@ const orient = markDef.orient; const hasSizeDef = encoding[sizeChannel] ?? encoding.size ?? getMarkPropOrConfig('size', markDef, config, { vgChannel: sizeChannel }); const offsetScaleChannel = getOffsetChannel(channel); - const isBarBand = mark === 'bar' && (channel === 'x' ? orient === 'vertical' : orient === 'horizontal'); + const isBarOrTickBand = mark === 'bar' && (channel === 'x' ? orient === 'vertical' : orient === 'horizontal') || mark === 'tick' && (channel === 'y' ? orient === 'vertical' : orient === 'horizontal'); // x, x2, and width -- we must specify two of these in all conditions if (isFieldDef(channelDef) && (isBinning(channelDef.bin) || isBinned(channelDef.bin) || channelDef.timeUnit && !channelDef2) && !(hasSizeDef && !isRelativeBandSize(hasSizeDef)) && !encoding[offsetScaleChannel] && !hasDiscreteDomain(scaleType)) { return rectBinPosition({ fieldDef: channelDef, fieldDef2: channelDef2, channel, model }); - } else if ((isFieldOrDatumDef(channelDef) && hasDiscreteDomain(scaleType) || isBarBand) && !channelDef2) { + } else if ((isFieldOrDatumDef(channelDef) && hasDiscreteDomain(scaleType) || isBarOrTickBand) && !channelDef2) { return positionAndSize(channelDef, channel, model); } else { return rangePosition(channel, model, { defaultPos: 'zeroOrMax', defaultPos2: 'zeroOrMin' @@ -11592,13 +11607,14 @@ } if (!hasFieldDef) { const { bandPaddingInner, barBandPaddingInner, - rectBandPaddingInner + rectBandPaddingInner, + tickBandPaddingInner } = config.scale; - const padding = getFirstDefined(bandPaddingInner, mark === 'bar' ? barBandPaddingInner : rectBandPaddingInner); // this part is like paddingInner in scale.ts + const padding = getFirstDefined(bandPaddingInner, mark === 'tick' ? tickBandPaddingInner : mark === 'bar' ? barBandPaddingInner : rectBandPaddingInner); // this part is like paddingInner in scale.ts if (isSignalRef(padding)) { return { signal: `(1 - (${padding.signal})) * ${sizeChannel}` }; } else if (vega.isNumber(padding)) { @@ -11629,13 +11645,15 @@ const vgSizeChannel = getSizeChannel(channel); const channel2 = getSecondaryRangeChannel(channel); const offsetScaleChannel = getOffsetChannel(channel); const offsetScaleName = model.scaleName(offsetScaleChannel); const offsetScale = model.getScaleComponent(getOffsetScaleChannel(channel)); - + const useVlSizeChannel = + // Always uses size channel for ticks, because tick only calls rectPosition() for the size channel + markDef.type === 'tick' || // use "size" channel for bars, if there is orient and the channel matches the right orientation - const useVlSizeChannel = orient === 'horizontal' && channel === 'y' || orient === 'vertical' && channel === 'x'; + orient === 'horizontal' && channel === 'y' || orient === 'vertical' && channel === 'x'; // Use size encoding / mark property / config if it exists let sizeMixins; if (encoding.size || markDef.size) { if (useVlSizeChannel) { @@ -11914,11 +11932,11 @@ [channel]: valueRef } : {}; } function markDefProperties(mark, ignore) { return VG_MARK_CONFIGS.reduce((m, prop) => { - if (!ALWAYS_IGNORE.has(prop) && mark[prop] !== undefined && ignore[prop] !== 'ignore') { + if (!ALWAYS_IGNORE.has(prop) && hasProperty(mark, prop) && ignore[prop] !== 'ignore') { m[prop] = signalOrValueRef(mark[prop]); } return m; }, {}); } @@ -12044,11 +12062,11 @@ let exists = false; marks.forEach((mark, i) => { const name = mark.name ?? ''; if (name === model.component.mark[0].name) { index = i; - } else if (name.indexOf(VORONOI) >= 0) { + } else if (name.includes(VORONOI)) { exists = true; } }); if (!exists) { marks.splice(index + 1, 0, cellDef); @@ -12090,11 +12108,11 @@ return signals; }, signals: (model, selCmpt, signals) => { const name = selCmpt.name; const proj = selCmpt.project; - const signal = signals.filter(s => s.name === name + TUPLE)[0]; + const signal = signals.find(s => s.name === name + TUPLE); const fields = name + TUPLE_FIELDS; const values = proj.items.map(p => varName(`${name}_${p.field}`)); const valid = values.map(v => `${v} !== null`).join(' && '); if (values.length) { signal.update = `${valid} ? {fields: ${fields}, values: [${values.join(', ')}]} : null`; @@ -12345,11 +12363,11 @@ const name = selCmpt.name; const anchor = name + ANCHOR$1; const delta = name + DELTA$1; const channel = proj.channel; const boundScales = scaleBindings.defined(selCmpt); - const signal = signals.filter(s => s.name === proj.signals[boundScales ? 'data' : 'visual'])[0]; + const signal = signals.find(s => s.name === proj.signals[boundScales ? 'data' : 'visual']); const sizeSg = model.getSizeSignalRef(size).signal; const scaleCmpt = model.getScaleComponent(channel); const scaleType = scaleCmpt && scaleCmpt.get('type'); const reversed = scaleCmpt && scaleCmpt.get('reverse'); // scale parsing sets this flag for fieldDef.sort const sign = !boundScales ? '' : channel === X ? reversed ? '' : '-' : reversed ? '-' : ''; @@ -12411,11 +12429,11 @@ }; function onDelta(model, selCmpt, proj, size, signals) { const name = selCmpt.name; const channel = proj.channel; const boundScales = scaleBindings.defined(selCmpt); - const signal = signals.filter(s => s.name === proj.signals[boundScales ? 'data' : 'visual'])[0]; + const signal = signals.find(s => s.name === proj.signals[boundScales ? 'data' : 'visual']); const sizeSg = model.getSizeSignalRef(size).signal; const scaleCmpt = model.getScaleComponent(channel); const scaleType = scaleCmpt && scaleCmpt.get('type'); const base = boundScales ? domain(model, channel) : signal.name; const delta = name + DELTA; @@ -12565,13 +12583,13 @@ // Project transform applies its defaults. if (key === 'fields' || key === 'encodings') { continue; } if (key === 'mark') { - defaults[key] = { - ...cfg[key], - ...defaults[key] + defaults.mark = { + ...cfg.mark, + ...defaults.mark }; } if (defaults[key] === undefined || defaults[key] === true) { defaults[key] = duplicate(cfg[key] ?? defaults[key]); } @@ -12620,12 +12638,12 @@ const length = `length(data(${store}))`; return pred.empty === false ? `${length} && ${test}` : `!${length} || ${test}`; } function parseSelectionExtent(model, name, extent) { const vname = varName(name); - const encoding = extent['encoding']; - let field = extent['field']; + const encoding = extent.encoding; + let field = extent.field; let selCmpt; try { selCmpt = model.getSelectionComponent(vname, name); } catch (e) { // If a selection isn't found, treat it as a variable parameter. @@ -12703,11 +12721,12 @@ ...axis } = axisCmpt.combine(); if (disable) { return undefined; } - for (const prop in axis) { + for (const p in axis) { + const prop = p; const propType = AXIS_PROPERTY_TYPE[prop]; const propValue = axis[prop]; if (propType && propType !== kind && propType !== 'both') { // Remove properties that are not valid for this kind of axis delete axis[prop]; @@ -12758,10 +12777,11 @@ if (propIndex) { const { vgProp, part } = propIndex; + // FIXME: remove as any setAxisEncode(axis, part, vgProp, propValue); delete axis[prop]; } // else do nothing since the property already supports signal } @@ -13843,41 +13863,37 @@ const opacity = symbolOpacity === undefined ? getMaxValue(encoding.opacity) ?? markDef.opacity : undefined; if (out.fill) { // for fill legend, we don't want any fill in symbol if (channel === 'fill' || filled && channel === COLOR) { delete out.fill; - } else { - if (out.fill['field']) { - // For others, set fill to some opaque value (or nothing if a color is already set) - if (symbolFillColor) { - delete out.fill; - } else { - out.fill = signalOrValueRef(config.legend.symbolBaseFillColor ?? 'black'); - out.fillOpacity = signalOrValueRef(opacity ?? 1); - } - } else if (vega.isArray(out.fill)) { - const fill = getFirstConditionValue(encoding.fill ?? encoding.color) ?? markDef.fill ?? (filled && markDef.color); - if (fill) { - out.fill = signalOrValueRef(fill); - } + } else if (hasProperty(out.fill, 'field')) { + // For others, set fill to some opaque value (or nothing if a color is already set) + if (symbolFillColor) { + delete out.fill; + } else { + out.fill = signalOrValueRef(config.legend.symbolBaseFillColor ?? 'black'); + out.fillOpacity = signalOrValueRef(opacity ?? 1); } + } else if (vega.isArray(out.fill)) { + const fill = getFirstConditionValue(encoding.fill ?? encoding.color) ?? markDef.fill ?? (filled && markDef.color); + if (fill) { + out.fill = signalOrValueRef(fill); + } } } if (out.stroke) { if (channel === 'stroke' || !filled && channel === COLOR) { delete out.stroke; - } else { - if (out.stroke['field'] || symbolStrokeColor) { - // For others, remove stroke field - delete out.stroke; - } else if (vega.isArray(out.stroke)) { - const stroke = getFirstDefined(getFirstConditionValue(encoding.stroke || encoding.color), markDef.stroke, filled ? markDef.color : undefined); - if (stroke) { - out.stroke = { - value: stroke - }; - } + } else if (hasProperty(out.stroke, 'field') || symbolStrokeColor) { + // For others, remove stroke field + delete out.stroke; + } else if (vega.isArray(out.stroke)) { + const stroke = getFirstDefined(getFirstConditionValue(encoding.stroke || encoding.color), markDef.stroke, filled ? markDef.color : undefined); + if (stroke) { + out.stroke = { + value: stroke + }; } } } if (channel !== OPACITY) { const condition = isFieldDef(fieldOrDatumDef) && selectedCondition(model, legendCmpt, fieldOrDatumDef); @@ -14370,10 +14386,11 @@ channel, legendCmpt, legendType }; for (const part of ['labels', 'legend', 'title', 'symbols', 'gradient', 'entries']) { + // FIXME: remove as any (figure out what legendEncoding.entries is) const legendEncodingPart = guideEncodeEntry(legendEncoding[part] ?? {}, model); const value = part in legendEncodeRules ? legendEncodeRules[part](legendEncodingPart, legendEncodeParams) // apply rule : legendEncodingPart; // no rule -- just default values if (value !== undefined && !isEmpty(value)) { @@ -15139,13 +15156,14 @@ meas['*']['count'] = new Set([as ? as : vgField(s, { forAs: true })]); } else { meas[field] ??= {}; - meas[field][op] = new Set([as ? as : vgField(s, { + meas[field][op] ??= new Set(); + meas[field][op].add(as ? as : vgField(s, { forAs: true - })]); + })); } } } for (const s of t.groupby ?? []) { dims.add(s); @@ -15511,13 +15529,14 @@ } else if (isFieldGTPredicate(filter)) { val = signalRefOrValue(filter.gt); } else if (isFieldGTEPredicate(filter)) { val = signalRefOrValue(filter.gte); } else if (isFieldRangePredicate(filter)) { + // FIXME: remove as any val = filter.range[0]; } else if (isFieldOneOfPredicate(filter)) { - val = (filter.oneOf ?? filter['in'])[0]; + val = (filter.oneOf ?? filter.in)[0]; } // else -- for filter expression, we can't infer anything if (val) { if (isDateTime(val)) { implicit[filter.field] = 'date'; @@ -17254,12 +17273,12 @@ } function parseSelectionDomain(model, channel) { const scale = model.component.scales[channel]; const spec = model.specifiedScales[channel].domain; const bin = model.fieldDef(channel)?.bin; - const domain = isParameterDomain(spec) && spec; - const extent = isBinParams(bin) && isParameterExtent(bin.extent) && bin.extent; + const domain = isParameterDomain(spec) ? spec : undefined; + const extent = isBinParams(bin) && isParameterExtent(bin.extent) ? bin.extent : undefined; if (domain || extent) { // As scale parsing occurs before selection parsing, we cannot set // domainRaw directly. So instead, we store the selectionExtent on // the scale component, and then add domainRaw during scale assembly. scale.set('selectionExtent', domain ?? extent, true); @@ -17446,11 +17465,11 @@ return domain; } // only keep sort properties that work with unioned domains const unionDomainSorts = unique(sorts.map(s => { - if (isBoolean(s) || !('op' in s) || vega.isString(s.op) && s.op in MULTIDOMAIN_SORT_OP_INDEX) { + if (isBoolean(s) || !('op' in s) || vega.isString(s.op) && vega.hasOwnProperty(MULTIDOMAIN_SORT_OP_INDEX, s.op)) { return s; } warn(domainSortDropped(s)); return true; }), hash); @@ -18185,11 +18204,11 @@ warn(channelIncompatability); } } if (supportedByScaleType && channelIncompatability === undefined) { if (specifiedValue !== undefined) { - const timeUnit = fieldOrDatumDef['timeUnit']; + const timeUnit = fieldOrDatumDef.timeUnit; const type = fieldOrDatumDef.type; switch (property) { // domainMax/Min to signal if the value is a datetime object case 'domainMax': case 'domainMin': @@ -18206,11 +18225,11 @@ break; default: localScaleCmpt.copyKeyFromObject(property, specifiedScale); } } else { - const value = property in scaleRules ? scaleRules[property]({ + const value = hasProperty(scaleRules, property) ? scaleRules[property]({ model, channel, fieldOrDatumDef, scaleType, scalePadding, @@ -18414,16 +18433,17 @@ // paddingOuter would only be called if it's a band scale, just return the default for bandScale. const { bandPaddingInner, barBandPaddingInner, rectBandPaddingInner, + tickBandPaddingInner, bandWithNestedOffsetPaddingInner } = scaleConfig; if (hasNestedOffsetScale) { return bandWithNestedOffsetPaddingInner; } - return getFirstDefined(bandPaddingInner, mark === 'bar' ? barBandPaddingInner : rectBandPaddingInner); + return getFirstDefined(bandPaddingInner, mark === 'bar' ? barBandPaddingInner : mark === 'tick' ? tickBandPaddingInner : rectBandPaddingInner); } else if (isXorYOffset(channel)) { if (scaleType === ScaleType.BAND) { return scaleConfig.offsetBandPaddingInner; } } @@ -18578,12 +18598,12 @@ warn(discreteChannelCannotEncode(channel, 'ordinal')); } return 'ordinal'; } if (isXorY(channel) || isXorYOffset(channel)) { - if (contains(['rect', 'bar', 'image', 'rule'], mark.type)) { - // The rect/bar mark should fit into a band. + if (contains(['rect', 'bar', 'image', 'rule', 'tick'], mark.type)) { + // The rect/bar/tick mark should fit into a band. // For rule, using band scale to make rule align with axis ticks better https://github.com/vega/vega-lite/issues/3429 return 'band'; } if (hasNestedOffsetScale) { // If there is a nested offset scale, then there is a "band" for the span of the nested scale. @@ -18670,11 +18690,11 @@ // Don't generate scale for shape of geoshape if (fieldOrDatumDef && mark === GEOSHAPE && channel === SHAPE && fieldOrDatumDef.type === GEOJSON) { continue; } - let specifiedScale = fieldOrDatumDef && fieldOrDatumDef['scale']; + let specifiedScale = fieldOrDatumDef && fieldOrDatumDef.scale; if (fieldOrDatumDef && specifiedScale !== null && specifiedScale !== false) { specifiedScale ??= {}; const hasNestedOffsetScale = channelHasNestedOffsetScale(encoding, channel); const sType = scaleType(specifiedScale, channel, fieldOrDatumDef, markDef, hasNestedOffsetScale); scaleComponents[channel] = new ScaleComponent(model.scaleName(`${channel}`, true), { @@ -20350,11 +20370,11 @@ facetFieldDef.header = null; } return facetFieldDef; } channelHasField(channel) { - return !!this.facet[channel]; + return hasProperty(this.facet, channel); } fieldDef(channel) { return this.facet[channel]; } parseData() { @@ -20748,20 +20768,20 @@ // if both datasets have a name defined, we cannot merge if (data.name && other.hasName() && data.name !== other.dataName) { continue; } - const formatMesh = data['format']?.mesh; + const formatMesh = data.format?.mesh; const otherFeature = otherData.format?.feature; // feature and mesh are mutually exclusive if (formatMesh && otherFeature) { continue; } // we have to extract the same feature or mesh - const formatFeature = data['format']?.feature; + const formatFeature = data.format?.feature; if ((formatFeature || otherFeature) && formatFeature !== otherFeature) { continue; } const otherMesh = otherData.format?.mesh; if ((formatMesh || otherMesh) && formatMesh !== otherMesh) { @@ -21966,62 +21986,31 @@ const { config, markDef } = model; const orient = markDef.orient; - const vgSizeChannel = orient === 'horizontal' ? 'width' : 'height'; + const vgSizeAxisChannel = orient === 'horizontal' ? 'x' : 'y'; + const vgThicknessAxisChannel = orient === 'horizontal' ? 'y' : 'x'; const vgThicknessChannel = orient === 'horizontal' ? 'height' : 'width'; return { ...baseEncodeEntry(model, { align: 'ignore', baseline: 'ignore', color: 'include', orient: 'ignore', size: 'ignore', theta: 'ignore' }), - ...pointPosition('x', model, { + ...rectPosition(model, vgSizeAxisChannel), + ...pointPosition(vgThicknessAxisChannel, model, { defaultPos: 'mid', - vgChannel: 'xc' + vgChannel: vgThicknessAxisChannel === 'y' ? 'yc' : 'xc' }), - ...pointPosition('y', model, { - defaultPos: 'mid', - vgChannel: 'yc' - }), - // size / thickness => width / height - ...nonPosition('size', model, { - defaultValue: defaultSize(model), - vgChannel: vgSizeChannel - }), [vgThicknessChannel]: signalOrValueRef(getMarkPropOrConfig('thickness', markDef, config)) }; } }; - function defaultSize(model) { - const { - config, - markDef - } = model; - const { - orient - } = markDef; - const vgSizeChannel = orient === 'horizontal' ? 'width' : 'height'; - const scale = model.getScaleComponent(orient === 'horizontal' ? 'x' : 'y'); - const markPropOrConfig = getMarkPropOrConfig('size', markDef, config, { - vgChannel: vgSizeChannel - }) ?? config.tick.bandSize; - if (markPropOrConfig !== undefined) { - return markPropOrConfig; - } else { - const scaleRange = scale ? scale.get('range') : undefined; - if (scaleRange && isVgRangeStep(scaleRange) && vega.isNumber(scaleRange.step)) { - return scaleRange.step * 3 / 4; - } - const defaultViewStep = getViewConfigDiscreteStep(config.view, vgSizeChannel); - return defaultViewStep * 3 / 4; - } - } const markCompiler = { arc, area, bar, @@ -22964,9 +22953,10 @@ exports.every = every; exports.fieldIntersection = fieldIntersection; exports.flatAccessWithDatum = flatAccessWithDatum; exports.getFirstDefined = getFirstDefined; exports.hasIntersection = hasIntersection; + exports.hasProperty = hasProperty; exports.hash = hash; exports.internalField = internalField; exports.isBoolean = isBoolean; exports.isEmpty = isEmpty; exports.isEqual = isEqual;