diff --git a/build/generate-flow-typed-style-spec.js b/build/generate-flow-typed-style-spec.js index d7a4070a801..0c6749682d1 100644 --- a/build/generate-flow-typed-style-spec.js +++ b/build/generate-flow-typed-style-spec.js @@ -114,6 +114,8 @@ fs.writeFileSync('flow-typed/style-spec.js', `// Generated code; do not edit. Ed declare type ColorSpecification = string; +declare type FormattedSpecification = string; + declare type FilterSpecification = | ['has', string] | ['!has', string] diff --git a/build/generate-style-code.js b/build/generate-style-code.js index 8fa49c1bb21..70e7b852b2b 100644 --- a/build/generate-style-code.js +++ b/build/generate-style-code.js @@ -5,6 +5,7 @@ const fs = require('fs'); const ejs = require('ejs'); const spec = require('../src/style-spec/reference/v8'); const Color = require('../src/style-spec/util/color'); +const {Formatted} = require('../src/style-spec/expression/definitions/formatted'); global.camelize = function (str) { return str.replace(/(?:^|-)(.)/g, function (_, x) { @@ -24,6 +25,8 @@ global.flowType = function (property) { return Object.keys(property.values).map(JSON.stringify).join(' | '); case 'color': return `Color`; + case 'formatted': + return `string | Formatted`; case 'array': if (property.length) { return `[${new Array(property.length).fill(flowType({type: property.value})).join(', ')}]`; @@ -61,6 +64,8 @@ global.runtimeType = function (property) { return 'StringType'; case 'color': return `ColorType`; + case 'formatted': + return `FormattedType`; case 'array': if (property.length) { return `array(${runtimeType({type: property.value})}, ${property.length})`; @@ -131,4 +136,3 @@ const layers = Object.keys(spec.layer.type.values).map((type) => { for (const layer of layers) { fs.writeFileSync(`src/style/style_layer/${layer.type.replace('-', '_')}_style_layer_properties.js`, propertiesJs(layer)) } - diff --git a/debug/streets-v10-viewport.json b/debug/streets-v10-viewport.json index e9abc573e4a..6567e4afc9a 100644 --- a/debug/streets-v10-viewport.json +++ b/debug/streets-v10-viewport.json @@ -2853,10 +2853,10 @@ "text-padding": 1, "text-rotation-alignment": "map", "text-pitch-alignment": "viewport", - "text-field": ["concat", - ["formatted", ["get", "name"], { "font-scale": 1.2 }], - ["formatted", " - ", {}], - ["formatted", ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] }] + "text-field": ["format", + ["get", "name"], { "font-scale": 1.2 }, + " - ", {}, + ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] } ], "text-letter-spacing": 0.01 }, @@ -2888,10 +2888,10 @@ "text-padding": 1, "text-rotation-alignment": "map", "text-pitch-alignment": "viewport", - "text-field": ["concat", - ["formatted", ["get", "name"], { "font-scale": 1.2 }], - ["formatted", " - ", {}], - ["formatted", ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] }] + "text-field": ["format", + ["get", "name"], { "font-scale": 1.2 }, + " - ", {}, + ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] } ], "text-letter-spacing": 0.01 }, @@ -2917,10 +2917,10 @@ "text-padding": 1, "text-rotation-alignment": "map", "text-pitch-alignment": "viewport", - "text-field": ["concat", - ["formatted", ["get", "name"], { "font-scale": 1.2 }], - ["formatted", " - ", {}], - ["formatted", ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] }] + "text-field": ["format", + ["get", "name"], { "font-scale": 1.2 }, + " - ", {}, + ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] } ], "text-letter-spacing": 0.01 }, @@ -3699,10 +3699,10 @@ }, "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.15 ] ], [ 8, [ 0, 0 ] ] ] }, "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] }, - "text-field": ["concat", - ["formatted", ["get", "name_en"], { "font-scale": 1.2 }], - ["formatted", "\n", {}], - ["formatted", ["get", "name"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] }] + "text-field": ["format", + ["get", "name"], { "font-scale": 1.2 }, + "\n", {}, + ["get", "name_en"], { "font-scale": 0.8, "text-font": ["literal", [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ]] } ], "text-max-width": 7, "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 32 ] ] } diff --git a/docs/components/expression-metadata.js b/docs/components/expression-metadata.js index 2c217674b0f..15c8f55741d 100644 --- a/docs/components/expression-metadata.js +++ b/docs/components/expression-metadata.js @@ -135,11 +135,12 @@ const types = { type: 'collator', parameters: [ '{ "case-sensitive": boolean, "diacritic-sensitive": boolean, "locale": string }' ] }], - formatted: [{ + format: [{ type: 'formatted', parameters: [ - 'string', - '{ "font-scale": number, "text-font": array }' + 'input_1: string, options_1: { "font-scale": number, "text-font": array }', + '...', + 'input_n: string, options_n: { "font-scale": number, "text-font": array }' ] }] }; diff --git a/docs/pages/style-spec.js b/docs/pages/style-spec.js index a55e6a056dc..efc2532c968 100644 --- a/docs/pages/style-spec.js +++ b/docs/pages/style-spec.js @@ -159,6 +159,9 @@ const navigation = [ { "title": "String" }, + { + "title": "Formatted" + }, { "title": "Boolean" }, @@ -948,6 +951,19 @@ export default class extends React.Component {

Especially of note is the support for hsl, which can be easier to reason about than rgb().

+
+ +

Formatted

+

The formatted type represents a string broken into sections annotated with separate formatting options.

+ {highlightJSON(` + { + "text-field": ["format", + "foo", { "font-scale": 1.2 }, + "bar", { "font-scale": 0.8 } + ] + }`)} +
+

String

diff --git a/flow-typed/style-spec.js b/flow-typed/style-spec.js index 5f96263d5fe..64d51866e31 100644 --- a/flow-typed/style-spec.js +++ b/flow-typed/style-spec.js @@ -2,6 +2,8 @@ declare type ColorSpecification = string; +declare type FormattedSpecification = string; + declare type FilterSpecification = | ['has', string] | ['!has', string] @@ -223,7 +225,7 @@ declare type SymbolLayerSpecification = {| "icon-pitch-alignment"?: PropertyValueSpecification<"map" | "viewport" | "auto">, "text-pitch-alignment"?: PropertyValueSpecification<"map" | "viewport" | "auto">, "text-rotation-alignment"?: PropertyValueSpecification<"map" | "viewport" | "auto">, - "text-field"?: DataDrivenPropertyValueSpecification, + "text-field"?: DataDrivenPropertyValueSpecification, "text-font"?: DataDrivenPropertyValueSpecification>, "text-size"?: DataDrivenPropertyValueSpecification, "text-max-width"?: DataDrivenPropertyValueSpecification, diff --git a/src/data/bucket/symbol_bucket.js b/src/data/bucket/symbol_bucket.js index 68f524fe651..6f85864a84b 100644 --- a/src/data/bucket/symbol_bucket.js +++ b/src/data/bucket/symbol_bucket.js @@ -349,7 +349,7 @@ class SymbolBucket implements Bucket { const textField = layout.get('text-field'); const iconImage = layout.get('icon-image'); const hasText = - (textField.value.kind !== 'constant' || textField.value.value.length > 0) && + (textField.value.kind !== 'constant' || textField.value.value.toString().length > 0) && (textFont.value.kind !== 'constant' || textFont.value.value.length > 0); const hasIcon = iconImage.value.kind !== 'constant' || iconImage.value.value && iconImage.value.value.length > 0; diff --git a/src/style-spec/expression/definitions/formatted.js b/src/style-spec/expression/definitions/formatted.js index fc24419ba48..b9f5238e51e 100644 --- a/src/style-spec/expression/definitions/formatted.js +++ b/src/style-spec/expression/definitions/formatted.js @@ -18,13 +18,6 @@ export class FormattedSection { this.scale = scale; this.fontStack = fontStack; } - - serialize() { - const fontStack = this.fontStack ? - ["literal", this.fontStack.split(',')] : - null; - return ["formatted", this.text, { "text-font": fontStack, "font-scale": this.scale }]; - } } export class Formatted { @@ -35,77 +28,96 @@ export class Formatted { } toString(): string { - return this.sections.map(section => section.text).join(); + return this.sections.map(section => section.text).join(''); } serialize() { - if (this.sections.length === 1) { - return this.sections[0].serialize(); - } else { - return ["concat"].concat(this.sections.map(section => section.serialize())); + const serialized = ["format"]; + for (const section of this.sections) { + serialized.push(section.text); + const fontStack = section.fontStack ? + ["literal", section.fontStack.split(',')] : + null; + serialized.push({ "text-font": fontStack, "font-scale": section.scale }); } + return serialized; } } -export class FormattedExpression implements Expression { - type: Type; - text: Expression; +type FormattedSectionExpression = { + text: Expression, scale: Expression | null; font: Expression | null; +} + +export class FormattedExpression implements Expression { + type: Type; + sections: Array; - constructor(text: Expression, scale: Expression | null, font: Expression | null) { + constructor(sections: Array) { this.type = FormattedType; - this.text = text; - this.scale = scale; - this.font = font; + this.sections = sections; } static parse(args: Array, context: ParsingContext): ?Expression { - if (args.length !== 3) - return context.error(`Expected two arguments.`); - - const text = context.parse(args[1], 1, ValueType); - if (!text) return null; - const kind = text.type.kind; - if (kind !== 'string' && kind !== 'value' && kind !== 'null') - return context.error(`Formatted text type must be 'string', 'value', or 'null'.`); - - const options = (args[2]: any); - if (typeof options !== "object" || Array.isArray(options)) - return context.error(`Format options argument must be an object.`); - - let scale = null; - if (options['font-scale']) { - scale = context.parse(options['font-scale'], 1, NumberType); - if (!scale) return null; + if (args.length < 3) { + return context.error(`Expected at least two arguments.`); } - let font = null; - if (options['text-font']) { - font = context.parse(options['text-font'], 1, ValueType); // Require array of strings? - if (!font) return null; + if ((args.length - 1) % 2 !== 0) { + return context.error(`Expected an even number of arguments.`); } - return new FormattedExpression(text, scale, font); + const sections: Array = []; + for (let i = 1; i < args.length - 1; i += 2) { + const text = context.parse(args[i], 1, ValueType); + if (!text) return null; + const kind = text.type.kind; + if (kind !== 'string' && kind !== 'value' && kind !== 'null') + return context.error(`Formatted text type must be 'string', 'value', or 'null'.`); + + const options = (args[i + 1]: any); + if (typeof options !== "object" || Array.isArray(options)) + return context.error(`Format options argument must be an object.`); + + let scale = null; + if (options['font-scale']) { + scale = context.parse(options['font-scale'], 1, NumberType); + if (!scale) return null; + } + + let font = null; + if (options['text-font']) { + font = context.parse(options['text-font'], 1, ValueType); // Require array of strings? + if (!font) return null; + } + sections.push({text, scale, font}); + } + + return new FormattedExpression(sections); } evaluate(ctx: EvaluationContext) { - return new Formatted([ - new FormattedSection( - this.text.evaluate(ctx) || "", - this.scale ? this.scale.evaluate(ctx) : null, - this.font ? this.font.evaluate(ctx).join(',') : null + return new Formatted( + this.sections.map(section => + new FormattedSection( + section.text.evaluate(ctx) || "", + section.scale ? section.scale.evaluate(ctx) : null, + section.font ? section.font.evaluate(ctx).join(',') : null + ) ) - ]); + ); } eachChild(fn: (Expression) => void) { - fn(this.text); - if (this.scale) { - fn(this.scale); - } - if (this.font) { - fn(this.font); + for (const section of this.sections) { + fn(section.text); + if (section.scale) { + fn(section.scale); + } + if (section.font) { + fn(section.font); + } } } @@ -116,13 +128,18 @@ export class FormattedExpression implements Expression { } serialize() { - const options = {}; - if (this.scale) { - options['font-scale'] = this.scale.serialize(); - } - if (this.font) { - options['text-font'] = this.font.serialize(); + const serialized = ["format"]; + for (const section of this.sections) { + serialized.push(section.text.serialize()); + const options = {}; + if (section.scale) { + options['font-scale'] = section.scale.serialize(); + } + if (section.font) { + options['text-font'] = section.font.serialize(); + } + serialized.push(options); } - return ["formatted", this.text.serialize(), options]; + return serialized; } } diff --git a/src/style-spec/expression/definitions/index.js b/src/style-spec/expression/definitions/index.js index e91e5997dbf..c36001e97db 100644 --- a/src/style-spec/expression/definitions/index.js +++ b/src/style-spec/expression/definitions/index.js @@ -1,6 +1,6 @@ // @flow -import { NumberType, StringType, BooleanType, ColorType, ObjectType, ValueType, ErrorType, CollatorType, FormattedType, array, toString } from '../types'; +import { NumberType, StringType, BooleanType, ColorType, ObjectType, ValueType, ErrorType, CollatorType, array, toString } from '../types'; import { typeOf, Color, validateRGBA } from '../values'; import CompoundExpression from '../compound_expression'; @@ -47,7 +47,7 @@ const expressions: ExpressionRegistry = { 'case': Case, 'coalesce': Coalesce, 'collator': CollatorExpression, - 'formatted': FormattedExpression, + 'format': FormattedExpression, 'interpolate': Interpolate, 'length': Length, 'let': Let, @@ -541,24 +541,11 @@ CompoundExpression.register(expressions, { [StringType], (ctx, [s]) => s.evaluate(ctx).toLowerCase() ], - 'concat': { - type: StringType, - overloads: [ - [ - varargs(StringType), - (ctx, args) => args.map(arg => arg.evaluate(ctx)).join('') - ], - [ - varargs(FormattedType), - (ctx, args) => { - return new Formatted( - args.map(arg => arg.evaluate(ctx).sections) - .reduce((acc, val) => acc.concat(val), []) - ); - } - ] - ] - }, + 'concat': [ + StringType, + varargs(StringType), + (ctx, args) => args.map(arg => arg.evaluate(ctx)).join('') + ], 'resolved-locale': [ StringType, [CollatorType], diff --git a/src/style-spec/expression/definitions/literal.js b/src/style-spec/expression/definitions/literal.js index 09146fec3d5..d686aa6e036 100644 --- a/src/style-spec/expression/definitions/literal.js +++ b/src/style-spec/expression/definitions/literal.js @@ -62,16 +62,13 @@ class Literal implements Expression { // so we have to implement an equivalent serialization here return ["rgba"].concat(this.value.toArray()); } else if (this.value instanceof Formatted) { - // Constant-folding can generate Literal expressions that you - // couldn't actually generate with a "literal" expression, - // so we have to implement an equivalent serialization here + // Same as Color return this.value.serialize(); } else { assert(this.value === null || typeof this.value === 'string' || typeof this.value === 'number' || - typeof this.value === 'boolean' || - typeof this.value === 'formatted'); + typeof this.value === 'boolean'); return (this.value: any); } } diff --git a/src/style-spec/expression/types.js b/src/style-spec/expression/types.js index 02f2aaff719..5a6cd4c2d8d 100644 --- a/src/style-spec/expression/types.js +++ b/src/style-spec/expression/types.js @@ -66,6 +66,7 @@ const valueMemberTypes = [ StringType, BooleanType, ColorType, + FormattedType, ObjectType, array(ValueType) ]; diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 9698f1c624d..93750fe70d4 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -1508,7 +1508,7 @@ "type": "formatted", "default": "", "tokens": true, - "doc": "Value to use for a text label.", + "doc": "Value to use for a text label. If a plain `string` is provided, it will be treated as a `formatted` with default/inherited formatting options.", "sdk-support": { "basic functionality": { "js": "0.10.0", @@ -2505,7 +2505,7 @@ } } }, - "formatted": { + "format": { "doc": "Returns `formatted` text containing annotations for use in mixed-format `text-field` entries. If set, the `text-font` argument overrides the font specified by the root layout properties. If set, the `font-scale` argument specifies a scaling factor relative to the `text-size` specified in the root layout properties.", "group": "Types", "sdk-support": { diff --git a/src/style-spec/validate/validate_formatted.js b/src/style-spec/validate/validate_formatted.js index 4cac73e2450..61ed402df3d 100644 --- a/src/style-spec/validate/validate_formatted.js +++ b/src/style-spec/validate/validate_formatted.js @@ -1,6 +1,4 @@ // @flow - -import ValidationError from '../error/validation_error'; import validateExpression from './validate_expression'; import validateString from './validate_string'; diff --git a/src/style/style_layer/background_style_layer_properties.js b/src/style/style_layer/background_style_layer_properties.js index 5b1cbacbefd..182cd821891 100644 --- a/src/style/style_layer/background_style_layer_properties.js +++ b/src/style/style_layer/background_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "background-color": DataConstantProperty, diff --git a/src/style/style_layer/circle_style_layer_properties.js b/src/style/style_layer/circle_style_layer_properties.js index 3e9176f6947..9321d797397 100644 --- a/src/style/style_layer/circle_style_layer_properties.js +++ b/src/style/style_layer/circle_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "circle-radius": DataDrivenProperty, diff --git a/src/style/style_layer/fill_extrusion_style_layer_properties.js b/src/style/style_layer/fill_extrusion_style_layer_properties.js index ac0966a83be..78a29cdcf18 100644 --- a/src/style/style_layer/fill_extrusion_style_layer_properties.js +++ b/src/style/style_layer/fill_extrusion_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "fill-extrusion-opacity": DataConstantProperty, diff --git a/src/style/style_layer/fill_style_layer_properties.js b/src/style/style_layer/fill_style_layer_properties.js index 9851f734508..a62dd756e1c 100644 --- a/src/style/style_layer/fill_style_layer_properties.js +++ b/src/style/style_layer/fill_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "fill-antialias": DataConstantProperty, diff --git a/src/style/style_layer/heatmap_style_layer_properties.js b/src/style/style_layer/heatmap_style_layer_properties.js index dd53a9ee9a0..d425156c001 100644 --- a/src/style/style_layer/heatmap_style_layer_properties.js +++ b/src/style/style_layer/heatmap_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "heatmap-radius": DataDrivenProperty, diff --git a/src/style/style_layer/hillshade_style_layer_properties.js b/src/style/style_layer/hillshade_style_layer_properties.js index f16f9b51fd6..3f2e5c2ecdd 100644 --- a/src/style/style_layer/hillshade_style_layer_properties.js +++ b/src/style/style_layer/hillshade_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "hillshade-illumination-direction": DataConstantProperty, diff --git a/src/style/style_layer/layer_properties.js.ejs b/src/style/style_layer/layer_properties.js.ejs index d9740285990..7bf3c2bf567 100644 --- a/src/style/style_layer/layer_properties.js.ejs +++ b/src/style/style_layer/layer_properties.js.ejs @@ -19,6 +19,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + <% if (layoutProperties.length) { -%> export type LayoutProps = {| <% for (const property of layoutProperties) { -%> diff --git a/src/style/style_layer/line_style_layer_properties.js b/src/style/style_layer/line_style_layer_properties.js index bef2f2ed44a..442d67d04b3 100644 --- a/src/style/style_layer/line_style_layer_properties.js +++ b/src/style/style_layer/line_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type LayoutProps = {| "line-cap": DataConstantProperty<"butt" | "round" | "square">, "line-join": DataDrivenProperty<"bevel" | "round" | "miter">, diff --git a/src/style/style_layer/raster_style_layer_properties.js b/src/style/style_layer/raster_style_layer_properties.js index e0193d510b2..4c0e28bb8f3 100644 --- a/src/style/style_layer/raster_style_layer_properties.js +++ b/src/style/style_layer/raster_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type PaintProps = {| "raster-opacity": DataConstantProperty, diff --git a/src/style/style_layer/symbol_style_layer_properties.js b/src/style/style_layer/symbol_style_layer_properties.js index ed22587f068..1d06c35d6bd 100644 --- a/src/style/style_layer/symbol_style_layer_properties.js +++ b/src/style/style_layer/symbol_style_layer_properties.js @@ -14,6 +14,8 @@ import { import type Color from '../../style-spec/util/color'; +import type {Formatted} from '../../style-spec/expression/definitions/formatted'; + export type LayoutProps = {| "symbol-placement": DataConstantProperty<"point" | "line" | "line-center">, "symbol-spacing": DataConstantProperty, @@ -34,7 +36,7 @@ export type LayoutProps = {| "icon-pitch-alignment": DataConstantProperty<"map" | "viewport" | "auto">, "text-pitch-alignment": DataConstantProperty<"map" | "viewport" | "auto">, "text-rotation-alignment": DataConstantProperty<"map" | "viewport" | "auto">, - "text-field": DataDrivenProperty, + "text-field": DataDrivenProperty, "text-font": DataDrivenProperty>, "text-size": DataDrivenProperty, "text-max-width": DataDrivenProperty, diff --git a/test/integration/expression-tests/concat/formatted/test.json b/test/integration/expression-tests/formatted/basic/test.json similarity index 59% rename from test/integration/expression-tests/concat/formatted/test.json rename to test/integration/expression-tests/formatted/basic/test.json index dffabf25f70..eef30eb57f4 100644 --- a/test/integration/expression-tests/concat/formatted/test.json +++ b/test/integration/expression-tests/formatted/basic/test.json @@ -1,27 +1,22 @@ { "expression": [ - "concat", + "format", "a", - [ - "formatted", - "b", - { - "font-scale": 2 - } - ], - [ - "formatted", - "c", - { - "text-font": [ - "literal", - [ - "a", - "b" - ] + {}, + "b", + { + "font-scale": 2 + }, + "c", + { + "text-font": [ + "literal", + [ + "a", + "b" ] - } - ] + ] + } ], "inputs": [ [ @@ -34,7 +29,7 @@ "result": "success", "isFeatureConstant": true, "isZoomConstant": true, - "type": "string" + "type": "formatted" }, "outputs": [ { @@ -58,31 +53,28 @@ } ], "serialized": [ - "concat", - [ - "formatted", - "a", - { - "font-scale": null, - "text-font": null - } - ], - [ - "formatted", - "b", - { - "font-scale": 2, - "text-font": null - } - ], - [ - "formatted", - "c", - { - "font-scale": null, - "text-font": ["literal", ["a", "b"]] - } - ] + "format", + "a", + { + "font-scale": null, + "text-font": null + }, + "b", + { + "font-scale": 2, + "text-font": null + }, + "c", + { + "font-scale": null, + "text-font": [ + "literal", + [ + "a", + "b" + ] + ] + } ] } } diff --git a/test/integration/expression-tests/formatted/to-string/test.json b/test/integration/expression-tests/formatted/to-string/test.json new file mode 100644 index 00000000000..2c505712fbd --- /dev/null +++ b/test/integration/expression-tests/formatted/to-string/test.json @@ -0,0 +1,42 @@ +{ + "expression": [ + "to-string", + [ + "format", + "a", + {}, + "b", + { + "font-scale": 2 + }, + "c", + { + "text-font": [ + "literal", + [ + "a", + "b" + ] + ] + } + ] + ], + "inputs": [ + [ + {}, + {} + ] + ], + "expected": { + "compiled": { + "result": "success", + "isFeatureConstant": true, + "isZoomConstant": true, + "type": "string" + }, + "outputs": [ + "abc" + ], + "serialized": "abc" + } +} diff --git a/test/integration/render-tests/text-field/formatted-line/expected.png b/test/integration/render-tests/text-field/formatted-line/expected.png index 2e8583d0719..a5c6a684a08 100644 Binary files a/test/integration/render-tests/text-field/formatted-line/expected.png and b/test/integration/render-tests/text-field/formatted-line/expected.png differ diff --git a/test/integration/render-tests/text-field/formatted-line/style.json b/test/integration/render-tests/text-field/formatted-line/style.json index 5f2766c9119..cd8e99a5f1e 100644 --- a/test/integration/render-tests/text-field/formatted-line/style.json +++ b/test/integration/render-tests/text-field/formatted-line/style.json @@ -37,10 +37,10 @@ "symbol-placement": "line", "text-allow-overlap": true, "text-ignore-placement": true, - "text-field": ["concat", - ["formatted", ["get", "name"], { "font-scale": 1.2 }], - " - ", - ["formatted", ["get", "class"], { "font-scale": 0.8, "text-font": ["literal", [ "NotoCJK" ]] }] + "text-field": ["format", + ["get", "name"], { "font-scale": 1.2 }, + " - ", {}, + ["get", "class"], { "font-scale": 0.8, "text-font": ["literal", [ "NotoCJK" ]] } ], "text-font": [ "Open Sans Semibold", diff --git a/test/integration/render-tests/text-field/formatted/style.json b/test/integration/render-tests/text-field/formatted/style.json index 042c1d26191..5fdaa3c4178 100644 --- a/test/integration/render-tests/text-field/formatted/style.json +++ b/test/integration/render-tests/text-field/formatted/style.json @@ -36,11 +36,11 @@ "type": "symbol", "source": "point", "layout": { - "text-field": ["concat", - ["formatted", ["get", "name_en"], { "font-scale": 1.5 }], - ["formatted", "Italy", { "font-scale": 0.5} ], - "\n", - ["formatted", ["get", "name"], { "font-scale": 0.5, "text-font": ["literal", [ "NotoCJK" ]] }] + "text-field": ["format", + ["get", "name_en"], { "font-scale": 1.5 }, + "Italy", { "font-scale": 0.5} , + "\n", {}, + ["get", "name"], { "font-scale": 0.5, "text-font": ["literal", [ "NotoCJK" ]] } ], "text-font": [ "Open Sans Semibold", diff --git a/test/unit/style-spec/spec.test.js b/test/unit/style-spec/spec.test.js index 8052f61c217..0f29e0d10a1 100644 --- a/test/unit/style-spec/spec.test.js +++ b/test/unit/style-spec/spec.test.js @@ -35,7 +35,8 @@ function validSchema(k, t, obj, ref, version, kind) { 'text-anchor-enum', 'text-transform-enum', 'visibility-enum', - 'property-type' + 'property-type', + 'formatted' ]); const keys = [ 'default',