Themes: add possibility to add an icon to 'render' (just like with mappings), add contact:mastodon support as general question, add mastodon question to hackerspaces
This commit is contained in:
parent
64648f7bb4
commit
03aafbe99c
8 changed files with 237 additions and 99 deletions
|
@ -112,6 +112,7 @@
|
|||
"website",
|
||||
"email",
|
||||
"phone",
|
||||
"mastodon",
|
||||
{
|
||||
"builtin": "opening_hours_24_7",
|
||||
"override": {
|
||||
|
|
|
@ -173,11 +173,13 @@
|
|||
"render": {
|
||||
"*": "<a href='tel:{phone}'>{phone}</a>"
|
||||
},
|
||||
"icon": "./assets/layers/questions/phone.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "contact:phone~*",
|
||||
"then": "<a href='tel:{contact:phone}'>{contact:phone}</a>",
|
||||
"hideInAnswer": true
|
||||
"hideInAnswer": true,
|
||||
"icon": "./assets/layers/questions/phone.svg"
|
||||
}
|
||||
],
|
||||
"freeform": {
|
||||
|
@ -188,6 +190,21 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mastodon",
|
||||
"description": "Shows and asks for the mastodon handle",
|
||||
"question": {
|
||||
"en": "What is the Mastodon-handle of {title()}?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "contact:mastodon",
|
||||
"type": "fediverse"
|
||||
},
|
||||
"render": {
|
||||
"*": "{fediverse_link(contact:mastodon)}"
|
||||
},
|
||||
"icon": "./assets/svg/mastodon.svg"
|
||||
},
|
||||
{
|
||||
"id": "osmlink",
|
||||
"render": {
|
||||
|
@ -205,6 +222,7 @@
|
|||
"render": {
|
||||
"*": "<a href='mailto:{email}' target='_blank'>{email}</a>"
|
||||
},
|
||||
"icon": "./assets/svg/envelope.svg",
|
||||
"labels": [
|
||||
"contact"
|
||||
],
|
||||
|
@ -236,6 +254,7 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "contact:email~*",
|
||||
"icon": "./assets/svg/envelope.svg",
|
||||
"then": "<a href='mailto:{contact:email}' target='_blank'>{contact:email}</a>",
|
||||
"hideInAnswer": true
|
||||
}
|
||||
|
@ -253,6 +272,7 @@
|
|||
"labels": [
|
||||
"contact"
|
||||
],
|
||||
"icon": "./assets/layers/icons/website.svg",
|
||||
"question": {
|
||||
"en": "What is the website of {title()}?",
|
||||
"nl": "Wat is de website van {title()}?",
|
||||
|
@ -292,7 +312,8 @@
|
|||
{
|
||||
"if": "contact:website~*",
|
||||
"then": "<a href='{contact:website}' rel='nofollow noopener noreferrer' target='_blank'>{contact:website}</a>",
|
||||
"hideInAnswer": true
|
||||
"hideInAnswer": true,
|
||||
"icon": "./assets/layers/icons/website.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2203,4 +2224,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -610,6 +610,11 @@
|
|||
"feedback": "This is not a valid email address",
|
||||
"noAt": "An e-mail address must contain an @"
|
||||
},
|
||||
"fediverse": {
|
||||
"description": "A fediverse handle, often @username@server.tld",
|
||||
"feedback": "A fediverse handle consists of @username@server.tld or is a link to a profile",
|
||||
"invalidHost": "{host} is not a valid hostname"
|
||||
},
|
||||
"float": {
|
||||
"description": "a number",
|
||||
"feedback": "This is not a number"
|
||||
|
|
|
@ -41,6 +41,26 @@ export interface TagRenderingConfigJson {
|
|||
| Record<string, string>
|
||||
| { special: Record<string, string | Record<string, string>> & { type: string } }
|
||||
|
||||
/**
|
||||
* An icon shown next to the rendering; typically shown pretty small
|
||||
* This is only shown next to the "render" value
|
||||
* Type: icon
|
||||
*/
|
||||
icon?:
|
||||
| string
|
||||
| {
|
||||
/**
|
||||
* The path to the icon
|
||||
* Type: icon
|
||||
*/
|
||||
path: string
|
||||
/**
|
||||
* A hint to mapcomplete on how to render this icon within the mapping.
|
||||
* This is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)
|
||||
*/
|
||||
class?: "small" | "medium" | "large" | string
|
||||
}
|
||||
|
||||
/**
|
||||
* Only show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.
|
||||
*
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
import { Translation, TypedTranslation } from "../../UI/i18n/Translation"
|
||||
import { TagsFilter } from "../../Logic/Tags/TagsFilter"
|
||||
import {Translation, TypedTranslation} from "../../UI/i18n/Translation"
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import { TagUtils, UploadableTag } from "../../Logic/Tags/TagUtils"
|
||||
import { And } from "../../Logic/Tags/And"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import {TagUtils, UploadableTag} from "../../Logic/Tags/TagUtils"
|
||||
import {And} from "../../Logic/Tags/And"
|
||||
import {Utils} from "../../Utils"
|
||||
import {Tag} from "../../Logic/Tags/Tag"
|
||||
import BaseUIElement from "../../UI/BaseUIElement"
|
||||
import Combine from "../../UI/Base/Combine"
|
||||
import Title from "../../UI/Base/Title"
|
||||
import Link from "../../UI/Base/Link"
|
||||
import List from "../../UI/Base/List"
|
||||
import {
|
||||
MappingConfigJson,
|
||||
QuestionableTagRenderingConfigJson,
|
||||
} from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import { FixedUiElement } from "../../UI/Base/FixedUiElement"
|
||||
import { Paragraph } from "../../UI/Base/Paragraph"
|
||||
import {MappingConfigJson, QuestionableTagRenderingConfigJson,} from "./Json/QuestionableTagRenderingConfigJson"
|
||||
import {FixedUiElement} from "../../UI/Base/FixedUiElement"
|
||||
import {Paragraph} from "../../UI/Base/Paragraph"
|
||||
import Svg from "../../Svg"
|
||||
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
|
||||
import Validators, {ValidatorType} from "../../UI/InputElement/Validators"
|
||||
|
||||
export interface Icon {
|
||||
|
||||
}
|
||||
|
||||
export interface Mapping {
|
||||
readonly if: UploadableTag
|
||||
|
@ -45,6 +46,8 @@ export interface Mapping {
|
|||
export default class TagRenderingConfig {
|
||||
public readonly id: string
|
||||
public readonly render?: TypedTranslation<object>
|
||||
public readonly renderIcon?: string
|
||||
public readonly renderIconClass?: string
|
||||
public readonly question?: TypedTranslation<object>
|
||||
public readonly questionhint?: TypedTranslation<object>
|
||||
public readonly condition?: TagsFilter
|
||||
|
@ -121,9 +124,16 @@ export default class TagRenderingConfig {
|
|||
this.question = Translations.T(json.question, translationKey + ".question")
|
||||
this.questionhint = Translations.T(json.questionHint, translationKey + ".questionHint")
|
||||
this.description = Translations.T(json.description, translationKey + ".description")
|
||||
this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`)
|
||||
this.condition = TagUtils.Tag(json.condition ?? {and: []}, `${context}.condition`)
|
||||
if (typeof json.icon === "string") {
|
||||
this.renderIcon = json.icon
|
||||
this.renderIconClass = "small"
|
||||
}else if (typeof json.icon === "object"){
|
||||
this.renderIcon = json.icon.path
|
||||
this.renderIconClass = json.icon.class
|
||||
}
|
||||
this.metacondition = TagUtils.Tag(
|
||||
json.metacondition ?? { and: [] },
|
||||
json.metacondition ?? {and: []},
|
||||
`${context}.metacondition`
|
||||
)
|
||||
if (json.freeform) {
|
||||
|
@ -238,15 +248,17 @@ export default class TagRenderingConfig {
|
|||
if (txt.indexOf("{" + this.freeform.key + ":") >= 0) {
|
||||
continue
|
||||
}
|
||||
if (txt.indexOf("{canonical(" + this.freeform.key + ")") >= 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
this.freeform.type === "opening_hours" &&
|
||||
txt.indexOf("{opening_hours_table(") >= 0
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const keyFirstArg = ["canonical", "fediverse_link"]
|
||||
if (keyFirstArg.some(funcName => txt.indexOf(`{${funcName}(${this.freeform.key}`) >= 0)) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
this.freeform.type === "wikidata" &&
|
||||
txt.indexOf("{wikipedia(" + this.freeform.key) >= 0
|
||||
|
@ -532,7 +544,7 @@ export default class TagRenderingConfig {
|
|||
*/
|
||||
public GetRenderValueWithImage(
|
||||
tags: Record<string, string>
|
||||
): { then: TypedTranslation<any>; icon?: string } | undefined {
|
||||
): { then: TypedTranslation<any>; icon?: string, iconClass?: string } | undefined {
|
||||
if (this.condition !== undefined) {
|
||||
if (!this.condition.matchesProperties(tags)) {
|
||||
return undefined
|
||||
|
@ -551,7 +563,7 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
|
||||
if (this.freeform?.key === undefined || tags[this.freeform.key] !== undefined) {
|
||||
return { then: this.render }
|
||||
return {then: this.render, icon: this.renderIcon, iconClass: this.renderIconClass}
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
@ -773,7 +785,7 @@ export default class TagRenderingConfig {
|
|||
if (m.ifnot !== undefined) {
|
||||
msgs.push(
|
||||
"Unselecting this answer will add " +
|
||||
m.ifnot.asHumanString(true, false, {})
|
||||
m.ifnot.asHumanString(true, false, {})
|
||||
)
|
||||
}
|
||||
return msgs
|
||||
|
@ -803,12 +815,12 @@ export default class TagRenderingConfig {
|
|||
this.description,
|
||||
this.question !== undefined
|
||||
? new Combine([
|
||||
"The question is ",
|
||||
new FixedUiElement(this.question.txt).SetClass("font-bold bold"),
|
||||
])
|
||||
"The question is ",
|
||||
new FixedUiElement(this.question.txt).SetClass("font-bold bold"),
|
||||
])
|
||||
: new FixedUiElement(
|
||||
"This tagrendering has no question and is thus read-only"
|
||||
).SetClass("italic"),
|
||||
"This tagrendering has no question and is thus read-only"
|
||||
).SetClass("italic"),
|
||||
new Combine(withRender),
|
||||
mappings,
|
||||
condition,
|
||||
|
|
|
@ -18,6 +18,7 @@ import ColorValidator from "./Validators/ColorValidator"
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import Combine from "../Base/Combine"
|
||||
import Title from "../Base/Title"
|
||||
import FediverseValidator from "./Validators/FediverseValidator";
|
||||
|
||||
export type ValidatorType = (typeof Validators.availableTypes)[number]
|
||||
|
||||
|
@ -39,6 +40,7 @@ export default class Validators {
|
|||
"phone",
|
||||
"opening_hours",
|
||||
"color",
|
||||
"fediverse"
|
||||
] as const
|
||||
|
||||
public static readonly AllValidators: ReadonlyArray<Validator> = [
|
||||
|
@ -58,6 +60,7 @@ export default class Validators {
|
|||
new PhoneValidator(),
|
||||
new OpeningHoursValidator(),
|
||||
new ColorValidator(),
|
||||
new FediverseValidator()
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
63
src/UI/InputElement/Validators/FediverseValidator.ts
Normal file
63
src/UI/InputElement/Validators/FediverseValidator.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import {Validator} from "../Validator"
|
||||
import {Translation} from "../../i18n/Translation";
|
||||
import Translations from "../../i18n/Translations";
|
||||
|
||||
export default class FediverseValidator extends Validator {
|
||||
|
||||
public static readonly usernameAtServer: RegExp = /^@?(\w+)@((\w|\.)+)$/
|
||||
|
||||
constructor() {
|
||||
super("fediverse", "Validates fediverse addresses and normalizes them into `@username@server`-format");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an `@username@host`
|
||||
* @param s
|
||||
*/
|
||||
reformat(s: string): string {
|
||||
if(!s.startsWith("@")){
|
||||
s = "@"+s
|
||||
}
|
||||
if (s.match(FediverseValidator.usernameAtServer)) {
|
||||
return s
|
||||
}
|
||||
try {
|
||||
const url = new URL(s)
|
||||
const path = url.pathname
|
||||
if (path.match(/^\/\w+$/)) {
|
||||
return `@${path.substring(1)}@${url.hostname}`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Nothing to do here
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
getFeedback(s: string): Translation | undefined {
|
||||
const match = s.match(FediverseValidator.usernameAtServer)
|
||||
console.log("Match:", match)
|
||||
if (match) {
|
||||
const host = match[2]
|
||||
try {
|
||||
const url = new URL("https://" + host)
|
||||
return undefined
|
||||
} catch (e) {
|
||||
return Translations.t.validation.fediverse.invalidHost.Subs({host})
|
||||
}
|
||||
}
|
||||
try {
|
||||
const url = new URL(s)
|
||||
const path = url.pathname
|
||||
if (path.match(/^\/\w+$/)) {
|
||||
return undefined
|
||||
}
|
||||
} catch (e) {
|
||||
// Nothing to do here
|
||||
}
|
||||
return Translations.t.validation.fediverse.feedback
|
||||
}
|
||||
|
||||
isValid(s): boolean {
|
||||
return this.getFeedback(s) === undefined
|
||||
|
||||
}
|
||||
}
|
|
@ -1,56 +1,52 @@
|
|||
import Combine from "./Base/Combine"
|
||||
import { FixedUiElement } from "./Base/FixedUiElement"
|
||||
import {FixedUiElement} from "./Base/FixedUiElement"
|
||||
import BaseUIElement from "./BaseUIElement"
|
||||
import Title from "./Base/Title"
|
||||
import Table from "./Base/Table"
|
||||
import {
|
||||
RenderingSpecification,
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
} from "./SpecialVisualization"
|
||||
import { HistogramViz } from "./Popup/HistogramViz"
|
||||
import { MinimapViz } from "./Popup/MinimapViz"
|
||||
import { ShareLinkViz } from "./Popup/ShareLinkViz"
|
||||
import { UploadToOsmViz } from "./Popup/UploadToOsmViz"
|
||||
import { MultiApplyViz } from "./Popup/MultiApplyViz"
|
||||
import { AddNoteCommentViz } from "./Popup/AddNoteCommentViz"
|
||||
import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz"
|
||||
import {RenderingSpecification, SpecialVisualization, SpecialVisualizationState,} from "./SpecialVisualization"
|
||||
import {HistogramViz} from "./Popup/HistogramViz"
|
||||
import {MinimapViz} from "./Popup/MinimapViz"
|
||||
import {ShareLinkViz} from "./Popup/ShareLinkViz"
|
||||
import {UploadToOsmViz} from "./Popup/UploadToOsmViz"
|
||||
import {MultiApplyViz} from "./Popup/MultiApplyViz"
|
||||
import {AddNoteCommentViz} from "./Popup/AddNoteCommentViz"
|
||||
import {PlantNetDetectionViz} from "./Popup/PlantNetDetectionViz"
|
||||
import TagApplyButton from "./Popup/TagApplyButton"
|
||||
import { CloseNoteButton } from "./Popup/CloseNoteButton"
|
||||
import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis"
|
||||
import { Store, Stores, UIEventSource } from "../Logic/UIEventSource"
|
||||
import {CloseNoteButton} from "./Popup/CloseNoteButton"
|
||||
import {MapillaryLinkVis} from "./Popup/MapillaryLinkVis"
|
||||
import {Store, Stores, UIEventSource} from "../Logic/UIEventSource"
|
||||
import AllTagsPanel from "./Popup/AllTagsPanel.svelte"
|
||||
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders"
|
||||
import { ImageCarousel } from "./Image/ImageCarousel"
|
||||
import { ImageUploadFlow } from "./Image/ImageUploadFlow"
|
||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import { Utils } from "../Utils"
|
||||
import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata"
|
||||
import { Translation } from "./i18n/Translation"
|
||||
import {ImageCarousel} from "./Image/ImageCarousel"
|
||||
import {ImageUploadFlow} from "./Image/ImageUploadFlow"
|
||||
import {VariableUiElement} from "./Base/VariableUIElement"
|
||||
import {Utils} from "../Utils"
|
||||
import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata"
|
||||
import {Translation} from "./i18n/Translation"
|
||||
import Translations from "./i18n/Translations"
|
||||
import ReviewForm from "./Reviews/ReviewForm"
|
||||
import ReviewElement from "./Reviews/ReviewElement"
|
||||
import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization"
|
||||
import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"
|
||||
import { SubtleButton } from "./Base/SubtleButton"
|
||||
import {SubtleButton} from "./Base/SubtleButton"
|
||||
import Svg from "../Svg"
|
||||
import NoteCommentElement from "./Popup/NoteCommentElement"
|
||||
import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"
|
||||
import FileSelectorButton from "./Input/FileSelectorButton"
|
||||
import { LoginToggle } from "./Popup/LoginButton"
|
||||
import {LoginToggle} from "./Popup/LoginButton"
|
||||
import Toggle from "./Input/Toggle"
|
||||
import { SubstitutedTranslation } from "./SubstitutedTranslation"
|
||||
import {SubstitutedTranslation} from "./SubstitutedTranslation"
|
||||
import List from "./Base/List"
|
||||
import StatisticsPanel from "./BigComponents/StatisticsPanel"
|
||||
import AutoApplyButton from "./Popup/AutoApplyButton"
|
||||
import { LanguageElement } from "./Popup/LanguageElement"
|
||||
import {LanguageElement} from "./Popup/LanguageElement"
|
||||
import FeatureReviews from "../Logic/Web/MangroveReviews"
|
||||
import Maproulette from "../Logic/Maproulette"
|
||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||
import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||
import {BBoxFeatureSourceForLayer} from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource"
|
||||
import QuestionViz from "./Popup/QuestionViz"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { GeoOperations } from "../Logic/GeoOperations"
|
||||
import {Feature, Point} from "geojson"
|
||||
import {GeoOperations} from "../Logic/GeoOperations"
|
||||
import CreateNewNote from "./Popup/CreateNewNote.svelte"
|
||||
import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte"
|
||||
import UserProfile from "./BigComponents/UserProfile.svelte"
|
||||
|
@ -58,30 +54,27 @@ import LanguagePicker from "./LanguagePicker"
|
|||
import Link from "./Base/Link"
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
||||
import NearbyImages, {
|
||||
NearbyImageOptions,
|
||||
P4CPicture,
|
||||
SelectOneNearbyImage,
|
||||
} from "./Popup/NearbyImages"
|
||||
import { Tag } from "../Logic/Tags/Tag"
|
||||
import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage,} from "./Popup/NearbyImages"
|
||||
import {Tag} from "../Logic/Tags/Tag"
|
||||
import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { And } from "../Logic/Tags/And"
|
||||
import { SaveButton } from "./Popup/SaveButton"
|
||||
import {And} from "../Logic/Tags/And"
|
||||
import {SaveButton} from "./Popup/SaveButton"
|
||||
import Lazy from "./Base/Lazy"
|
||||
import { CheckBox } from "./Input/Checkboxes"
|
||||
import {CheckBox} from "./Input/Checkboxes"
|
||||
import Slider from "./Input/Slider"
|
||||
import { OsmTags, WayId } from "../Models/OsmFeature"
|
||||
import {OsmTags, WayId} from "../Models/OsmFeature"
|
||||
import MoveWizard from "./Popup/MoveWizard"
|
||||
import SplitRoadWizard from "./Popup/SplitRoadWizard"
|
||||
import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz"
|
||||
import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz"
|
||||
import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte"
|
||||
import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte"
|
||||
import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz"
|
||||
import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz"
|
||||
import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz"
|
||||
import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz"
|
||||
import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte"
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm"
|
||||
import {OpenJosm} from "./BigComponents/OpenJosm"
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
|
||||
import FediverseValidator from "./InputElement/Validators/FediverseValidator";
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -180,7 +173,7 @@ class NearbyImageVis implements SpecialVisualization {
|
|||
towardsCenter,
|
||||
new Combine([
|
||||
new VariableUiElement(
|
||||
radius.GetValue().map((radius) => t.withinRadius.Subs({ radius }))
|
||||
radius.GetValue().map((radius) => t.withinRadius.Subs({radius}))
|
||||
),
|
||||
radius,
|
||||
]).SetClass("flex justify-between"),
|
||||
|
@ -393,24 +386,24 @@ export default class SpecialVisualizations {
|
|||
viz.docs,
|
||||
viz.args.length > 0
|
||||
? new Table(
|
||||
["name", "default", "description"],
|
||||
viz.args.map((arg) => {
|
||||
let defaultArg = arg.defaultValue ?? "_undefined_"
|
||||
if (defaultArg == "") {
|
||||
defaultArg = "_empty string_"
|
||||
}
|
||||
return [arg.name, defaultArg, arg.doc]
|
||||
})
|
||||
)
|
||||
["name", "default", "description"],
|
||||
viz.args.map((arg) => {
|
||||
let defaultArg = arg.defaultValue ?? "_undefined_"
|
||||
if (defaultArg == "") {
|
||||
defaultArg = "_empty string_"
|
||||
}
|
||||
return [arg.name, defaultArg, arg.doc]
|
||||
})
|
||||
)
|
||||
: undefined,
|
||||
new Title("Example usage of " + viz.funcName, 4),
|
||||
new FixedUiElement(
|
||||
viz.example ??
|
||||
"`{" +
|
||||
viz.funcName +
|
||||
"(" +
|
||||
viz.args.map((arg) => arg.defaultValue).join(",") +
|
||||
")}`"
|
||||
"`{" +
|
||||
viz.funcName +
|
||||
"(" +
|
||||
viz.args.map((arg) => arg.defaultValue).join(",") +
|
||||
")}`"
|
||||
).SetClass("literal-code"),
|
||||
])
|
||||
}
|
||||
|
@ -469,14 +462,14 @@ export default class SpecialVisualizations {
|
|||
s.structuredExamples === undefined
|
||||
? []
|
||||
: s.structuredExamples().map((e) => {
|
||||
return s.constr(
|
||||
state,
|
||||
new UIEventSource<Record<string, string>>(e.feature.properties),
|
||||
e.args,
|
||||
e.feature,
|
||||
undefined
|
||||
)
|
||||
})
|
||||
return s.constr(
|
||||
state,
|
||||
new UIEventSource<Record<string, string>>(e.feature.properties),
|
||||
e.args,
|
||||
e.feature,
|
||||
undefined
|
||||
)
|
||||
})
|
||||
return new Combine([new Title(s.funcName), s.docs, ...examples])
|
||||
}
|
||||
|
||||
|
@ -491,7 +484,7 @@ export default class SpecialVisualizations {
|
|||
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
return new SvelteUIElement(AddNewPoint, {
|
||||
state,
|
||||
coordinate: { lon, lat },
|
||||
coordinate: {lon, lat},
|
||||
})
|
||||
},
|
||||
},
|
||||
|
@ -610,7 +603,7 @@ export default class SpecialVisualizations {
|
|||
feature: Feature
|
||||
): BaseUIElement {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } })
|
||||
return new SvelteUIElement(CreateNewNote, {state, coordinate: {lon, lat}})
|
||||
},
|
||||
},
|
||||
new CloseNoteButton(),
|
||||
|
@ -687,7 +680,7 @@ export default class SpecialVisualizations {
|
|||
docs: "Prints all key-value pairs of the object - used for debugging",
|
||||
args: [],
|
||||
constr: (state, tags: UIEventSource<any>) =>
|
||||
new SvelteUIElement(AllTagsPanel, { tags, state }),
|
||||
new SvelteUIElement(AllTagsPanel, {tags, state}),
|
||||
},
|
||||
{
|
||||
funcName: "image_carousel",
|
||||
|
@ -1326,7 +1319,7 @@ export default class SpecialVisualizations {
|
|||
],
|
||||
constr(state, featureTags, args) {
|
||||
const [key, tr] = args
|
||||
const translation = new Translation({ "*": tr })
|
||||
const translation = new Translation({"*": tr})
|
||||
return new VariableUiElement(
|
||||
featureTags.map((tags) => {
|
||||
const properties: object[] = JSON.parse(tags[key])
|
||||
|
@ -1344,12 +1337,32 @@ export default class SpecialVisualizations {
|
|||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
funcName: "fediverse_link",
|
||||
docs: "Converts a fediverse username or link into a clickable link",
|
||||
args: [{
|
||||
name: "key",
|
||||
doc: "The attribute-name containing the link",
|
||||
required: true
|
||||
}],
|
||||
constr(state: SpecialVisualizationState, tagSource: UIEventSource<Record<string, string>>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement {
|
||||
const key = argument[0]
|
||||
const validator = new FediverseValidator()
|
||||
return new VariableUiElement(tagSource.map(tags => tags[key]).map(fediAccount => {
|
||||
fediAccount = validator.reformat(fediAccount)
|
||||
const [_, username, host] = fediAccount.match(FediverseValidator.usernameAtServer)
|
||||
|
||||
return new Link(fediAccount, "https://" + host + "/@" + username, true)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
||||
|
||||
const invalid = specialVisualizations
|
||||
.map((sp, i) => ({ sp, i }))
|
||||
.map((sp, i) => ({sp, i}))
|
||||
.filter((sp) => sp.sp.funcName === undefined)
|
||||
if (invalid.length > 0) {
|
||||
throw (
|
||||
|
|
Loading…
Reference in a new issue