Ocelot-Social/webapp/components/Select/LocationSelect.vue

166 lines
4.1 KiB
Vue

<template>
<div>
<label class="ds-input-label">
{{ `${$t('settings.data.labelCity')}` + locationNameLabelAddOnOldName }}
</label>
<ds-select
id="city"
v-model="currentValue"
:options="cities"
icon="map-marker"
:icon-right="null"
:placeholder="$t('settings.data.labelCity') + ' …'"
:loading="loadingGeo"
@input.native="handleCityInput"
/>
<os-button
v-if="locationName !== '' && canBeCleared"
data-test="clear-location-button"
variant="primary"
appearance="ghost"
size="sm"
:aria-label="$t('actions.clear')"
style="right: -94%; top: -48px"
@click="clearLocationName"
>
<template #icon><os-icon :icon="icons.close" /></template>
</os-button>
</div>
</template>
<script>
import { OsButton, OsIcon } from '@ocelot-social/ui'
import { iconRegistry } from '~/utils/iconRegistry'
import { queryLocations } from '~/graphql/location'
let timeout
export default {
name: 'LocationSelect',
components: { OsButton, OsIcon },
props: {
value: {
type: [String, Object],
required: true,
},
canBeCleared: {
type: Boolean,
required: false,
default: true,
},
showPreviousLocation: {
type: Boolean,
required: false,
default: true,
},
},
async created() {
this.icons = iconRegistry
await this.resolveLocalizedLocation()
},
data() {
return {
currentValue: this.value,
loadingGeo: false,
cities: [],
}
},
computed: {
locationName() {
return typeof this.value === 'object' ? this.value.value : this.value
},
locationNameLabelAddOnOldName() {
return this.locationName !== '' && this.showPreviousLocation ? ' — ' + this.locationName : ''
},
currentLocale() {
return this.$store && this.$store.state.i18n && this.$store.state.i18n.locale
},
},
watch: {
currentValue() {
if (this.currentValue !== this.value) {
this.$emit('input', this.currentValue)
}
},
value(newVal, oldVal) {
if (newVal !== this.currentValue) {
this.currentValue = newVal
}
// resolve when value is set after initial mount (e.g. settings page)
const newName = typeof newVal === 'object' ? newVal.value : newVal
const oldName = typeof oldVal === 'object' ? oldVal.value : oldVal
if (newName && newName !== oldName) {
this.resolveLocalizedLocation()
}
},
currentLocale() {
this.resolveLocalizedLocation()
},
},
methods: {
handleCityInput(event) {
clearTimeout(timeout)
timeout = setTimeout(
() => this.requestGeoData(event.target ? event.target.value.trim() : ''),
500,
)
},
processLocationsResult(places) {
if (!places.length) {
return []
}
const result = []
places.forEach((place) => {
result.push({
label: place.place_name,
value: place.place_name,
id: place.id,
})
})
return result
},
async requestGeoData(value) {
if (value === '') {
this.cities = []
return
}
try {
this.loadingGeo = true
const place = encodeURIComponent(value)
const lang = this.$i18n.locale()
const {
data: { queryLocations: result },
} = await this.$apollo.query({
query: queryLocations(),
variables: { place, lang },
fetchPolicy: 'network-only',
})
this.cities = this.processLocationsResult(result)
this.loadingGeo = false
return this.cities.find((city) => city.value === value)
} catch (error) {
this.$toast.error(error.message)
} finally {
this.loadingGeo = false
}
},
async resolveLocalizedLocation() {
if (!this.locationName) return
const result = await this.requestGeoData(this.locationName)
this.$nextTick(() => {
this.currentValue = result || (this.cities.length ? this.cities[0] : this.locationName)
})
},
clearLocationName() {
this.currentValue = ''
},
},
}
</script>