Themeconfig: improve icon badge expansion

This commit is contained in:
Pieter Vander Vennet 2025-01-18 02:31:15 +01:00
parent add464f58f
commit 2b0b62fcfa
2 changed files with 77 additions and 48 deletions

View file

@ -1,18 +1,6 @@
import { import { Concat, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault } from "./Conversion"
Concat,
DesugaringContext,
DesugaringStep,
Each,
FirstOf,
Fuse,
On,
SetDefault,
} from "./Conversion"
import { LayerConfigJson } from "../Json/LayerConfigJson" import { LayerConfigJson } from "../Json/LayerConfigJson"
import { import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
MinimalTagRenderingConfigJson,
TagRenderingConfigJson,
} from "../Json/TagRenderingConfigJson"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import RewritableConfigJson from "../Json/RewritableConfigJson" import RewritableConfigJson from "../Json/RewritableConfigJson"
import SpecialVisualizations from "../../../UI/SpecialVisualizations" import SpecialVisualizations from "../../../UI/SpecialVisualizations"
@ -36,7 +24,7 @@ import { ExpandTagRendering } from "./ExpandTagRendering"
class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> { class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
constructor() { constructor() {
super( super(
'Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus \'ExpandFilter\' should be run afterwards. Can be disabled with "#filter":"no-auto"', "Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus 'ExpandFilter' should be run afterwards. Can be disabled with \"#filter\":\"no-auto\"",
["filter"], ["filter"],
"AddFiltersFromTagRenderings" "AddFiltersFromTagRenderings"
) )
@ -139,7 +127,7 @@ class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> {
if (json.freeform.inline === true) { if (json.freeform.inline === true) {
context.err( context.err(
"'inline' is set, but the rendering contains a special visualisation...\n " + "'inline' is set, but the rendering contains a special visualisation...\n " +
spec[key] spec[key]
) )
} }
json = JSON.parse(JSON.stringify(json)) json = JSON.parse(JSON.stringify(json))
@ -238,20 +226,20 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
if (blacklisted?.length > 0 && used?.length > 0) { if (blacklisted?.length > 0 && used?.length > 0) {
context.err( context.err(
"The {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." + "The {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." +
"\n Whitelisted: " + "\n Whitelisted: " +
used.join(", ") + used.join(", ") +
"\n Blacklisted: " + "\n Blacklisted: " +
blacklisted.join(", ") blacklisted.join(", ")
) )
} }
for (const usedLabel of used) { for (const usedLabel of used) {
if (!allLabels.has(usedLabel)) { if (!allLabels.has(usedLabel)) {
context.err( context.err(
"This layers specifies a special question element for label `" + "This layers specifies a special question element for label `" +
usedLabel + usedLabel +
"`, but this label doesn't exist.\n" + "`, but this label doesn't exist.\n" +
" Available labels are " + " Available labels are " +
Array.from(allLabels).join(", ") Array.from(allLabels).join(", ")
) )
} }
seen.add(usedLabel) seen.add(usedLabel)
@ -265,8 +253,8 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> {
const question: QuestionableTagRenderingConfigJson = { const question: QuestionableTagRenderingConfigJson = {
id: "leftover-questions", id: "leftover-questions",
render: { render: {
"*": `{questions( ,${Array.from(seen).join(";")})}`, "*": `{questions( ,${Array.from(seen).join(";")})}`
}, }
} }
json.tagRenderings.push(question) json.tagRenderings.push(question)
} }
@ -348,13 +336,13 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
if (json.allowMove && !usedSpecialFunctions.has("move_button")) { if (json.allowMove && !usedSpecialFunctions.has("move_button")) {
json.tagRenderings.push({ json.tagRenderings.push({
id: "move-button", id: "move-button",
render: { "*": "{move_button()}" }, render: { "*": "{move_button()}" }
}) })
} }
if (json.deletion && !usedSpecialFunctions.has("delete_button")) { if (json.deletion && !usedSpecialFunctions.has("delete_button")) {
json.tagRenderings.push({ json.tagRenderings.push({
id: "delete-button", id: "delete-button",
render: { "*": "{delete_button()}" }, render: { "*": "{delete_button()}" }
}) })
} }
@ -369,9 +357,9 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
or: [ or: [
"__featureSwitchIsDebugging=true", "__featureSwitchIsDebugging=true",
"mapcomplete-show_tags=full", "mapcomplete-show_tags=full",
"mapcomplete-show_debug=yes", "mapcomplete-show_debug=yes"
], ]
}, }
} }
json.tagRenderings?.push(trc) json.tagRenderings?.push(trc)
} }
@ -479,10 +467,10 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
private static convertIfNeeded( private static convertIfNeeded(
input: input:
| (object & { | (object & {
special: { special: {
type: string type: string
} }
}) })
| any, | any,
context: ConversionContext context: ConversionContext
): any { ): any {
@ -580,7 +568,7 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> {
.map((nm) => RewriteSpecial.escapeStr(special[nm] ?? "", context)) .map((nm) => RewriteSpecial.escapeStr(special[nm] ?? "", context))
.join(",") .join(",")
return { return {
"*": `{${type}(${args})${clss}}`, "*": `{${type}(${args})${clss}}`
} }
} }
@ -678,11 +666,50 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
}[] = [] }[] = []
for (let i = 0; i < badgesJson.length; i++) { for (let i = 0; i < badgesJson.length; i++) {
const iconBadge: { const iconBadge: string | ({
if: TagConfigJson if: TagConfigJson
then: string | MinimalTagRenderingConfigJson then: string | MinimalTagRenderingConfigJson
} = badgesJson[i] }) = badgesJson[i]
const expanded = this._expand.convert(
if (typeof iconBadge === "string") {
const expanded: QuestionableTagRenderingConfigJson[] = this._expand.convert(
iconBadge,
context.enters("iconBadges", i)
)
for (const tr of expanded) {
const condition = tr.condition
for (const trElement of tr.mappings) {
const showIf = TagUtils.optimzeJson({
and: Utils.NoNull([condition,
{
or: Utils.NoNull([
trElement.alsoShowIf, trElement.if
])
}
])
})
if (showIf === true) {
context.warn("Dropping iconBadge that would be _always_ shown: " + (trElement.icon ?? trElement.then))
continue
}
if (showIf === false) {
continue
}
iconBadges.push({
if: showIf,
then: trElement.icon ?? trElement.then
})
}
}
continue
}
const expanded: QuestionableTagRenderingConfigJson[] = this._expand.convert(
<QuestionableTagRenderingConfigJson>iconBadge.then, <QuestionableTagRenderingConfigJson>iconBadge.then,
context.enters("iconBadges", i) context.enters("iconBadges", i)
) )
@ -694,7 +721,7 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
iconBadges.push( iconBadges.push(
...expanded.map((resolved) => ({ ...expanded.map((resolved) => ({
if: iconBadge.if, if: iconBadge.if,
then: <MinimalTagRenderingConfigJson>resolved, then: <MinimalTagRenderingConfigJson>resolved
})) }))
) )
} }
@ -707,7 +734,7 @@ class PreparePointRendering extends Fuse<PointRenderingConfigJson> {
constructor(state: DesugaringContext, layer: LayerConfigJson) { constructor(state: DesugaringContext, layer: LayerConfigJson) {
super( super(
"Prepares point renderings by expanding 'icon' and 'iconBadges'." + "Prepares point renderings by expanding 'icon' and 'iconBadges'." +
" A tagRendering from the host tagRenderings will be substituted in", " A tagRendering from the host tagRenderings will be substituted in",
new On( new On(
"marker", "marker",
new Each( new Each(
@ -834,7 +861,7 @@ export class AddRatingBadge extends DesugaringStep<LayerConfigJson> {
const specialVis: Exclude<RenderingSpecification, string>[] = < const specialVis: Exclude<RenderingSpecification, string>[] = <
Exclude<RenderingSpecification, string>[] Exclude<RenderingSpecification, string>[]
>ValidationUtils.getAllSpecialVisualisations(<any>json.tagRenderings).filter( >ValidationUtils.getAllSpecialVisualisations(<any>json.tagRenderings).filter(
(rs) => typeof rs !== "string" (rs) => typeof rs !== "string"
) )
const funcs = new Set<string>(specialVis.map((rs) => rs.func.funcName)) const funcs = new Set<string>(specialVis.map((rs) => rs.func.funcName))
@ -870,7 +897,7 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
} }
return <TagRenderingConfigJson>{ return <TagRenderingConfigJson>{
id: "title_icon_auto_" + tr.id, id: "title_icon_auto_" + tr.id,
mappings, mappings
} }
} }
@ -915,8 +942,8 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
.enters("titleIcons", i) .enters("titleIcons", i)
.warn( .warn(
"TagRendering with id " + "TagRendering with id " +
trId + trId +
" does not have any icons, not generating an icon for this" " does not have any icons, not generating an icon for this"
) )
continue continue
} }
@ -979,7 +1006,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
(layer) => (layer) =>
new Concat( new Concat(
new ExpandTagRendering(state, layer, { new ExpandTagRendering(state, layer, {
addToContext: options?.addTagRenderingsToContext ?? false, addToContext: options?.addTagRenderingsToContext ?? false
}) })
) )
), ),

View file

@ -70,16 +70,18 @@ export default interface PointRenderingConfigJson {
* They will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout. * They will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.
* *
* Note: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle * Note: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle
* Alternatively, this can reuse a _tagRendering_ from another layer, e.g. one of the 'icons'-tagrenderings.
* See ExpandIconBadges on how this is handled
* group: hidden * group: hidden
*/ */
iconBadges?: { iconBadges?: (string | {
if: TagConfigJson if: TagConfigJson
/** /**
* Badge to show * Badge to show
* Type: icon * Type: icon
*/ */
then: string | MinimalTagRenderingConfigJson then: string | MinimalTagRenderingConfigJson
}[] })[]
/** /**
* question: What size should the marker be on the map? * question: What size should the marker be on the map?