fix(webapp): fix map & user-teaser (#9484)
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 86 KiB |
5
packages/ui/src/ocelot/icons/svgs/map-pin-filled.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="33" viewBox="0 0 20 33">
|
||||||
|
<ellipse cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3"/>
|
||||||
|
<path d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
||||||
|
<path d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z" fill="#fff"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 692 B |
@ -140,7 +140,7 @@ hr {
|
|||||||
transition-delay: 0;
|
transition-delay: 0;
|
||||||
transition: opacity 80ms ease-out;
|
transition: opacity 80ms ease-out;
|
||||||
|
|
||||||
@media(hover: none) {
|
@media(pointer: coarse) {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,8 +48,10 @@ export default {
|
|||||||
if (typeof document === 'undefined') return
|
if (typeof document === 'undefined') return
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
document.body.classList.add('dropdown-open')
|
document.body.classList.add('dropdown-open')
|
||||||
|
this.addOverlayClickHandler()
|
||||||
} else {
|
} else {
|
||||||
document.body.classList.remove('dropdown-open')
|
document.body.classList.remove('dropdown-open')
|
||||||
|
this.removeOverlayClickHandler()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -57,6 +59,7 @@ export default {
|
|||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
clearTimeout(mouseEnterTimer)
|
clearTimeout(mouseEnterTimer)
|
||||||
clearTimeout(mouseLeaveTimer)
|
clearTimeout(mouseLeaveTimer)
|
||||||
|
this.removeOverlayClickHandler()
|
||||||
if (this.isPopoverOpen) {
|
if (this.isPopoverOpen) {
|
||||||
this.isPopoverOpen = false
|
this.isPopoverOpen = false
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
@ -116,6 +119,28 @@ export default {
|
|||||||
clearTimeout(mouseEnterTimer)
|
clearTimeout(mouseEnterTimer)
|
||||||
clearTimeout(mouseLeaveTimer)
|
clearTimeout(mouseLeaveTimer)
|
||||||
},
|
},
|
||||||
|
addOverlayClickHandler() {
|
||||||
|
this.overlayClickHandler = (e) => {
|
||||||
|
// Allow clicks inside the popover content (rendered under <body> by v-tooltip)
|
||||||
|
if (e.target.closest('.tooltip-inner, .popover-inner')) return
|
||||||
|
// Allow clicks on the trigger itself
|
||||||
|
if (this.$el.contains(e.target)) return
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
this.isPopoverOpen = false
|
||||||
|
}
|
||||||
|
// Capture phase fires before any other handler
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.isPopoverOpen && this.overlayClickHandler) {
|
||||||
|
document.addEventListener('click', this.overlayClickHandler, true)
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
},
|
||||||
|
removeOverlayClickHandler() {
|
||||||
|
if (!this.overlayClickHandler) return
|
||||||
|
document.removeEventListener('click', this.overlayClickHandler, true)
|
||||||
|
this.overlayClickHandler = null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -5,6 +5,12 @@ import Vuex from 'vuex'
|
|||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
window.matchMedia = jest.fn().mockImplementation(() => ({
|
||||||
|
matches: false,
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
// Mock Math.random, used in Dropdown
|
// Mock Math.random, used in Dropdown
|
||||||
Object.assign(Math, {
|
Object.assign(Math, {
|
||||||
random: () => 0,
|
random: () => 0,
|
||||||
@ -12,11 +18,6 @@ Object.assign(Math, {
|
|||||||
|
|
||||||
const waitForPopover = async () => await new Promise((resolve) => setTimeout(resolve, 1000))
|
const waitForPopover = async () => await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
let mockIsTouchDevice
|
|
||||||
jest.mock('../utils/isTouchDevice', () => ({
|
|
||||||
isTouchDevice: jest.fn(() => mockIsTouchDevice),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const userTilda = {
|
const userTilda = {
|
||||||
name: 'Tilda Swinton',
|
name: 'Tilda Swinton',
|
||||||
slug: 'tilda-swinton',
|
slug: 'tilda-swinton',
|
||||||
@ -65,7 +66,11 @@ describe('UserTeaser', () => {
|
|||||||
user = userTilda,
|
user = userTilda,
|
||||||
withPopoverEnabled = true,
|
withPopoverEnabled = true,
|
||||||
}) => {
|
}) => {
|
||||||
mockIsTouchDevice = onTouchScreen
|
window.matchMedia.mockImplementation(() => ({
|
||||||
|
matches: onTouchScreen,
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
getters: {
|
getters: {
|
||||||
|
|||||||
@ -2,6 +2,16 @@ import { render, waitFor, fireEvent } from '@testing-library/vue'
|
|||||||
import { RouterLinkStub } from '@vue/test-utils'
|
import { RouterLinkStub } from '@vue/test-utils'
|
||||||
import UserTeaserHelper from './UserTeaserHelper.vue'
|
import UserTeaserHelper from './UserTeaserHelper.vue'
|
||||||
|
|
||||||
|
let mockMatchMediaMatches = false
|
||||||
|
const mockMatchMediaListeners = []
|
||||||
|
window.matchMedia = jest.fn().mockImplementation(() => ({
|
||||||
|
get matches() {
|
||||||
|
return mockMatchMediaMatches
|
||||||
|
},
|
||||||
|
addEventListener: jest.fn((event, handler) => mockMatchMediaListeners.push(handler)),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
const userLink = {
|
const userLink = {
|
||||||
@ -9,12 +19,6 @@ const userLink = {
|
|||||||
params: { slug: 'slug', id: 'id' },
|
params: { slug: 'slug', id: 'id' },
|
||||||
}
|
}
|
||||||
|
|
||||||
let mockIsTouchDevice
|
|
||||||
|
|
||||||
jest.mock('../utils/isTouchDevice', () => ({
|
|
||||||
isTouchDevice: jest.fn(() => mockIsTouchDevice),
|
|
||||||
}))
|
|
||||||
|
|
||||||
describe('UserTeaserHelper', () => {
|
describe('UserTeaserHelper', () => {
|
||||||
const Wrapper = ({
|
const Wrapper = ({
|
||||||
withLinkToProfile = true,
|
withLinkToProfile = true,
|
||||||
@ -22,7 +26,7 @@ describe('UserTeaserHelper', () => {
|
|||||||
withPopoverEnabled = true,
|
withPopoverEnabled = true,
|
||||||
hoverDelay = 500,
|
hoverDelay = 500,
|
||||||
}) => {
|
}) => {
|
||||||
mockIsTouchDevice = onTouchScreen
|
mockMatchMediaMatches = onTouchScreen
|
||||||
|
|
||||||
return render(UserTeaserHelper, {
|
return render(UserTeaserHelper, {
|
||||||
localVue,
|
localVue,
|
||||||
|
|||||||
@ -20,10 +20,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isTouchDevice } from '../utils/isTouchDevice'
|
import touchDevice from '~/mixins/touchDevice'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserTeaserHelper',
|
name: 'UserTeaserHelper',
|
||||||
|
mixins: [touchDevice],
|
||||||
props: {
|
props: {
|
||||||
userLink: { type: Object, default: null },
|
userLink: { type: Object, default: null },
|
||||||
linkToProfile: { type: Boolean, default: true },
|
linkToProfile: { type: Boolean, default: true },
|
||||||
@ -36,11 +37,6 @@ export default {
|
|||||||
isHovering: false,
|
isHovering: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
isTouchDevice() {
|
|
||||||
return isTouchDevice()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
handleMouseEnter() {
|
handleMouseEnter() {
|
||||||
if (!this.showPopover) return
|
if (!this.showPopover) return
|
||||||
|
|||||||
@ -2,6 +2,15 @@ import { render } from '@testing-library/vue'
|
|||||||
import { RouterLinkStub } from '@vue/test-utils'
|
import { RouterLinkStub } from '@vue/test-utils'
|
||||||
import UserTeaserPopover from './UserTeaserPopover.vue'
|
import UserTeaserPopover from './UserTeaserPopover.vue'
|
||||||
|
|
||||||
|
const mockMatchMedia = (matches = false) => {
|
||||||
|
window.matchMedia = jest.fn().mockImplementation(() => ({
|
||||||
|
matches,
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
mockMatchMedia(false)
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
@ -51,10 +60,7 @@ describe('UserTeaserPopover', () => {
|
|||||||
onTouchScreen = false,
|
onTouchScreen = false,
|
||||||
userData = user,
|
userData = user,
|
||||||
}) => {
|
}) => {
|
||||||
const mockIsTouchDevice = onTouchScreen
|
mockMatchMedia(onTouchScreen)
|
||||||
jest.mock('../utils/isTouchDevice', () => ({
|
|
||||||
isTouchDevice: jest.fn(() => mockIsTouchDevice),
|
|
||||||
}))
|
|
||||||
return render(UserTeaserPopover, {
|
return render(UserTeaserPopover, {
|
||||||
localVue,
|
localVue,
|
||||||
propsData: {
|
propsData: {
|
||||||
|
|||||||
@ -44,11 +44,12 @@
|
|||||||
import { OsButton, OsNumber } from '@ocelot-social/ui'
|
import { OsButton, OsNumber } from '@ocelot-social/ui'
|
||||||
import Badges from '~/components/Badges.vue'
|
import Badges from '~/components/Badges.vue'
|
||||||
import LocationInfo from '~/components/LocationInfo/LocationInfo.vue'
|
import LocationInfo from '~/components/LocationInfo/LocationInfo.vue'
|
||||||
import { isTouchDevice } from '~/components/utils/isTouchDevice'
|
import touchDevice from '~/mixins/touchDevice'
|
||||||
import { userTeaserQuery } from '~/graphql/User.js'
|
import { userTeaserQuery } from '~/graphql/User.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserTeaserPopover',
|
name: 'UserTeaserPopover',
|
||||||
|
mixins: [touchDevice],
|
||||||
components: {
|
components: {
|
||||||
Badges,
|
Badges,
|
||||||
LocationInfo,
|
LocationInfo,
|
||||||
@ -60,9 +61,6 @@ export default {
|
|||||||
userLink: { type: Object },
|
userLink: { type: Object },
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isTouchDevice() {
|
|
||||||
return isTouchDevice()
|
|
||||||
},
|
|
||||||
user() {
|
user() {
|
||||||
return (this.User && this.User[0]) ?? null
|
return (this.User && this.User[0]) ?? null
|
||||||
},
|
},
|
||||||
|
|||||||
@ -412,7 +412,19 @@ exports[`UserTeaserPopover given a touch device shows button when userLink is pr
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!---->
|
<a
|
||||||
|
class="link os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] disabled:hover:bg-[var(--color-disabled)] disabled:hover:text-[var(--color-disabled-contrast)] disabled:hover:border-[var(--color-disabled)] disabled:active:bg-[var(--color-disabled)] disabled:active:text-[var(--color-disabled-contrast)] disabled:active:border-[var(--color-disabled)] h-[36px] min-w-[36px] px-[16px] py-0 text-[15px] leading-[normal] rounded-[5px] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] link"
|
||||||
|
data-appearance="filled"
|
||||||
|
data-variant="primary"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center"
|
||||||
|
>
|
||||||
|
|
||||||
|
user-teaser.popover.open-profile
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
23
webapp/mixins/touchDevice.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const pointerQuery =
|
||||||
|
typeof window !== 'undefined' ? window.matchMedia('(pointer: coarse)') : null
|
||||||
|
return {
|
||||||
|
isTouchDevice: pointerQuery ? pointerQuery.matches : false,
|
||||||
|
pointerQuery,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.pointerQuery) {
|
||||||
|
this.onPointerChange = (e) => {
|
||||||
|
this.isTouchDevice = e.matches
|
||||||
|
}
|
||||||
|
this.pointerQuery.addEventListener('change', this.onPointerChange)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.pointerQuery && this.onPointerChange) {
|
||||||
|
this.pointerQuery.removeEventListener('change', this.onPointerChange)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -6,7 +6,12 @@ import Map from './map'
|
|||||||
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder'
|
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder'
|
||||||
|
|
||||||
jest.mock('@mapbox/mapbox-gl-geocoder', () => {
|
jest.mock('@mapbox/mapbox-gl-geocoder', () => {
|
||||||
return jest.fn().mockImplementation(jest.fn())
|
return jest.fn().mockImplementation(() => {
|
||||||
|
const mockParent = global.document.createElement('div')
|
||||||
|
const container = global.document.createElement('div')
|
||||||
|
mockParent.appendChild(container)
|
||||||
|
return { container, clear: jest.fn() }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
jest.mock('mapbox-gl', () => {
|
jest.mock('mapbox-gl', () => {
|
||||||
@ -79,9 +84,9 @@ const mapMock = {
|
|||||||
getContainer: mapGetContainerMock,
|
getContainer: mapGetContainerMock,
|
||||||
queryRenderedFeatures: mapQueryRenderedFeaturesMock,
|
queryRenderedFeatures: mapQueryRenderedFeaturesMock,
|
||||||
getStyle: mapGetStyleMock,
|
getStyle: mapGetStyleMock,
|
||||||
getCanvas: jest.fn(() => ({
|
getCanvas: jest.fn().mockReturnValue({
|
||||||
style: { cursor: '' },
|
style: { cursor: '' },
|
||||||
})),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
const stubs = {
|
const stubs = {
|
||||||
@ -574,18 +579,6 @@ describe('map', () => {
|
|||||||
expect(links[1].getAttribute('href')).toBe('/groups/g1/journalism')
|
expect(links[1].getAttribute('href')).toBe('/groups/g1/journalism')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('clears pending leave timeout', () => {
|
|
||||||
jest.useFakeTimers()
|
|
||||||
wrapper.vm.popupOnLeaveTimeoutId = setTimeout(() => {}, 3000)
|
|
||||||
mapQueryRenderedFeaturesMock.mockReturnValueOnce(features)
|
|
||||||
onEventMocks.mouseenter({
|
|
||||||
point: { x: 100, y: 200 },
|
|
||||||
lngLat: { lng: 10.0, lat: 53.55 },
|
|
||||||
})
|
|
||||||
expect(wrapper.vm.popupOnLeaveTimeoutId).toBeNull()
|
|
||||||
jest.useRealTimers()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('removes existing popup before showing new one', () => {
|
it('removes existing popup before showing new one', () => {
|
||||||
mapboxgl.__popupInstance.isOpen.mockReturnValueOnce(true)
|
mapboxgl.__popupInstance.isOpen.mockReturnValueOnce(true)
|
||||||
mapQueryRenderedFeaturesMock.mockReturnValueOnce(features)
|
mapQueryRenderedFeaturesMock.mockReturnValueOnce(features)
|
||||||
@ -621,20 +614,10 @@ describe('map', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('mouseleave event', () => {
|
describe('mouseleave event', () => {
|
||||||
it('sets timeout to remove popup when open', () => {
|
it('resets cursor style', () => {
|
||||||
jest.useFakeTimers()
|
mapMock.getCanvas().style.cursor = 'pointer'
|
||||||
mapboxgl.__popupInstance.isOpen.mockReturnValueOnce(true)
|
|
||||||
onEventMocks.mouseleave()
|
onEventMocks.mouseleave()
|
||||||
expect(wrapper.vm.popupOnLeaveTimeoutId).toBeTruthy()
|
expect(mapMock.getCanvas().style.cursor).toBe('')
|
||||||
jest.advanceTimersByTime(3000)
|
|
||||||
expect(mapboxgl.__popupInstance.remove).toHaveBeenCalled()
|
|
||||||
jest.useRealTimers()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does nothing when popup is not open', () => {
|
|
||||||
mapboxgl.__popupInstance.isOpen.mockReturnValueOnce(false)
|
|
||||||
onEventMocks.mouseleave()
|
|
||||||
expect(wrapper.vm.popupOnLeaveTimeoutId).toBeFalsy()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1057,10 +1040,13 @@ describe('map', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('beforeDestroy', () => {
|
describe('beforeDestroy', () => {
|
||||||
it('removes resize listener', () => {
|
it('removes resize listeners', () => {
|
||||||
|
wrapper.vm.onMapLoad({ map: mapMock })
|
||||||
const spy = jest.spyOn(window, 'removeEventListener')
|
const spy = jest.spyOn(window, 'removeEventListener')
|
||||||
|
const geocoderHandler = wrapper.vm.geocoderCollapseHandler
|
||||||
wrapper.destroy()
|
wrapper.destroy()
|
||||||
expect(spy).toHaveBeenCalledWith('resize', wrapper.vm.updateMapPosition)
|
expect(spy).toHaveBeenCalledWith('resize', wrapper.vm.updateMapPosition)
|
||||||
|
expect(spy).toHaveBeenCalledWith('resize', geocoderHandler)
|
||||||
spy.mockRestore()
|
spy.mockRestore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -33,17 +33,19 @@
|
|||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
id="map-legend-content"
|
id="map-legend-content"
|
||||||
v-show="legendOpen || !isMobile"
|
:class="{ 'map-legend-content--hidden': isMobile && !legendOpen }"
|
||||||
class="map-legend-content"
|
class="map-legend-content"
|
||||||
role="region"
|
role="region"
|
||||||
:aria-label="$t('map.legend.title')"
|
:aria-label="$t('map.legend.title')"
|
||||||
>
|
>
|
||||||
<div v-for="type in markers.types" :key="type.id" class="map-legend-item">
|
<div v-for="type in markers.types" :key="type.id" class="map-legend-item">
|
||||||
<img
|
<span :style="{ color: type.color }">
|
||||||
:alt="$t('map.legend.' + type.id)"
|
<os-icon
|
||||||
:src="'/img/mapbox/marker-icons/' + type.icon.legendName"
|
:icon="icons.mapPinFilled"
|
||||||
width="15"
|
size="xl"
|
||||||
/>
|
:aria-label="$t('map.legend.' + type.id)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
{{ $t('map.legend.' + type.id) }}
|
{{ $t('map.legend.' + type.id) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,6 +63,8 @@ import mapboxgl from 'mapbox-gl'
|
|||||||
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder'
|
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder'
|
||||||
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css'
|
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
|
import { OsIcon } from '@ocelot-social/ui'
|
||||||
|
import { iconRegistry } from '~/utils/iconRegistry'
|
||||||
import { profileUserQuery } from '~/graphql/User'
|
import { profileUserQuery } from '~/graphql/User'
|
||||||
import { mapQuery } from '~/graphql/MapQuery'
|
import { mapQuery } from '~/graphql/MapQuery'
|
||||||
import mobile from '~/mixins/mobile'
|
import mobile from '~/mixins/mobile'
|
||||||
@ -73,6 +77,7 @@ export default {
|
|||||||
mixins: [mobile(maxMobileWidth)],
|
mixins: [mobile(maxMobileWidth)],
|
||||||
components: {
|
components: {
|
||||||
Empty,
|
Empty,
|
||||||
|
OsIcon,
|
||||||
},
|
},
|
||||||
head() {
|
head() {
|
||||||
return {
|
return {
|
||||||
@ -96,33 +101,33 @@ export default {
|
|||||||
types: [
|
types: [
|
||||||
{
|
{
|
||||||
id: 'theUser',
|
id: 'theUser',
|
||||||
|
color: '#f79640',
|
||||||
icon: {
|
icon: {
|
||||||
id: 'marker-orange',
|
id: 'marker-orange',
|
||||||
legendName: 'mapbox-marker-icon-orange.svg',
|
|
||||||
mapName: 'mapbox-marker-icon-20px-orange.png',
|
mapName: 'mapbox-marker-icon-20px-orange.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'user',
|
id: 'user',
|
||||||
|
color: '#33c377',
|
||||||
icon: {
|
icon: {
|
||||||
id: 'marker-green',
|
id: 'marker-green',
|
||||||
legendName: 'mapbox-marker-icon-green.svg',
|
|
||||||
mapName: 'mapbox-marker-icon-20px-green.png',
|
mapName: 'mapbox-marker-icon-20px-green.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'group',
|
id: 'group',
|
||||||
|
color: '#f84d4d',
|
||||||
icon: {
|
icon: {
|
||||||
id: 'marker-red',
|
id: 'marker-red',
|
||||||
legendName: 'mapbox-marker-icon-red.svg',
|
|
||||||
mapName: 'mapbox-marker-icon-20px-red.png',
|
mapName: 'mapbox-marker-icon-20px-red.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'event',
|
id: 'event',
|
||||||
|
color: '#7753eb',
|
||||||
icon: {
|
icon: {
|
||||||
id: 'marker-purple',
|
id: 'marker-purple',
|
||||||
legendName: 'mapbox-marker-icon-purple.svg',
|
|
||||||
mapName: 'mapbox-marker-icon-20px-purple.png',
|
mapName: 'mapbox-marker-icon-20px-purple.png',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -133,10 +138,12 @@ export default {
|
|||||||
isSourceAndLayerAdded: false,
|
isSourceAndLayerAdded: false,
|
||||||
isFlyToCenter: false,
|
isFlyToCenter: false,
|
||||||
popup: null,
|
popup: null,
|
||||||
popupOnLeaveTimeoutId: null,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.icons = iconRegistry
|
||||||
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.updateMapPosition()
|
this.updateMapPosition()
|
||||||
window.addEventListener('resize', this.updateMapPosition)
|
window.addEventListener('resize', this.updateMapPosition)
|
||||||
@ -149,6 +156,9 @@ export default {
|
|||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
window.removeEventListener('resize', this.updateMapPosition)
|
window.removeEventListener('resize', this.updateMapPosition)
|
||||||
|
if (this.geocoderCollapseHandler) {
|
||||||
|
window.removeEventListener('resize', this.geocoderCollapseHandler)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
@ -212,6 +222,21 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addGeocoder() {
|
||||||
|
this.geocoder = new MapboxGeocoder({
|
||||||
|
accessToken: this.$env.MAPBOX_TOKEN,
|
||||||
|
mapboxgl: this.mapboxgl,
|
||||||
|
marker: false,
|
||||||
|
collapsed: this.geocoderCollapsed,
|
||||||
|
})
|
||||||
|
this.map.addControl(this.geocoder, 'top-right')
|
||||||
|
// Ensure geocoder stays at the top of the control group
|
||||||
|
const container = this.geocoder.container
|
||||||
|
const parent = container.parentNode
|
||||||
|
if (parent && parent.firstChild !== container) {
|
||||||
|
parent.insertBefore(container, parent.firstChild)
|
||||||
|
}
|
||||||
|
},
|
||||||
updateMapPosition() {
|
updateMapPosition() {
|
||||||
const navbar = document.getElementById('navbar')
|
const navbar = document.getElementById('navbar')
|
||||||
const footer = document.getElementById('footer')
|
const footer = document.getElementById('footer')
|
||||||
@ -239,15 +264,17 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// add search field for locations
|
// add search field for locations
|
||||||
this.map.addControl(
|
this.geocoderCollapsed = window.innerWidth <= 810
|
||||||
new MapboxGeocoder({
|
this.addGeocoder()
|
||||||
accessToken: this.$env.MAPBOX_TOKEN,
|
this.geocoderCollapseHandler = () => {
|
||||||
mapboxgl: this.mapboxgl,
|
const shouldCollapse = window.innerWidth <= 810
|
||||||
marker: false,
|
if (shouldCollapse !== this.geocoderCollapsed) {
|
||||||
collapsed: window.innerWidth <= 810,
|
this.geocoderCollapsed = shouldCollapse
|
||||||
}),
|
this.map.removeControl(this.geocoder)
|
||||||
'top-right',
|
this.addGeocoder()
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', this.geocoderCollapseHandler)
|
||||||
|
|
||||||
// add style switcher control
|
// add style switcher control
|
||||||
let closePopoverHandler = null
|
let closePopoverHandler = null
|
||||||
@ -335,10 +362,6 @@ export default {
|
|||||||
|
|
||||||
// show popup for given features at coordinates
|
// show popup for given features at coordinates
|
||||||
const showPopup = (features, lngLat) => {
|
const showPopup = (features, lngLat) => {
|
||||||
if (this.popupOnLeaveTimeoutId) {
|
|
||||||
clearTimeout(this.popupOnLeaveTimeoutId)
|
|
||||||
this.popupOnLeaveTimeoutId = null
|
|
||||||
}
|
|
||||||
if (this.markers.popup.isOpen()) {
|
if (this.markers.popup.isOpen()) {
|
||||||
this.markers.popup.remove()
|
this.markers.popup.remove()
|
||||||
}
|
}
|
||||||
@ -441,12 +464,7 @@ export default {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.map.on('mouseleave', 'markers', () => {
|
this.map.on('mouseleave', 'markers', () => {
|
||||||
if (this.markers.popup.isOpen()) {
|
this.map.getCanvas().style.cursor = ''
|
||||||
this.popupOnLeaveTimeoutId = setTimeout(() => {
|
|
||||||
this.map.getCanvas().style.cursor = ''
|
|
||||||
this.markers.popup.remove()
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Mobile: show popup on click/tap
|
// Mobile: show popup on click/tap
|
||||||
@ -722,6 +740,8 @@ export default {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
font-family: $font-family-text;
|
||||||
|
font-size: $font-size-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mgl-map-wrapper {
|
.mgl-map-wrapper {
|
||||||
@ -733,10 +753,22 @@ export default {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapboxgl-ctrl-attrib.mapboxgl-compact {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapboxgl-ctrl-attrib-button {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-content {
|
.mapboxgl-popup-content {
|
||||||
max-height: 40vh;
|
max-height: 40vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
border-radius: $border-radius-x-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-popup-container {
|
.map-popup-container {
|
||||||
@ -747,14 +779,14 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-close-button {
|
.mapboxgl-popup-close-button {
|
||||||
font-size: 1.2rem;
|
font-size: $font-size-large;
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-popup-header {
|
.map-popup-header {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 1.1em;
|
font-size: $font-size-large;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@ -804,7 +836,7 @@ export default {
|
|||||||
height: 29px;
|
height: 29px;
|
||||||
min-width: 29px;
|
min-width: 29px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border-radius: 4px;
|
border-radius: $border-radius-x-large;
|
||||||
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
@ -832,9 +864,9 @@ export default {
|
|||||||
left: 10px;
|
left: 10px;
|
||||||
background: rgba(255, 255, 255, 0.75);
|
background: rgba(255, 255, 255, 0.75);
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
border-radius: 4px;
|
border-radius: $border-radius-x-large;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
font-size: 0.8rem;
|
font-size: $font-size-base;
|
||||||
color: $color-neutral-10;
|
color: $color-neutral-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,11 +877,13 @@ export default {
|
|||||||
border: none;
|
border: none;
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
color: $color-neutral-10;
|
color: $color-neutral-10;
|
||||||
border-radius: 4px;
|
border-radius: $border-radius-x-large;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.8rem;
|
font-size: $font-size-base;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
order: 1;
|
order: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
@ -858,8 +892,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.map-legend-arrow {
|
.map-legend-arrow {
|
||||||
float: right;
|
font-size: $font-size-small;
|
||||||
font-size: 0.7rem;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-legend-content {
|
.map-legend-content {
|
||||||
@ -879,22 +913,34 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.map-legend-toggle {
|
.map-legend-toggle {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-legend-content {
|
.map-legend-content {
|
||||||
order: 0;
|
order: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-legend-content--hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
height: 0;
|
||||||
|
padding: 0 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.map-legend--open .map-legend-content {
|
.map-legend--open .map-legend-content {
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-legend--open .map-legend-toggle {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-style-switcher {
|
.map-style-switcher {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 4px;
|
border-radius: $border-radius-x-large;
|
||||||
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,7 +968,7 @@ export default {
|
|||||||
right: 100%;
|
right: 100%;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 4px;
|
border-radius: $border-radius-x-large;
|
||||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -939,7 +985,7 @@ export default {
|
|||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.8rem;
|
font-size: $font-size-base;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#4264fb" stroke="#314ccd" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#314ccd" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#5b7897" stroke="#23374d" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#23374d" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#33c377" stroke="#269561" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#269561" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#f79640" stroke="#ba7334" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#ba7334" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#ee4e8b" stroke="#b43b71" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#b43b71" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#7753eb" stroke="#5a3fc0" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#5a3fc0" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#f84d4d" stroke="#951212" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#951212" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,16 +0,0 @@
|
|||||||
<!-- Create a custom map style: https://studio.mapbox.com -->
|
|
||||||
<svg id="marker" data-name="marker" xmlns="http://www.w3.org/2000/svg" width="20" height="48" viewBox="0 0 20 48">
|
|
||||||
<g id="mapbox-marker-icon">
|
|
||||||
<g id="icon">
|
|
||||||
<ellipse id="shadow" cx="10" cy="27" rx="9" ry="5" fill="#c4c4c4" opacity="0.3" style="isolation: isolate"/>
|
|
||||||
<g id="mask" opacity="0.3">
|
|
||||||
<g id="group">
|
|
||||||
<path id="shadow-2" data-name="shadow" fill="#bfbfbf" d="M10,32c5,0,9-2.2,9-5s-4-5-9-5-9,2.2-9,5S5,32,10,32Z" fill-rule="evenodd"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path id="color" fill="#d9d838" stroke="#a4a62d" stroke-width="0.5" d="M19.25,10.4a13.0663,13.0663,0,0,1-1.4607,5.2235,41.5281,41.5281,0,0,1-3.2459,5.5483c-1.1829,1.7369-2.3662,3.2784-3.2541,4.3859-.4438.5536-.8135.9984-1.0721,1.3046-.0844.1-.157.1852-.2164.2545-.06-.07-.1325-.1564-.2173-.2578-.2587-.3088-.6284-.7571-1.0723-1.3147-.8879-1.1154-2.0714-2.6664-3.2543-4.41a42.2677,42.2677,0,0,1-3.2463-5.5535A12.978,12.978,0,0,1,.75,10.4,9.4659,9.4659,0,0,1,10,.75,9.4659,9.4659,0,0,1,19.25,10.4Z"/>
|
|
||||||
<path id="circle" fill="#fff" stroke="#a4a62d" stroke-width="0.5" d="M13.55,10A3.55,3.55,0,1,1,10,6.45,3.5484,3.5484,0,0,1,13.55,10Z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect width="20" height="48" fill="none"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,3 +1,4 @@
|
|||||||
|
// Global mock for matchMedia (used by touchDevice mixin, drag detection, etc.)
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { createLocalVue } from '@vue/test-utils'
|
import { createLocalVue } from '@vue/test-utils'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
@ -6,6 +7,14 @@ import Filters from '~/plugins/vue-filters'
|
|||||||
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
||||||
import Directives from '~/plugins/vue-directives'
|
import Directives from '~/plugins/vue-directives'
|
||||||
import VueObserveVisibility from '~/plugins/vue-observe-visibility'
|
import VueObserveVisibility from '~/plugins/vue-observe-visibility'
|
||||||
|
|
||||||
|
window.matchMedia =
|
||||||
|
window.matchMedia ||
|
||||||
|
jest.fn().mockImplementation((query) => ({
|
||||||
|
matches: query === '(pointer: fine)' || query === '(min-width: 640px)',
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
}))
|
||||||
require('intersection-observer')
|
require('intersection-observer')
|
||||||
|
|
||||||
// Fail tests on Vue warnings
|
// Fail tests on Vue warnings
|
||||||
|
|||||||