From d4dab8c03ff12b71db32765d79324886c87bf41c Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Mon, 1 Jun 2026 21:03:34 +0200 Subject: [PATCH] feat!: rewrite frontend in Vue 3 with Pinia, DaisyUI, vite-plugin-pwa Migrates every component from class-based vue-property-decorator to + - - - diff --git a/src/base-components/BaseAccount.ts b/src/base-components/BaseAccount.ts deleted file mode 100644 index 42be93e..0000000 --- a/src/base-components/BaseAccount.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Component, Prop, Vue } from 'vue-property-decorator' -import { Getter } from 'vuex-class' -import bus, { SYNC } from '@/utils/bus-event' -import IAccount from '@/models/IAccount' -import IUser from '@/models/IUser' -import { findContrastColor } from '@/utils' - -@Component -export default class BaseAccount extends Vue { - @Getter public user!: IUser | null - @Prop({ type: String, required: true }) - public id!: string - public account: IAccount | null = null - public removing: boolean = false - - public async mounted(): Promise { - bus.$on(SYNC, this.getData) - await this.getData() - const anchor = this.$router.currentRoute.hash - if (anchor && document.querySelector(anchor)) { - this.$nextTick(() => { - location.href = anchor - // add scroll to manage fixed header - window.scrollBy(0, -60) - }) - } - } - - public colorStyle(display: boolean = true) { - if (!this.account || !display) { - return {} - } - return { - backgroundColor: this.account.color, - borderColor: this.account.color, - color: findContrastColor(this.account.color) - } - } - - public beforeDestroy(): void { - bus.$off(SYNC, this.getData) - } - - public async getData(docIds?: string[]): Promise { - throw new Error( - `abstract method needs to be overrided ${docIds && docIds.join(', ')}` - ) - } -} diff --git a/src/components/AccountCard.vue b/src/components/AccountCard.vue index b2b2c5f..1a42af5 100644 --- a/src/components/AccountCard.vue +++ b/src/components/AccountCard.vue @@ -1,112 +1,60 @@ - - - - + diff --git a/src/components/AccountList.vue b/src/components/AccountList.vue index 88ab218..78fc02b 100644 --- a/src/components/AccountList.vue +++ b/src/components/AccountList.vue @@ -1,76 +1,46 @@ - - - - + diff --git a/src/components/AccountSetting.vue b/src/components/AccountSetting.vue index a8afd28..b67d184 100644 --- a/src/components/AccountSetting.vue +++ b/src/components/AccountSetting.vue @@ -1,212 +1,157 @@ + + - - - - diff --git a/src/components/AccountShare.vue b/src/components/AccountShare.vue index 924e474..ec3a928 100644 --- a/src/components/AccountShare.vue +++ b/src/components/AccountShare.vue @@ -1,131 +1,105 @@ - - - - + diff --git a/src/components/AccountTransactionList.vue b/src/components/AccountTransactionList.vue index db930e6..d48dd71 100644 --- a/src/components/AccountTransactionList.vue +++ b/src/components/AccountTransactionList.vue @@ -1,79 +1,107 @@ + + - - - - diff --git a/src/components/AccountUserNew.vue b/src/components/AccountUserNew.vue index b826707..b99d61f 100644 --- a/src/components/AccountUserNew.vue +++ b/src/components/AccountUserNew.vue @@ -1,102 +1,80 @@ + + - - diff --git a/src/components/AppInstalled.vue b/src/components/AppInstalled.vue index 282b3c4..6b8cdff 100644 --- a/src/components/AppInstalled.vue +++ b/src/components/AppInstalled.vue @@ -1,21 +1,18 @@ + + - - diff --git a/src/components/AppResume.vue b/src/components/AppResume.vue index eb68346..ed0d125 100644 --- a/src/components/AppResume.vue +++ b/src/components/AppResume.vue @@ -1,13 +1,17 @@ + + - - - - - diff --git a/src/components/AxisLabel.vue b/src/components/AxisLabel.vue index d6398db..b320260 100644 --- a/src/components/AxisLabel.vue +++ b/src/components/AxisLabel.vue @@ -1,29 +1,19 @@ + + - - - diff --git a/src/components/ChartBalance.vue b/src/components/ChartBalance.vue index ae4a4e7..bc38c91 100644 --- a/src/components/ChartBalance.vue +++ b/src/components/ChartBalance.vue @@ -1,29 +1,61 @@ + + - - - - diff --git a/src/components/ChartPie.vue b/src/components/ChartPie.vue index db4953c..69e22c2 100644 --- a/src/components/ChartPie.vue +++ b/src/components/ChartPie.vue @@ -1,85 +1,59 @@ - + + + diff --git a/src/components/Charts/ChartProgress.vue b/src/components/Charts/ChartProgress.vue index d3bd87f..f0634d2 100644 --- a/src/components/Charts/ChartProgress.vue +++ b/src/components/Charts/ChartProgress.vue @@ -1,36 +1,22 @@ + + - - - diff --git a/src/components/ColorPicker.vue b/src/components/ColorPicker.vue index dcce19c..deb6c40 100644 --- a/src/components/ColorPicker.vue +++ b/src/components/ColorPicker.vue @@ -1,109 +1,80 @@ + + - - - diff --git a/src/components/ConfirmButton.vue b/src/components/ConfirmButton.vue index b228789..aa34548 100644 --- a/src/components/ConfirmButton.vue +++ b/src/components/ConfirmButton.vue @@ -1,51 +1,46 @@ + + - - - diff --git a/src/components/CurrencyInput.vue b/src/components/CurrencyInput.vue new file mode 100644 index 0000000..429cf24 --- /dev/null +++ b/src/components/CurrencyInput.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/components/EarthMap.vue b/src/components/EarthMap.vue index 8eb4afa..48f7c6f 100644 --- a/src/components/EarthMap.vue +++ b/src/components/EarthMap.vue @@ -1,119 +1,92 @@ - - - - \ No newline at end of file + diff --git a/src/components/FabButton.vue b/src/components/FabButton.vue index 60f7f6f..902f3f3 100644 --- a/src/components/FabButton.vue +++ b/src/components/FabButton.vue @@ -1,133 +1,112 @@ + + - - - diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index 083bdba..0000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/src/components/LangChanger.vue b/src/components/LangChanger.vue index c1fe592..b6ddb43 100644 --- a/src/components/LangChanger.vue +++ b/src/components/LangChanger.vue @@ -1,72 +1,47 @@ + + - - diff --git a/src/components/OnlineView.vue b/src/components/OnlineView.vue index 2c9fdec..564cd82 100644 --- a/src/components/OnlineView.vue +++ b/src/components/OnlineView.vue @@ -1,31 +1,30 @@ + + - - diff --git a/src/components/Payment.vue b/src/components/Payment.vue deleted file mode 100644 index a271b8a..0000000 --- a/src/components/Payment.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - diff --git a/src/components/PricingTable.vue b/src/components/PricingTable.vue deleted file mode 100644 index 210f232..0000000 --- a/src/components/PricingTable.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/QueueNotif.vue b/src/components/QueueNotif.vue index e09658a..f2f05ea 100644 --- a/src/components/QueueNotif.vue +++ b/src/components/QueueNotif.vue @@ -1,40 +1,39 @@ - - - + + diff --git a/src/components/RefundTransaction.vue b/src/components/RefundTransaction.vue index 54a9caa..49495c1 100644 --- a/src/components/RefundTransaction.vue +++ b/src/components/RefundTransaction.vue @@ -1,165 +1,95 @@ - - - - + diff --git a/src/components/TagList.vue b/src/components/TagList.vue index cc10cc8..b234aeb 100644 --- a/src/components/TagList.vue +++ b/src/components/TagList.vue @@ -1,210 +1,131 @@ + + - - - - diff --git a/src/components/TransactionCreate.vue b/src/components/TransactionCreate.vue index fb01714..fc249a3 100644 --- a/src/components/TransactionCreate.vue +++ b/src/components/TransactionCreate.vue @@ -1,6 +1,194 @@ + + - - - - diff --git a/src/components/TransactionSplit.vue b/src/components/TransactionSplit.vue index 5b9a9d2..48eece5 100644 --- a/src/components/TransactionSplit.vue +++ b/src/components/TransactionSplit.vue @@ -1,81 +1,64 @@ + + - - - - diff --git a/src/components/TransactionTagUpdate.vue b/src/components/TransactionTagUpdate.vue index 04debf8..0f607d8 100644 --- a/src/components/TransactionTagUpdate.vue +++ b/src/components/TransactionTagUpdate.vue @@ -1,46 +1,47 @@ + + - - diff --git a/src/components/UserNew.vue b/src/components/UserNew.vue index 533e96f..b0de0f1 100644 --- a/src/components/UserNew.vue +++ b/src/components/UserNew.vue @@ -1,95 +1,75 @@ - + - + diff --git a/src/components/VaquantIcon.vue b/src/components/VaquantIcon.vue new file mode 100644 index 0000000..821f49b --- /dev/null +++ b/src/components/VaquantIcon.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/composables/useBaseAccount.ts b/src/composables/useBaseAccount.ts new file mode 100644 index 0000000..21176e7 --- /dev/null +++ b/src/composables/useBaseAccount.ts @@ -0,0 +1,37 @@ +import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue' +import { useRoute } from 'vue-router' +import bus, { SYNC } from '@/utils/bus-event' +import type IAccount from '@/models/IAccount' +import { findContrastColor } from '@/utils' + +export const useBaseAccount = (getData: (docIds?: string[]) => Promise) => { + const account = ref(null) + const removing = ref(false) + const route = useRoute() + + const colorStyle = (display = true): Record => { + if (!account.value || !display) return {} + return { + backgroundColor: account.value.color ?? '', + borderColor: account.value.color ?? '', + color: findContrastColor(account.value.color ?? null) ?? '' + } + } + + onMounted(async () => { + bus.on(SYNC, getData) + await getData() + const anchor = route.hash + if (anchor && document.querySelector(anchor)) { + await nextTick() + location.href = anchor + window.scrollBy(0, -60) + } + }) + + onBeforeUnmount(() => { + bus.off(SYNC, getData) + }) + + return { account, removing, colorStyle } +} diff --git a/src/data/colors.ts b/src/data/colors.ts index b44f796..a0845cb 100644 --- a/src/data/colors.ts +++ b/src/data/colors.ts @@ -72,16 +72,17 @@ export default [ } ].map( (color: IColor): IColor => { - const darkValue = lightness(color.value, dark).toLowerCase() - const lightValue = lightness(color.value, light).toLowerCase() - const slightLightValue = lightness(color.value, 10).toLowerCase() + const value = color.value ?? '' + const darkValue = lightness(value, dark).toLowerCase() + const lightValue = lightness(value, light).toLowerCase() + const slightLightValue = lightness(value, 10).toLowerCase() return { ...color, darkValue, lightValue, slightLightValue, constrastColor: - fontColorContrast(color.value) === '#000000' ? darkValue : lightValue + fontColorContrast(value) === '#000000' ? darkValue : lightValue } } ) diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..dfadfd4 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,11 @@ +import { createI18n } from 'vue-i18n' +import messages from '@/messages' + +const i18n = createI18n({ + legacy: false, + locale: 'fr', + fallbackLocale: 'en', + messages +}) + +export default i18n diff --git a/src/main.ts b/src/main.ts index 93d6536..502a851 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,37 +1,30 @@ -import Vue from 'vue' -import VueI18n from 'vue-i18n' +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import App from './App.vue' import router from './router' -import store from './store' -import './styles/index.scss' -import 'bulma-checkradio' -import './registerServiceWorker' -import filters from './utils/filters' -import './utils/icons' -import messages from './messages' +import i18n from './i18n' +import { useUserStore } from './stores/user' +import { registerIcons } from './utils/icons' +import couchService from './services/CouchService' +import './styles/index.css' -Vue.use(VueI18n) +const bootstrap = async () => { + const app = createApp(App) -Vue.config.productionTip = false + const pinia = createPinia() + pinia.use(piniaPluginPersistedstate) + app.use(pinia) -for (const filter of Object.keys(filters)) { - Vue.filter(filter, filters[filter]) + app.use(router) + app.use(i18n) + registerIcons(app) + + const userStore = useUserStore() + couchService.start(() => userStore.publicAccountIds) + await userStore.retrieveUser() + + app.mount('#app') } -const i18n = new VueI18n({ - locale: 'fr', - fallbackLocale: 'en', - messages -}) - -const app = async () => { - await store.dispatch('retrieveUser') - new Vue({ - router, - store, - i18n, - render: (h) => h(App) - }).$mount('#app') -} - -app() +bootstrap() diff --git a/src/registerServiceWorker.ts b/src/registerServiceWorker.ts deleted file mode 100644 index 176da00..0000000 --- a/src/registerServiceWorker.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* tslint:disable:no-console */ - -import { register } from 'register-service-worker' -import queueNotifService from '@/services/QueueNotifService' - -if (process.env.NODE_ENV === 'production') { - register(`${process.env.BASE_URL}service-worker.js`, { - registrationOptions: {}, - ready() { - console.log('App is being served from cache by a service worker.') - }, - cached() { - console.log('Content has been cached for offline use.') - queueNotifService.success('Application mise à jour !') - }, - updated() { - console.log('New content is available; please refresh.') - queueNotifService.hasNewVersion() - }, - offline() { - console.log( - 'No internet connection found. App is running in offline mode.' - ) - }, - error(error) { - console.error('Error during service worker registration:', error) - } - }) -} diff --git a/src/router.ts b/src/router.ts deleted file mode 100644 index 9088b6d..0000000 --- a/src/router.ts +++ /dev/null @@ -1,112 +0,0 @@ -import Vue from 'vue' -import Router from 'vue-router' -import Home from './views/Home.vue' -import store from './store' - -Vue.use(Router) - -const router: Router = new Router({ - mode: 'history', - routes: [ - { - path: '/', - name: 'home', - component: Home, - }, - { - path: '/about', - name: 'about', - component: () => - import(/* webpackChunkName: "about" */ './views/About.vue'), - }, - { - path: '/pricing', - name: 'pricing', - component: () => - import(/* webpackChunkName: "pricing" */ './views/Pricing.vue'), - }, - { - path: '/security', - name: 'security', - component: () => - import(/* webpackChunkName: "pricing" */ './views/Security.vue'), - }, - { - path: '/account/new', - name: 'account-new', - component: () => - import( - /* webpackChunkName: "account-new" */ './views/accounts/AccountNew.vue' - ), - }, - { - path: '/account/:id', - name: 'account', - props: true, - component: () => - import( - /* webpackChunkName: "account" */ './views/accounts/AccountItem.vue' - ), - }, - { - path: '/account/:id/public', - name: 'account-public', - props: true, - component: () => - import( - /* webpackChunkName: "account-public" */ './views/accounts/AccountPublic.vue' - ), - }, - { - path: '/account/:id/transaction/new', - name: 'transaction-new', - props: true, - component: () => - import( - /* webpackChunkName: "transaction-new" */ './views/transactions/TransactionNew.vue' - ), - }, - { - path: '/transaction/:id/update', - name: 'transaction-update', - props: true, - component: () => - import( - /* webpackChunkName: "transaction-update" */ './views/transactions/TransactionUpdate.vue' - ), - }, - { - path: '/transaction/:id', - name: 'transaction', - props: true, - component: () => - import( - /* webpackChunkName: "transaction" */ './views/transactions/TransactionItem.vue' - ), - }, - { - path: '/user', - name: 'user', - component: () => - import(/* webpackChunkName: "user" */ './views/User.vue'), - }, - { - path: '/user/:premail', - name: 'user-premail', - props: true, - component: () => - import(/* webpackChunkName: "user" */ './views/User.vue'), - }, - ], -}) - -// tslint:disable-next-line:variable-name -router.beforeEach((to, _from, next) => { - const accountRoutes: string[] = ['account', 'transaction'] - if (!accountRoutes.includes(to.name || '')) { - store.dispatch('setTitle', null) - } - next() -}) - -export default router diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..ca93e64 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,75 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Home from '@/views/Home.vue' +import { useUserStore } from '@/stores/user' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', name: 'home', component: Home }, + { + path: '/about', + name: 'about', + component: () => import('@/views/About.vue') + }, + { + path: '/security', + name: 'security', + component: () => import('@/views/Security.vue') + }, + { + path: '/account/new', + name: 'account-new', + component: () => import('@/views/accounts/AccountNew.vue') + }, + { + path: '/account/:id', + name: 'account', + props: true, + component: () => import('@/views/accounts/AccountItem.vue') + }, + { + path: '/account/:id/public', + name: 'account-public', + props: true, + component: () => import('@/views/accounts/AccountPublic.vue') + }, + { + path: '/account/:id/transaction/new', + name: 'transaction-new', + props: true, + component: () => import('@/views/transactions/TransactionNew.vue') + }, + { + path: '/transaction/:id/update', + name: 'transaction-update', + props: true, + component: () => import('@/views/transactions/TransactionUpdate.vue') + }, + { + path: '/transaction/:id', + name: 'transaction', + props: true, + component: () => import('@/views/transactions/TransactionItem.vue') + }, + { + path: '/user', + name: 'user', + component: () => import('@/views/User.vue') + }, + { + path: '/user/:premail', + name: 'user-premail', + props: true, + component: () => import('@/views/User.vue') + } + ] +}) + +router.beforeEach((to) => { + const accountRoutes = ['account', 'transaction'] + if (!accountRoutes.includes(String(to.name || ''))) { + useUserStore().setTitle(null) + } +}) + +export default router diff --git a/src/scripts/validate-doc-update.ts b/src/scripts/validate-doc-update.ts deleted file mode 100644 index d8b6fb0..0000000 --- a/src/scripts/validate-doc-update.ts +++ /dev/null @@ -1,31 +0,0 @@ -import IAccount from '@/models/IAccount' -import ITransaction from '@/models/ITransaction' -import IExchange from '@/models/IExchange' - -type Doc = IAccount | ITransaction | IExchange - -interface IUserContext { - db: string - name: string | null - roles: string[] -} - -// tslint:disable-next-line: variable-name only-arrow-functions -const validate_doc_update = function( - newDoc: Doc, - oldDoc: Doc, - userCtx: IUserContext -) { - if (!userCtx.name) { - throw { forbidden: 'You need to be logged in to update accounts' } - } - - if (newDoc.doctype === 'account' && oldDoc.doctype === 'account') { - if (true) { - throw { forbidden: 'You do not have rights to update this account' } - } - } -} - -// tslint:disable-next-line:no-console -console.info(validate_doc_update.toString().replace(/\n/g, '')) diff --git a/src/services/AccountService.ts b/src/services/AccountService.ts index 416e976..777fe84 100644 --- a/src/services/AccountService.ts +++ b/src/services/AccountService.ts @@ -6,7 +6,7 @@ import { slug } from '@/utils' class AccountService { public async get(id: string): Promise { - const account: IAccount = await couchService.get(id) + const account = await couchService.get(id) if (!account) { return null } @@ -14,7 +14,7 @@ class AccountService { } public async getRemote(id: string): Promise { - const account: IAccount = await couchService.getRemote(id) + const account = await couchService.getRemote(id) if (!account) { return null } @@ -43,11 +43,12 @@ class AccountService { public async active(id: string): Promise { try { const account = await this.get(id) - if (account) { - account.archive = false + if (!account) { + return false } + account.archive = false const { ok } = await couchService.save(account) - return ok + return ok ?? false } catch (error) { // tslint:disable-next-line:no-console console.info('erreur dans la cloture du compte', { @@ -60,11 +61,12 @@ class AccountService { public async archive(id: string): Promise { try { const account = await this.get(id) - if (account) { - account.archive = true + if (!account) { + return false } + account.archive = true const { ok } = await couchService.save(account) - return ok + return ok ?? false } catch (error) { // tslint:disable-next-line:no-console console.info('erreur dans la cloture du compte', { @@ -96,7 +98,7 @@ class AccountService { try { const response = await couchService.getByPrefix('acc') const accounts = response.rows.map((row: any) => row.doc) as IAccount[] - const result = [] + const result: IAccount[] = [] for (const account of accounts) { if ((archived && account.archive) || (!archived && !account.archive)) { result.push(account) diff --git a/src/services/ChartService.ts b/src/services/ChartService.ts index 3b4037e..2bbb67c 100644 --- a/src/services/ChartService.ts +++ b/src/services/ChartService.ts @@ -1,5 +1,5 @@ import IPoint from '@/models/IPoint' -import { money } from '@/utils/filters' +import { money } from '@/utils/format' import ICurrency from '@/models/ICurrency' class ChartService { diff --git a/src/services/CouchService.ts b/src/services/CouchService.ts index d470d96..3e0183d 100644 --- a/src/services/CouchService.ts +++ b/src/services/CouchService.ts @@ -1,16 +1,15 @@ import { v4 as uuid } from 'uuid' import PouchDb from 'pouchdb-browser' import PouchDbAuthentication from 'pouchdb-authentication' -import bus, { SYNC } from '@/utils/bus-event' -import IUser from '@/models/IUser' import { debounce } from 'lodash-es' +import bus, { SYNC } from '@/utils/bus-event' +import type IUser from '@/models/IUser' import { confirmation } from '@/utils' -import store from '@/store' import queueNotifService from '@/services/QueueNotifService' PouchDb.plugin(PouchDbAuthentication) -const emit = debounce((ev: string, arr?: string[]) => bus.$emit(ev, arr), 150) +const emit = debounce((ev: typeof SYNC, arr?: string[]) => bus.emit(ev, arr), 150) const remoteConfig = { skip_setup: true, @@ -19,26 +18,33 @@ const remoteConfig = { withCredentials: true } } -const LOCALE_DB: string = 'VAQUANT_LOCALE_DB' -const REMOTE: string = process.env.VUE_APP_COUCHDB || '' +const LOCALE_DB = 'VAQUANT_LOCALE_DB' +const REMOTE: string = import.meta.env.VITE_COUCHDB || '' + +type PublicAccountIdsProvider = () => string[] class CouchService { public db: PouchDB.Database | null = null private innerRemote: PouchDB.Database | null = null private user: IUser | null = null - private sync: PouchDB.Replication.Sync<{}> | null = null - private userSync: PouchDB.Replication.Sync<{}> | null = null - private eventListened: boolean = false + private sync: PouchDB.Replication.Sync | null = null + private userSync: PouchDB.Replication.Sync | null = null + private eventListened = false + private publicAccountIds: PublicAccountIdsProvider = () => [] public get remote(): PouchDB.Database { if (!this.innerRemote) { this.innerRemote = new PouchDb(`${REMOTE}/vaquant`, remoteConfig) } - return this.innerRemote + return this.innerRemote as PouchDB.Database } constructor() { this.initDb() + } + + public start(publicAccountIds: PublicAccountIdsProvider): void { + this.publicAccountIds = publicAccountIds if (!this.eventListened) { this.eventListened = true window.addEventListener('online', () => { @@ -50,7 +56,6 @@ class CouchService { this.cancelSync() }) } - if (navigator.onLine) { this.initLive() } @@ -60,7 +65,7 @@ class CouchService { if (!this.db) { return false } - await this.cancelSync() + this.cancelSync() this.sync = this.db .sync(this.remote, { @@ -69,13 +74,13 @@ class CouchService { filter: 'account/by_user', query_params: { userId: (this.user && this.user.userId) || '', - accountIds: [...store.getters.publicAccountIds] + accountIds: [...this.publicAccountIds()] } }) - .on('change', (result: PouchDB.Replication.SyncResult) => { + .on('change', (result: PouchDB.Replication.SyncResult<{ doctype?: string }>) => { if (result.direction === 'pull') { const hasDocOtherThanExchange = result.change.docs.some( - (doc: any) => doc.doctype !== 'exchange' + (doc) => (doc as { doctype?: string }).doctype !== 'exchange' ) if (hasDocOtherThanExchange) { emit(SYNC) @@ -85,10 +90,9 @@ class CouchService { emit(SYNC) } }) - .on('error', (error: any) => { - // tslint:disable-next-line:no-console + .on('error', (error: unknown) => { console.warn('on error', { error }) - if (error.name !== 'unauthorized') { + if ((error as { name?: string })?.name !== 'unauthorized') { queueNotifService.error(`une erreur s'est produite`) } }) @@ -131,21 +135,25 @@ class CouchService { return navigator.onLine } - public async get(id: string): Promise { + public async get>( + id: string + ): Promise { if (!this.db) { return null } - return await this.db.get(id) + return (await this.db.get(id)) as T } - public async getRemote(id: string): Promise { + public async getRemote>( + id: string + ): Promise { if (!this.remote) { return null } - return await this.remote.get(id) + return (await this.remote.get(id)) as T } - public async getByPrefix( + public async getByPrefix( prefix: string ): Promise> { if (!this.db) { @@ -158,40 +166,34 @@ class CouchService { return (await this.db.allDocs({ include_docs: true, startkey: prefix, - endkey: `${prefix}\ufff0` + endkey: `${prefix}￰` })) as PouchDB.Core.AllDocsResponse } - public async query(query: string): Promise { + public async query(query: string): Promise { if (!this.db) { return [] } const response = await this.db.query(query, { include_docs: true }) - return response.rows.map((row: any) => row.doc) + return response.rows.map((row) => row.doc) } - public async save(doc: any): Promise { + public async save(doc: object): Promise { if (!this.db) { - return { - id: '', - rev: '', - ok: false - } + return { id: '', rev: '', ok: false } } - const response = await this.db.put(doc) - return response + return await this.db.put(doc as PouchDB.Core.PutDocument) } public async multipleSave( - docs: any[] + docs: object[] ): Promise> { if (!this.db) { return [] } - const response = await this.db.bulkDocs(docs) - return response + return await this.db.bulkDocs(docs as PouchDB.Core.PutDocument[]) } public async remove(id: string): Promise { @@ -200,17 +202,13 @@ class CouchService { } const doc = await this.db.get(id) if (doc) { - const response = await this.save({ - ...doc, - _deleted: true - }) - - return response.ok + const response = await this.save({ ...doc, _deleted: true }) + return response.ok ?? false } return false } - public newId(prefix: string, withUuid: boolean = false): string { + public newId(prefix: string, withUuid = false): string { return `${prefix}${Date.now().toString()}${withUuid ? uuid() : ''}` } @@ -220,12 +218,11 @@ class CouchService { await this.db.destroy() this.db = null } - this.setUser(this.user, false) } } - private cancelSync(): any { + private cancelSync(): void { if (this.sync) { this.sync.cancel() this.sync = null diff --git a/src/services/EmailService.ts b/src/services/EmailService.ts index 51f07f9..5e5a021 100644 --- a/src/services/EmailService.ts +++ b/src/services/EmailService.ts @@ -1,5 +1,5 @@ -declare const Email: any -import '@/utils/email' +declare const Email: { send: (...args: unknown[]) => unknown } +import '@/utils/smtp' import IUser from '@/models/IUser' class EmailService { diff --git a/src/services/MapService.ts b/src/services/MapService.ts index c3ca00a..80980b8 100644 --- a/src/services/MapService.ts +++ b/src/services/MapService.ts @@ -2,7 +2,7 @@ import ILocation from '@/models/ILocation' import ILocationQuery from '@/models/ILocationQuery' class MapService { - private key: string = process.env.VUE_APP_MAP_KEY || '' + private key: string = import.meta.env.VITE_MAP_KEY || '' public async getPosition(): Promise { const position = await this.getCurrentPosition() @@ -44,12 +44,13 @@ class MapService { return `https://dev.virtualearth.net/REST/v1/Locations/${latitude},${longitude}?key=${this.key}` } - private getCurrentPosition(): Promise { + private getCurrentPosition(): Promise { return new Promise((resolve, reject) => { if (!('geolocation' in navigator)) { resolve(null) + return } - return navigator.geolocation.getCurrentPosition(resolve, reject) + navigator.geolocation.getCurrentPosition(resolve, reject) }) } } diff --git a/src/services/TransactionService.ts b/src/services/TransactionService.ts index 5838d1e..d547a1d 100644 --- a/src/services/TransactionService.ts +++ b/src/services/TransactionService.ts @@ -45,8 +45,7 @@ class TransactionService { } public async get(id: string): Promise { - const transaction: ITransaction = await couchService.get(id) - return transaction + return await couchService.get(id) } public async add( diff --git a/src/shims-tsx.d.ts b/src/shims-tsx.d.ts deleted file mode 100644 index c656c68..0000000 --- a/src/shims-tsx.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Vue, { VNode } from 'vue' - -declare global { - namespace JSX { - // tslint:disable no-empty-interface - interface Element extends VNode {} - // tslint:disable no-empty-interface - interface ElementClass extends Vue {} - interface IntrinsicElements { - [elem: string]: any - } - } -} diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts deleted file mode 100644 index 9da0fb4..0000000 --- a/src/shims-vue.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare module '*.vue' { - import Vue from 'vue' - export default Vue -} -declare module 'vue-click-outside' -declare module 'lightness' -declare module 'crypto-js/aes' -declare module 'crypto-js/enc-utf8' -declare module '@xkeshi/vue-qrcode' -declare module 'font-color-contrast' -declare module 'vue-stripe-checkout' -declare module 'mapbox-gl/dist/mapbox-gl' diff --git a/src/shims.d.ts b/src/shims.d.ts new file mode 100644 index 0000000..adb8a1e --- /dev/null +++ b/src/shims.d.ts @@ -0,0 +1,13 @@ +/// +/// +/// +/// +/// +/// + +declare module 'lightness' { + const lightness: (color: string, value: number) => string + export default lightness +} + +import 'pinia-plugin-persistedstate' diff --git a/src/store.ts b/src/store.ts deleted file mode 100644 index b549084..0000000 --- a/src/store.ts +++ /dev/null @@ -1,156 +0,0 @@ -import Vue from 'vue' -import Vuex from 'vuex' -import VuexPersistence from 'vuex-persist' -import IUser from '@/models/IUser' -import userService from '@/services/UserService' -import couchService from '@/services/CouchService' -import IResponse from '@/models/IResponse' -import IUserPassword from '@/models/IUserPassword' -import IIdPassword from '@/models/IEmailPassword' -import { hasGoodNetwork } from '@/utils/network' -import { toHex } from '@/utils' -import queueNotifService from '@/services/QueueNotifService' - -const DEFAULT_LOCALE = 'fr' - -Vue.use(Vuex) - -const SET_LOCALE: string = 'SET_LOCALE' -const SET_TITLE: string = 'SET_TITLE' -const SET_USER: string = 'SET_USER' -const ADD_PUBLIC_ACCOUNT: string = 'ADD_PUBLIC_ACCOUNT' -const REMOVE_PUBLIC_ACCOUNT: string = 'REMOVE_PUBLIC_ACCOUNT' - -interface IState { - locale: string - user: IUser | null - hexUser: string | null - title: string | null - publicAccountIds: string[] -} - -export default new Vuex.Store({ - state: { - locale: DEFAULT_LOCALE, - user: null, - hexUser: null, - title: null, - publicAccountIds: [] - }, - getters: { - locale: ({ locale }) => locale || DEFAULT_LOCALE, - user: ({ user }) => user, - title: ({ title }) => title, - username: (state: IState) => - state.user - ? state.user.firstname && state.user.lastname - ? `${state.user.firstname} ${state.user.lastname}`.trim() - : state.user.email - : '', - isAppAdmin: (state: IState) => - state.user && - state.user.roles && - state.user.roles.find((role: string) => role === 'vaquant-admin'), - publicAccountIds: ({ publicAccountIds }) => publicAccountIds - }, - mutations: { - [SET_LOCALE](store, locale: string) { - store.locale = locale - }, - [SET_TITLE](store, title: string | null) { - store.title = title - }, - [SET_USER]( - store, - { user, replicate }: { user: IUser; replicate?: boolean } - ) { - store.user = user - store.hexUser = user ? toHex(user.userId) : null - couchService.setUser(user, replicate || false) - }, - [ADD_PUBLIC_ACCOUNT](store, { accountId }: { accountId: string }) { - if (!accountId) { - return - } - store.publicAccountIds = [ - ...new Set([...store.publicAccountIds, accountId]) - ] - }, - [REMOVE_PUBLIC_ACCOUNT](store, { accountId }: { accountId: string }) { - if (!accountId) { - return - } - store.publicAccountIds = store.publicAccountIds.filter( - (id: string) => id !== accountId && id !== null - ) - } - }, - actions: { - setLocale({ commit }, locale: string) { - commit(SET_LOCALE, locale) - }, - setTitle({ commit }, title: string) { - commit(SET_TITLE, title) - }, - async retrieveUser({ commit, state }) { - let user = state.user - if (user) { - commit(SET_USER, { user, replicate: false }) - } - // Update user if present and available online. - if (navigator.onLine && hasGoodNetwork()) { - user = await userService.getUser(user) - } - if (user) { - commit(SET_USER, { user, replicate: false }) - } - }, - async signup({ commit }, { user, password, replicate }: IUserPassword) { - const response: IResponse = await userService.signup(user, password) - if (response.ok) { - commit(SET_USER, { user, replicate }) - } else { - queueNotifService.error(response.message || '') - } - }, - async login({ commit, state }, { userId, password }: IIdPassword) { - const response: IResponse = await userService.login(userId, password) - if (response.ok) { - const user: IUser | null = await userService.getUser(state.user) - commit(SET_USER, { user }) - } else { - queueNotifService.error(response.message || '') - } - }, - async logout({ commit }) { - const response = await userService.logout() - if (response.ok) { - commit(SET_USER, { user: null }) - } else { - queueNotifService.error(response.message || '') - } - }, - async remove({ commit, state }) { - if (state.user) { - const response = await userService.deleteUser(state.user.userId) - if (response.ok) { - commit(SET_USER, { user: null }) - } else { - queueNotifService.error(response.message || '') - } - } - }, - addAccountId({ commit }, { accountId }: { accountId: string }) { - commit(ADD_PUBLIC_ACCOUNT, { accountId }) - }, - removeAccountId({ commit }, { accountId }: { accountId: string }) { - commit(REMOVE_PUBLIC_ACCOUNT, { accountId }) - } - }, - plugins: [ - new VuexPersistence({ - key: 'vaquant', - storage: window.localStorage - }).plugin - ] -}) diff --git a/src/stores/user.ts b/src/stores/user.ts new file mode 100644 index 0000000..d3b306e --- /dev/null +++ b/src/stores/user.ts @@ -0,0 +1,133 @@ +import { defineStore } from 'pinia' +import type IUser from '@/models/IUser' +import type IUserPassword from '@/models/IUserPassword' +import type IEmailPassword from '@/models/IEmailPassword' +import type IResponse from '@/models/IResponse' +import userService from '@/services/UserService' +import couchService from '@/services/CouchService' +import queueNotifService from '@/services/QueueNotifService' +import { hasGoodNetwork } from '@/utils/network' +import { toHex } from '@/utils' + +const DEFAULT_LOCALE = 'fr' + +interface UserState { + locale: string + user: IUser | null + hexUser: string | null + title: string | null + publicAccountIds: string[] +} + +export const useUserStore = defineStore('user', { + state: (): UserState => ({ + locale: DEFAULT_LOCALE, + user: null, + hexUser: null, + title: null, + publicAccountIds: [] + }), + + getters: { + currentLocale: (state) => state.locale || DEFAULT_LOCALE, + username: (state): string => + state.user + ? state.user.firstname && state.user.lastname + ? `${state.user.firstname} ${state.user.lastname}`.trim() + : state.user.email + : '', + isAppAdmin: (state): boolean => + !!state.user?.roles?.some((role: string) => role === 'vaquant-admin') + }, + + actions: { + setLocale(locale: string) { + this.locale = locale + }, + + setTitle(title: string | null) { + this.title = title + }, + + setUser({ user, replicate }: { user: IUser | null; replicate?: boolean }) { + this.user = user + this.hexUser = user ? toHex(user.userId) : null + couchService.setUser(user, replicate || false) + }, + + addAccountId(accountId: string) { + if (!accountId) { + return + } + this.publicAccountIds = Array.from( + new Set([...this.publicAccountIds, accountId]) + ) + }, + + removeAccountId(accountId: string) { + if (!accountId) { + return + } + this.publicAccountIds = this.publicAccountIds.filter( + (id) => id !== accountId && id !== null + ) + }, + + async retrieveUser() { + let user = this.user + if (user) { + this.setUser({ user, replicate: false }) + } + if (navigator.onLine && hasGoodNetwork()) { + user = await userService.getUser(user) + } + if (user) { + this.setUser({ user, replicate: false }) + } + }, + + async signup({ user, password, replicate }: IUserPassword) { + const response: IResponse = await userService.signup(user, password) + if (response.ok) { + this.setUser({ user, replicate }) + } else { + queueNotifService.error(response.message || '') + } + }, + + async login({ userId, password }: IEmailPassword) { + const response: IResponse = await userService.login(userId, password) + if (response.ok) { + const user: IUser | null = await userService.getUser(this.user) + this.setUser({ user }) + } else { + queueNotifService.error(response.message || '') + } + }, + + async logout() { + const response = await userService.logout() + if (response.ok) { + this.setUser({ user: null }) + } else { + queueNotifService.error(response.message || '') + } + }, + + async remove() { + if (this.user) { + const response = await userService.deleteUser(this.user.userId) + if (response.ok) { + this.setUser({ user: null }) + } else { + queueNotifService.error(response.message || '') + } + } + } + }, + + persist: { + key: 'vaquant', + pick: ['locale', 'user', 'hexUser', 'publicAccountIds'] + } +}) diff --git a/src/styles/earth-map.css b/src/styles/earth-map.css deleted file mode 100644 index 66be870..0000000 --- a/src/styles/earth-map.css +++ /dev/null @@ -1,688 +0,0 @@ -.mapboxgl-map { - font: 12px/20px Helvetica Neue, Arial, Helvetica, sans-serif; - overflow: hidden; - position: relative; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - text-align: left; -} -.mapboxgl-map:-webkit-full-screen { - width: 100%; - height: 100%; -} -.mapboxgl-canary { - background-color: salmon; -} -.mapboxgl-canvas-container.mapboxgl-interactive, -.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass { - cursor: -webkit-grab; - cursor: -moz-grab; - cursor: grab; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; -} -.mapboxgl-canvas-container.mapboxgl-interactive.mapboxgl-track-pointer { - cursor: pointer; -} -.mapboxgl-canvas-container.mapboxgl-interactive:active, -.mapboxgl-ctrl-group button.mapboxgl-ctrl-compass:active { - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - cursor: grabbing; -} -.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate, -.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate .mapboxgl-canvas { - touch-action: pan-x pan-y; -} -.mapboxgl-canvas-container.mapboxgl-touch-drag-pan, -.mapboxgl-canvas-container.mapboxgl-touch-drag-pan .mapboxgl-canvas { - touch-action: pinch-zoom; -} -.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan, -.mapboxgl-canvas-container.mapboxgl-touch-zoom-rotate.mapboxgl-touch-drag-pan - .mapboxgl-canvas { - touch-action: none; -} -.mapboxgl-ctrl-bottom-left, -.mapboxgl-ctrl-bottom-right, -.mapboxgl-ctrl-top-left, -.mapboxgl-ctrl-top-right { - position: absolute; - pointer-events: none; - z-index: 2; -} -.mapboxgl-ctrl-top-left { - top: 0; - left: 0; -} -.mapboxgl-ctrl-top-right { - top: 0; - right: 0; -} -.mapboxgl-ctrl-bottom-left { - bottom: 0; - left: 0; -} -.mapboxgl-ctrl-bottom-right { - right: 0; - bottom: 0; -} -.mapboxgl-ctrl { - clear: both; - pointer-events: auto; - transform: translate(0); -} -.mapboxgl-ctrl-top-left .mapboxgl-ctrl { - margin: 10px 0 0 10px; - float: left; -} -.mapboxgl-ctrl-top-right .mapboxgl-ctrl { - margin: 10px 10px 0 0; - float: right; -} -.mapboxgl-ctrl-bottom-left .mapboxgl-ctrl { - margin: 0 0 10px 10px; - float: left; -} -.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl { - margin: 0 10px 10px 0; - float: right; -} -.mapboxgl-ctrl-group { - border-radius: 4px; - background: #fff; -} -.mapboxgl-ctrl-group:not(:empty) { - -moz-box-shadow: 0 0 2px rgba(0, 0, 0, 0.1); - -webkit-box-shadow: 0 0 2px rgba(0, 0, 0, 0.1); - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl-group:not(:empty) { - box-shadow: 0 0 0 2px ButtonText; - } -} -.mapboxgl-ctrl-group button { - width: 29px; - height: 29px; - display: block; - padding: 0; - outline: none; - border: 0; - box-sizing: border-box; - background-color: transparent; - cursor: pointer; -} -.mapboxgl-ctrl-group button + button { - border-top: 1px solid #ddd; -} -.mapboxgl-ctrl button .mapboxgl-ctrl-icon { - display: block; - width: 100%; - height: 100%; - background-repeat: no-repeat; - background-position: 50%; -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl-icon { - background-color: transparent; - } - .mapboxgl-ctrl-group button + button { - border-top: 1px solid ButtonText; - } -} -.mapboxgl-ctrl button::-moz-focus-inner { - border: 0; - padding: 0; -} -.mapboxgl-ctrl-group button:focus { - box-shadow: 0 0 2px 2px #0096ff; -} -.mapboxgl-ctrl button:disabled { - cursor: not-allowed; -} -.mapboxgl-ctrl button:disabled .mapboxgl-ctrl-icon { - opacity: 0.25; -} -.mapboxgl-ctrl button:not(:disabled):hover { - background-color: rgba(0, 0, 0, 0.05); -} -.mapboxgl-ctrl-group button:focus:focus-visible { - box-shadow: 0 0 2px 2px #0096ff; -} -.mapboxgl-ctrl-group button:focus:not(:focus-visible) { - box-shadow: none; -} -.mapboxgl-ctrl-group button:focus:first-child { - border-radius: 4px 4px 0 0; -} -.mapboxgl-ctrl-group button:focus:last-child { - border-radius: 0 0 4px 4px; -} -.mapboxgl-ctrl-group button:focus:only-child { - border-radius: inherit; -} -.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); - } -} -@media (-ms-high-contrast: black-on-white) { - .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-out .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-9z'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-zoom-in .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5z'/%3E%3C/svg%3E"); - } -} -.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); - } -} -@media (-ms-high-contrast: black-on-white) { - .mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3h1zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16h1zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5H13zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1V7.5z'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1h-5.5zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1v-5.5zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1v5.5zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1h5.5z'/%3E%3C/svg%3E"); - } -} -.mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E"); -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23999'/%3E%3C/svg%3E"); - } -} -@media (-ms-high-contrast: black-on-white) { - .mapboxgl-ctrl button.mapboxgl-ctrl-compass .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E"); - } -} -.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23aaa'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E"); -} -.mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-waiting - .mapboxgl-ctrl-icon { - -webkit-animation: mapboxgl-spin 2s linear infinite; - -moz-animation: mapboxgl-spin 2s infinite linear; - -o-animation: mapboxgl-spin 2s infinite linear; - -ms-animation: mapboxgl-spin 2s infinite linear; - animation: mapboxgl-spin 2s linear infinite; -} -@media (-ms-high-contrast: active) { - .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23fff'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23999'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-active-error - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e58978'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%2333b5e5'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl - button.mapboxgl-ctrl-geolocate.mapboxgl-ctrl-geolocate-background-error - .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23e54e33'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3C/svg%3E"); - } -} -@media (-ms-high-contrast: black-on-white) { - .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E"); - } - .mapboxgl-ctrl button.mapboxgl-ctrl-geolocate:disabled .mapboxgl-ctrl-icon { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23666'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath d='M14 5l1 1-9 9-1-1 9-9z' fill='red'/%3E%3C/svg%3E"); - } -} -@-webkit-keyframes mapboxgl-spin { - 0% { - -webkit-transform: rotate(0deg); - } - to { - -webkit-transform: rotate(1turn); - } -} -@-moz-keyframes mapboxgl-spin { - 0% { - -moz-transform: rotate(0deg); - } - to { - -moz-transform: rotate(1turn); - } -} -@-o-keyframes mapboxgl-spin { - 0% { - -o-transform: rotate(0deg); - } - to { - -o-transform: rotate(1turn); - } -} -@-ms-keyframes mapboxgl-spin { - 0% { - -ms-transform: rotate(0deg); - } - to { - -ms-transform: rotate(1turn); - } -} -@keyframes mapboxgl-spin { - 0% { - transform: rotate(0deg); - } - to { - transform: rotate(1turn); - } -} -a.mapboxgl-ctrl-logo { - width: 88px; - height: 23px; - margin: 0 0 -4px -4px; - display: block; - background-repeat: no-repeat; - cursor: pointer; - overflow: hidden; - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg opacity='.3' stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cg opacity='.9' fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/g%3E%3C/svg%3E"); -} -a.mapboxgl-ctrl-logo.mapboxgl-compact { - width: 23px; -} -@media (-ms-high-contrast: active) { - a.mapboxgl-ctrl-logo { - background-color: transparent; - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg stroke='%23000' stroke-width='3'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cg fill='%23fff'%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/g%3E%3C/svg%3E"); - } -} -@media (-ms-high-contrast: black-on-white) { - a.mapboxgl-ctrl-logo { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='88' height='23' viewBox='0 0 88 23' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill-rule='evenodd'%3E%3Cdefs%3E%3Cpath id='a' d='M11.5 2.25c5.105 0 9.25 4.145 9.25 9.25s-4.145 9.25-9.25 9.25-9.25-4.145-9.25-9.25 4.145-9.25 9.25-9.25zM6.997 15.983c-.051-.338-.828-5.802 2.233-8.873a4.395 4.395 0 013.13-1.28c1.27 0 2.49.51 3.39 1.42.91.9 1.42 2.12 1.42 3.39 0 1.18-.449 2.301-1.28 3.13C12.72 16.93 7 16 7 16l-.003-.017zM15.3 10.5l-2 .8-.8 2-.8-2-2-.8 2-.8.8-2 .8 2 2 .8z'/%3E%3Cpath id='b' d='M50.63 8c.13 0 .23.1.23.23V9c.7-.76 1.7-1.18 2.73-1.18 2.17 0 3.95 1.85 3.95 4.17s-1.77 4.19-3.94 4.19c-1.04 0-2.03-.43-2.74-1.18v3.77c0 .13-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V8.23c0-.12.1-.23.23-.23h1.4zm-3.86.01c.01 0 .01 0 .01-.01.13 0 .22.1.22.22v7.55c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V15c-.7.76-1.69 1.19-2.73 1.19-2.17 0-3.94-1.87-3.94-4.19 0-2.32 1.77-4.19 3.94-4.19 1.03 0 2.02.43 2.73 1.18v-.75c0-.12.1-.23.23-.23h1.4zm26.375-.19a4.24 4.24 0 00-4.16 3.29c-.13.59-.13 1.19 0 1.77a4.233 4.233 0 004.17 3.3c2.35 0 4.26-1.87 4.26-4.19 0-2.32-1.9-4.17-4.27-4.17zM60.63 5c.13 0 .23.1.23.23v3.76c.7-.76 1.7-1.18 2.73-1.18 1.88 0 3.45 1.4 3.84 3.28.13.59.13 1.2 0 1.8-.39 1.88-1.96 3.29-3.84 3.29-1.03 0-2.02-.43-2.73-1.18v.77c0 .12-.1.23-.23.23h-1.4c-.13 0-.23-.1-.23-.23V5.23c0-.12.1-.23.23-.23h1.4zm-34 11h-1.4c-.13 0-.23-.11-.23-.23V8.22c.01-.13.1-.22.23-.22h1.4c.13 0 .22.11.23.22v.68c.5-.68 1.3-1.09 2.16-1.1h.03c1.09 0 2.09.6 2.6 1.55.45-.95 1.4-1.55 2.44-1.56 1.62 0 2.93 1.25 2.9 2.78l.03 5.2c0 .13-.1.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.8 0-1.46.7-1.59 1.62l.01 4.68c0 .13-.11.23-.23.23h-1.41c-.13 0-.23-.11-.23-.23v-4.59c0-.98-.74-1.71-1.62-1.71-.85 0-1.54.79-1.6 1.8v4.5c0 .13-.1.23-.23.23zm53.615 0h-1.61c-.04 0-.08-.01-.12-.03-.09-.06-.13-.19-.06-.28l2.43-3.71-2.39-3.65a.213.213 0 01-.03-.12c0-.12.09-.21.21-.21h1.61c.13 0 .24.06.3.17l1.41 2.37 1.4-2.37a.34.34 0 01.3-.17h1.6c.04 0 .08.01.12.03.09.06.13.19.06.28l-2.37 3.65 2.43 3.7c0 .05.01.09.01.13 0 .12-.09.21-.21.21h-1.61c-.13 0-.24-.06-.3-.17l-1.44-2.42-1.44 2.42a.34.34 0 01-.3.17zm-7.12-1.49c-1.33 0-2.42-1.12-2.42-2.51 0-1.39 1.08-2.52 2.42-2.52 1.33 0 2.42 1.12 2.42 2.51 0 1.39-1.08 2.51-2.42 2.52zm-19.865 0c-1.32 0-2.39-1.11-2.42-2.48v-.07c.02-1.38 1.09-2.49 2.4-2.49 1.32 0 2.41 1.12 2.41 2.51 0 1.39-1.07 2.52-2.39 2.53zm-8.11-2.48c-.01 1.37-1.09 2.47-2.41 2.47s-2.42-1.12-2.42-2.51c0-1.39 1.08-2.52 2.4-2.52 1.33 0 2.39 1.11 2.41 2.48l.02.08zm18.12 2.47c-1.32 0-2.39-1.11-2.41-2.48v-.06c.02-1.38 1.09-2.48 2.41-2.48s2.42 1.12 2.42 2.51c0 1.39-1.09 2.51-2.42 2.51z'/%3E%3C/defs%3E%3Cmask id='c'%3E%3Crect width='100%25' height='100%25' fill='%23fff'/%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/mask%3E%3Cg stroke='%23fff' stroke-width='3' fill='%23fff'%3E%3Ccircle mask='url(%23c)' cx='11.5' cy='11.5' r='9.25'/%3E%3Cuse xlink:href='%23b' mask='url(%23c)'/%3E%3C/g%3E%3Cuse xlink:href='%23a'/%3E%3Cuse xlink:href='%23b'/%3E%3C/svg%3E"); - } -} -.mapboxgl-ctrl.mapboxgl-ctrl-attrib { - padding: 0 5px; - background-color: hsla(0, 0%, 100%, 0.5); - margin: 0; -} -@media screen { - .mapboxgl-ctrl-attrib.mapboxgl-compact { - min-height: 20px; - padding: 0; - margin: 10px; - position: relative; - background-color: #fff; - border-radius: 3px 12px 12px 3px; - } - .mapboxgl-ctrl-attrib.mapboxgl-compact:hover { - padding: 2px 24px 2px 4px; - visibility: visible; - margin-top: 6px; - } - .mapboxgl-ctrl-bottom-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:hover, - .mapboxgl-ctrl-top-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:hover { - padding: 2px 4px 2px 24px; - border-radius: 12px 3px 3px 12px; - } - .mapboxgl-ctrl-attrib.mapboxgl-compact .mapboxgl-ctrl-attrib-inner { - display: none; - } - .mapboxgl-ctrl-attrib.mapboxgl-compact:hover .mapboxgl-ctrl-attrib-inner { - display: block; - } - .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - content: ''; - cursor: pointer; - position: absolute; - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E"); - background-color: hsla(0, 0%, 100%, 0.5); - width: 24px; - height: 24px; - box-sizing: border-box; - border-radius: 12px; - } - .mapboxgl-ctrl-bottom-right > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - bottom: 0; - right: 0; - } - .mapboxgl-ctrl-top-right > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - top: 0; - right: 0; - } - .mapboxgl-ctrl-top-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - top: 0; - left: 0; - } - .mapboxgl-ctrl-bottom-left > .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - bottom: 0; - left: 0; - } -} -@media screen and (-ms-high-contrast: active) { - .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' fill='%23fff'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E"); - } -} -@media screen and (-ms-high-contrast: black-on-white) { - .mapboxgl-ctrl-attrib.mapboxgl-compact:after { - background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='24' height='24' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd'%3E%3Cpath d='M4 10a6 6 0 1012 0 6 6 0 10-12 0m5-3a1 1 0 102 0 1 1 0 10-2 0m0 3a1 1 0 112 0v3a1 1 0 11-2 0'/%3E%3C/svg%3E"); - } -} -.mapboxgl-ctrl-attrib a { - color: rgba(0, 0, 0, 0.75); - text-decoration: none; -} -.mapboxgl-ctrl-attrib a:hover { - color: inherit; - text-decoration: underline; -} -.mapboxgl-ctrl-attrib .mapbox-improve-map { - font-weight: 700; - margin-left: 2px; -} -.mapboxgl-attrib-empty { - display: none; -} -.mapboxgl-ctrl-scale { - background-color: hsla(0, 0%, 100%, 0.75); - font-size: 10px; - border: 2px solid #333; - border-top: #333; - padding: 0 5px; - color: #333; - box-sizing: border-box; -} -.mapboxgl-popup { - position: absolute; - top: 0; - left: 0; - display: -webkit-flex; - display: flex; - will-change: transform; - pointer-events: none; -} -.mapboxgl-popup-anchor-top, -.mapboxgl-popup-anchor-top-left, -.mapboxgl-popup-anchor-top-right { - -webkit-flex-direction: column; - flex-direction: column; -} -.mapboxgl-popup-anchor-bottom, -.mapboxgl-popup-anchor-bottom-left, -.mapboxgl-popup-anchor-bottom-right { - -webkit-flex-direction: column-reverse; - flex-direction: column-reverse; -} -.mapboxgl-popup-anchor-left { - -webkit-flex-direction: row; - flex-direction: row; -} -.mapboxgl-popup-anchor-right { - -webkit-flex-direction: row-reverse; - flex-direction: row-reverse; -} -.mapboxgl-popup-tip { - width: 0; - height: 0; - border: 10px solid transparent; - z-index: 1; -} -.mapboxgl-popup-anchor-top .mapboxgl-popup-tip { - -webkit-align-self: center; - align-self: center; - border-top: none; - border-bottom-color: #fff; -} -.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip { - -webkit-align-self: flex-start; - align-self: flex-start; - border-top: none; - border-left: none; - border-bottom-color: #fff; -} -.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip { - -webkit-align-self: flex-end; - align-self: flex-end; - border-top: none; - border-right: none; - border-bottom-color: #fff; -} -.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip { - -webkit-align-self: center; - align-self: center; - border-bottom: none; - border-top-color: #fff; -} -.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip { - -webkit-align-self: flex-start; - align-self: flex-start; - border-bottom: none; - border-left: none; - border-top-color: #fff; -} -.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip { - -webkit-align-self: flex-end; - align-self: flex-end; - border-bottom: none; - border-right: none; - border-top-color: #fff; -} -.mapboxgl-popup-anchor-left .mapboxgl-popup-tip { - -webkit-align-self: center; - align-self: center; - border-left: none; - border-right-color: #fff; -} -.mapboxgl-popup-anchor-right .mapboxgl-popup-tip { - -webkit-align-self: center; - align-self: center; - border-right: none; - border-left-color: #fff; -} -.mapboxgl-popup-close-button { - position: absolute; - right: 0; - top: 0; - border: 0; - border-radius: 0 3px 0 0; - cursor: pointer; - background-color: transparent; -} -.mapboxgl-popup-close-button:hover { - background-color: rgba(0, 0, 0, 0.05); -} -.mapboxgl-popup-content { - position: relative; - background: #fff; - border-radius: 3px; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); - padding: 10px 10px 15px; - pointer-events: auto; -} -.mapboxgl-popup-anchor-top-left .mapboxgl-popup-content { - border-top-left-radius: 0; -} -.mapboxgl-popup-anchor-top-right .mapboxgl-popup-content { - border-top-right-radius: 0; -} -.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-content { - border-bottom-left-radius: 0; -} -.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-content { - border-bottom-right-radius: 0; -} -.mapboxgl-popup-track-pointer { - display: none; -} -.mapboxgl-popup-track-pointer * { - pointer-events: none; - user-select: none; -} -.mapboxgl-map:hover .mapboxgl-popup-track-pointer { - display: flex; -} -.mapboxgl-map:active .mapboxgl-popup-track-pointer { - display: none; -} -.mapboxgl-marker { - position: absolute; - top: 0; - left: 0; - will-change: transform; -} -.mapboxgl-user-location-dot, -.mapboxgl-user-location-dot:before { - background-color: #1da1f2; - width: 15px; - height: 15px; - border-radius: 50%; -} -.mapboxgl-user-location-dot:before { - content: ''; - position: absolute; - -webkit-animation: mapboxgl-user-location-dot-pulse 2s infinite; - -moz-animation: mapboxgl-user-location-dot-pulse 2s infinite; - -ms-animation: mapboxgl-user-location-dot-pulse 2s infinite; - animation: mapboxgl-user-location-dot-pulse 2s infinite; -} -.mapboxgl-user-location-dot:after { - border-radius: 50%; - border: 2px solid #fff; - content: ''; - height: 19px; - left: -2px; - position: absolute; - top: -2px; - width: 19px; - box-sizing: border-box; - box-shadow: 0 0 3px rgba(0, 0, 0, 0.35); -} -@-webkit-keyframes mapboxgl-user-location-dot-pulse { - 0% { - -webkit-transform: scale(1); - opacity: 1; - } - 70% { - -webkit-transform: scale(3); - opacity: 0; - } - to { - -webkit-transform: scale(1); - opacity: 0; - } -} -@-ms-keyframes mapboxgl-user-location-dot-pulse { - 0% { - -ms-transform: scale(1); - opacity: 1; - } - 70% { - -ms-transform: scale(3); - opacity: 0; - } - to { - -ms-transform: scale(1); - opacity: 0; - } -} -@keyframes mapboxgl-user-location-dot-pulse { - 0% { - transform: scale(1); - opacity: 1; - } - 70% { - transform: scale(3); - opacity: 0; - } - to { - transform: scale(1); - opacity: 0; - } -} -.mapboxgl-user-location-dot-stale { - background-color: #aaa; -} -.mapboxgl-user-location-dot-stale:after { - display: none; -} -.mapboxgl-user-location-accuracy-circle { - background-color: rgba(29, 161, 242, 0.2); - width: 1px; - height: 1px; - border-radius: 100%; -} -.mapboxgl-crosshair, -.mapboxgl-crosshair .mapboxgl-interactive, -.mapboxgl-crosshair .mapboxgl-interactive:active { - cursor: crosshair; -} -.mapboxgl-boxzoom { - position: absolute; - top: 0; - left: 0; - width: 0; - height: 0; - background: #fff; - border: 2px dotted #202020; - opacity: 0.5; -} -@media print { - .mapbox-improve-map { - display: none; - } -} diff --git a/src/styles/framework.scss b/src/styles/framework.scss deleted file mode 100644 index 3c9f642..0000000 --- a/src/styles/framework.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import './variables'; -@import '~bulma/sass/base/_all.sass'; -@import '~bulma/sass/components/navbar.sass'; -@import '~bulma/sass/components/breadcrumb.sass'; -@import '~bulma/sass/components/card.sass'; -@import '~bulma/sass/components/message.sass'; -@import '~bulma/sass/components/tabs.sass'; -@import '~bulma/sass/components/modal.sass'; -@import '~bulma/sass/elements/button.sass'; -@import '~bulma/sass/elements/box.sass'; -@import '~bulma/sass/elements/container.sass'; -@import '~bulma/sass/elements/table.sass'; -@import '~bulma/sass/elements/title.sass'; -@import '~bulma/sass/elements/progress.sass'; -@import '~bulma/sass/elements/other.sass'; -@import '~bulma/sass/form/_all.sass'; -@import '~bulma/sass/layout/_all.sass'; -@import '~bulma/sass/grid/columns.sass'; diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 0000000..379a63a --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,23 @@ +@import 'tailwindcss'; + +@plugin "daisyui" { + themes: vaquant --default, light, dark; +} + +@plugin "daisyui/theme" { + name: 'vaquant'; + default: true; + color-scheme: light; + --color-primary: #3f4fa6; + --color-primary-content: #ffffff; + --color-secondary: #2c3e50; + --color-secondary-content: #ffffff; + --color-success: oklch(67% 0.17 145); + --color-error: oklch(60% 0.21 25); + --color-base-100: #ffffff; + --color-base-200: #f5f5f7; + --color-base-300: #e5e5ea; + --color-base-content: #1c1c1e; +} + +@import '@tabler/icons-webfont/dist/tabler-icons.min.css'; diff --git a/src/styles/index.scss b/src/styles/index.scss deleted file mode 100644 index 77de212..0000000 --- a/src/styles/index.scss +++ /dev/null @@ -1,155 +0,0 @@ -// sass-lint:disable final-newline empty-line-between-blocks class-name-format no-url-protocols no-url-domains nesting-depth -@charset 'utf-8'; -@import url('https://fonts.googleapis.com/css?family=Nunito|Raleway|Open+Sans&display=swap'); -@import url('https://css.gg/css?=|pin-alt'); -@import './framework'; -@import './transitions'; -:root { - --primary-color: #{$primary}; - --primary-font-color: #{$main}; -} - -html { - overflow-y: auto; - min-height: 100vh; - user-select: none; -} - -body { - background-color: white; -} - -.app { - color: $main; - text-align: center; - - .field-body { - flex-grow: 3; - } -} - -nav { - &.nav { - a { - color: $main; - font-weight: bold; - - &.router-link-exact-active { - color: $blue; - } - } - } -} - -.app, -a, -button { - font-family: 'Nunito', sans-serif; -} - -.no-margin { - margin: 0 0.75rem; -} - -.clear-margin { - margin: 0; -} - -.no-padding { - padding: 0.75rem 0; -} - -.numeric { - font-family: 'Cutive Mono', monospace; -} - -$navbar-color: #eaeaea4d; - -.navbar-item.nav-button { - border: 2px solid $navbar-color; - border-radius: 10px; - margin: 5px; -} - -.title, -.subtitle, -tr { - color: $main; - font-family: 'Raleway', sans-serif; -} - -img { - &.logo { - height: auto; - margin: 10px 0; - width: 180pt; - } -} - -.icon { - height: auto; - width: 50px; -} - -.icon.is-left { - display: flex; - justify-content: center; - align-items: center; -} - -.back-icon { - vertical-align: middle; -} - -.breadcrumb { - font-size: 16pt; - text-align: center; - - ul { - justify-content: center; - } -} - -// Service Worker update -.notify-worker { - background-color: $main; - border-radius: 2px; - bottom: 30px; - color: $white; - margin: auto; - max-width: 450px; - min-width: 250px; - padding: 16px; - position: fixed; - text-align: center; - visibility: hidden; - width: 100%; - z-index: 1; - - &.show { - animation: fadein 0.5s; - visibility: visible; - } - - button { - margin-top: 10px; - } -} - -table { - .total { - font-variant: small-caps; - } -} - -@keyframes fadein { - from { - bottom: 0; - opacity: 0; - } - - to { - bottom: 30px; - opacity: 1; - } -} diff --git a/src/styles/transitions.scss b/src/styles/transitions.scss deleted file mode 100644 index 7d1ac43..0000000 --- a/src/styles/transitions.scss +++ /dev/null @@ -1,12 +0,0 @@ -// sass-lint:disable no-transition-all final-newline -.fade-enter-active, -.fade-leave-active { - transition-duration: .1s; - transition-property: opacity; - transition-timing-function: ease; -} - -.fade-enter, -.fade-leave-active { - opacity: 0; -} \ No newline at end of file diff --git a/src/styles/variables.scss b/src/styles/variables.scss deleted file mode 100644 index ab2edae..0000000 --- a/src/styles/variables.scss +++ /dev/null @@ -1,9 +0,0 @@ -// sass-lint:disable final-newline empty-line-between-blocks class-name-format no-url-protocols no-url-domains nesting-depth -$primary: #3f4fa6; -$main: #2c3e50; -$green: hsl(141, 63%, 44%); -$red: hsl(348, 54%, 52%); -$white: #fff; -$progress-border-radius: 0; - -@import '~bulma/sass/utilities/_all.sass'; diff --git a/src/utils/bus-event.ts b/src/utils/bus-event.ts index c0eefbd..87ca636 100644 --- a/src/utils/bus-event.ts +++ b/src/utils/bus-event.ts @@ -1,7 +1,14 @@ -import Vue from 'vue' +import mitt from 'mitt' -export default new Vue() +export const ONLINE = 'ONLINE' +export const OFFLINE = 'OFFLINE' +export const SYNC = 'SYNC' -export const ONLINE: string = 'ONLINE' -export const OFFLINE: string = 'OFFLINE' -export const SYNC: string = 'SYNC' +type Events = { + [ONLINE]: undefined + [OFFLINE]: undefined + [SYNC]: string[] | undefined +} + +export const bus = mitt() +export default bus diff --git a/src/utils/filters.ts b/src/utils/format.ts similarity index 55% rename from src/utils/filters.ts rename to src/utils/format.ts index 4c366c7..e339fb2 100644 --- a/src/utils/filters.ts +++ b/src/utils/format.ts @@ -1,14 +1,13 @@ -import ICurrency from '@/models/ICurrency' +import type ICurrency from '@/models/ICurrency' export const money = ( value: number, currency: ICurrency | null | undefined, - country: string = 'fr-FR' + country = 'fr-FR' ): string => { if (!currency) { return value.toString() } - return new Intl.NumberFormat(country, { style: 'currency', currency: currency.code, @@ -19,36 +18,24 @@ export const money = ( export const moneypad = ( value: number, currency?: ICurrency | null, - withPadEnd: boolean = true + withPadEnd = true ): string => { const m = money(value, currency) const s: string[] = m.split('\xa0') let cur: string | undefined = s.pop() - if (withPadEnd) { cur = (cur || '').padEnd(3, '\xa0') } - - const result = `${s.join('\xa0')}\xa0${cur || ''}` - return result + return `${s.join('\xa0')}\xa0${cur || ''}` } -export const date = (value: string, country: string = 'fr-FR') => { - return new Intl.DateTimeFormat(country).format(new Date(value)) -} +export const date = (value: string, country = 'fr-FR') => + new Intl.DateTimeFormat(country).format(new Date(value)) -export const fulldate = (value: string, country: string = 'fr-FR') => { - return new Intl.DateTimeFormat(country, { +export const fulldate = (value: string, country = 'fr-FR') => + new Intl.DateTimeFormat(country, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(value)) -} - -export default { - money, - moneypad, - date, - fulldate -} as { [key: string]: (...args: any[]) => any } diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts deleted file mode 100644 index 0b658b0..0000000 --- a/src/utils/hooks.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Component } from 'vue-property-decorator' - -Component.registerHooks(['beforeRouteEnter', 'beforeRouteUpdate']) diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 935d7dd..fe14280 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -1,15 +1,6 @@ -import Vue from 'vue' -import '@tabler/icons-webfont/dist/tabler-icons.min.css' +import type { App } from 'vue' +import VaquantIcon from '@/components/VaquantIcon.vue' -Vue.component('vaquant-icon', { - functional: true, - props: { - icon: { type: String, required: true } - }, - render(h, { props, data }) { - return h('i', { - ...data, - class: [`ti ti-${props.icon}`, data.class, data.staticClass] - }) - } -}) +export const registerIcons = (app: App): void => { + app.component('VaquantIcon', VaquantIcon) +} diff --git a/src/utils/network.ts b/src/utils/network.ts index dbabbad..9ab4797 100644 --- a/src/utils/network.ts +++ b/src/utils/network.ts @@ -1,20 +1,26 @@ -declare var navigator: any +interface NetworkConnection { + effectiveType?: string + metered?: boolean + bandwidth?: number +} + +interface NavigatorWithConnection { + connection?: NetworkConnection + mozConnection?: NetworkConnection + webkitConnection?: NetworkConnection +} export const hasGoodNetwork = (): boolean => { + const nav = navigator as Navigator & NavigatorWithConnection const connection = - navigator.connection || - navigator.mozConnection || - navigator.webkitConnection + nav.connection || nav.mozConnection || nav.webkitConnection if (connection) { if (connection.effectiveType) { - const goodNetworks: string[] = ['3g', '4g'] + const goodNetworks = ['3g', '4g'] return goodNetworks.includes(connection.effectiveType) - } else { - const highBandwidth: boolean = - connection.metered && (connection.bandwidth || 0) > 2 - return highBandwidth } + return !!(connection.metered && (connection.bandwidth || 0) > 2) } return true diff --git a/src/utils/serverless-url.ts b/src/utils/serverless-url.ts index 71c9249..53faf0d 100644 --- a/src/utils/serverless-url.ts +++ b/src/utils/serverless-url.ts @@ -1,5 +1,5 @@ const ROOT: string = - process.env.NODE_ENV === 'production' + import.meta.env.MODE === 'production' ? 'https://vaquant.azurewebsites.net/api' : 'http://localhost:7071/api' diff --git a/src/views/About.vue b/src/views/About.vue index 394e8be..485a9e0 100644 --- a/src/views/About.vue +++ b/src/views/About.vue @@ -1,29 +1,18 @@ - + - + diff --git a/src/views/Home.vue b/src/views/Home.vue index ee56716..92c61b5 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -1,70 +1,48 @@ + + - - - - diff --git a/src/views/Pricing.vue b/src/views/Pricing.vue deleted file mode 100644 index 874be0d..0000000 --- a/src/views/Pricing.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/src/views/Security.vue b/src/views/Security.vue index 2e8a315..f3a5c2e 100644 --- a/src/views/Security.vue +++ b/src/views/Security.vue @@ -1,18 +1,6 @@ - - diff --git a/src/views/User.vue b/src/views/User.vue index e2a43bb..9d94099 100644 --- a/src/views/User.vue +++ b/src/views/User.vue @@ -1,380 +1,263 @@ - - - - + diff --git a/src/views/accounts/AccountItem.vue b/src/views/accounts/AccountItem.vue index 1e2f653..a921b81 100644 --- a/src/views/accounts/AccountItem.vue +++ b/src/views/accounts/AccountItem.vue @@ -1,569 +1,323 @@ -