Multi-line rendering for multi-value tags

E.g. opening hours, multiple phone numbers, ...
This commit is contained in:
2026-02-24 14:50:39 +04:00
parent 95e9c621a5
commit a6ca362876
2 changed files with 59 additions and 16 deletions

View File

@@ -1,6 +1,7 @@
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import { fn } from '@ember/helper'; import { fn } from '@ember/helper';
import { on } from '@ember/modifier'; import { on } from '@ember/modifier';
import { htmlSafe } from '@ember/template';
import { humanizeOsmTag } from '../utils/format-text'; import { humanizeOsmTag } from '../utils/format-text';
import { getLocalizedName, getPlaceType } from '../utils/osm'; import { getLocalizedName, getPlaceType } from '../utils/osm';
import Icon from '../components/icon'; import Icon from '../components/icon';
@@ -95,21 +96,55 @@ export default class PlaceDetails extends Component {
return parts.join(', '); return parts.join(', ');
} }
formatMultiLine(val, type) {
if (!val) return null;
const parts = val.split(';').map((s) => s.trim()).filter(Boolean);
if (parts.length === 0) return null;
if (type === 'phone') {
return htmlSafe(
parts.map((p) => `<a href="tel:${p}">${p}</a>`).join('<br>')
);
}
if (type === 'url') {
return htmlSafe(
parts
.map(
(url) =>
`<a href="${url}" target="_blank" rel="noopener noreferrer">${this.getDomain(
url
)}</a>`
)
.join('<br>')
);
}
return htmlSafe(parts.join('<br>'));
}
get phone() { get phone() {
return this.tags.phone || this.tags['contact:phone']; const val = this.tags.phone || this.tags['contact:phone'];
return this.formatMultiLine(val, 'phone');
} }
get website() { get website() {
return this.place.url || this.tags.website || this.tags['contact:website']; const val = this.place.url || this.tags.website || this.tags['contact:website'];
return this.formatMultiLine(val, 'url');
} }
get websiteDomain() { getDomain(urlStr) {
const url = new URL(this.website); try {
return url.hostname; const url = new URL(urlStr);
return url.hostname;
} catch {
return urlStr;
}
} }
get openingHours() { get openingHours() {
return this.tags.opening_hours; const val = this.tags.opening_hours;
return this.formatMultiLine(val);
} }
get cuisine() { get cuisine() {
@@ -121,7 +156,9 @@ export default class PlaceDetails extends Component {
} }
get wikipedia() { get wikipedia() {
return this.tags.wikipedia; const val = this.tags.wikipedia;
if (!val) return null;
return val.split(';').map((s) => s.trim()).filter(Boolean)[0];
} }
get geoLink() { get geoLink() {
@@ -215,7 +252,7 @@ export default class PlaceDetails extends Component {
<div class="meta-info"> <div class="meta-info">
{{#if this.cuisine}} {{#if this.cuisine}}
<p> <p class="cuisine-info">
<strong>Cuisine:</strong> <strong>Cuisine:</strong>
{{this.cuisine}} {{this.cuisine}}
</p> </p>
@@ -224,25 +261,27 @@ export default class PlaceDetails extends Component {
{{#if this.openingHours}} {{#if this.openingHours}}
<p class="content-with-icon"> <p class="content-with-icon">
<Icon @name="clock" @title="Opening hours" /> <Icon @name="clock" @title="Opening hours" />
<span>{{this.openingHours}}</span> <span>
{{this.openingHours}}
</span>
</p> </p>
{{/if}} {{/if}}
{{#if this.phone}} {{#if this.phone}}
<p class="content-with-icon"> <p class="content-with-icon">
<Icon @name="phone" @title="Phone" /> <Icon @name="phone" @title="Phone" />
<span><a href="tel:{{this.phone}}">{{this.phone}}</a></span> <span>
{{this.phone}}
</span>
</p> </p>
{{/if}} {{/if}}
{{#if this.website}} {{#if this.website}}
<p class="content-with-icon"> <p class="content-with-icon">
<Icon @name="globe" @title="Website" /> <Icon @name="globe" @title="Website" />
<span><a <span>
href={{this.website}} {{this.website}}
target="_blank" </span>
rel="noopener noreferrer"
>{{this.websiteDomain}}</a></span>
</p> </p>
{{/if}} {{/if}}

View File

@@ -617,10 +617,14 @@ span.icon {
.content-with-icon { .content-with-icon {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: flex-start;
gap: 0.5rem; gap: 0.5rem;
} }
.content-with-icon .icon {
margin-top: 0.15rem;
}
/* Selected Pin Animation */ /* Selected Pin Animation */
.selected-pin-container { .selected-pin-container {
position: absolute; position: absolute;