diff --git a/public/fonts/fa-brands.ttf b/public/fonts/fa-brands.ttf deleted file mode 100644 index 82e255a..0000000 Binary files a/public/fonts/fa-brands.ttf and /dev/null differ diff --git a/public/fonts/fa-regular.ttf b/public/fonts/fa-regular.ttf deleted file mode 100644 index 5267d85..0000000 Binary files a/public/fonts/fa-regular.ttf and /dev/null differ diff --git a/public/fonts/fa-solid.ttf b/public/fonts/fa-solid.ttf deleted file mode 100644 index 16d4469..0000000 Binary files a/public/fonts/fa-solid.ttf and /dev/null differ diff --git a/public/img/customizable.png b/public/img/customizable.png deleted file mode 100644 index 592beb7..0000000 Binary files a/public/img/customizable.png and /dev/null differ diff --git a/public/img/lightweight.png b/public/img/lightweight.png deleted file mode 100644 index af6eab5..0000000 Binary files a/public/img/lightweight.png and /dev/null differ diff --git a/public/img/opensource.png b/public/img/opensource.png deleted file mode 100644 index f93575a..0000000 Binary files a/public/img/opensource.png and /dev/null differ diff --git a/src/client/api.ts b/src/client/api.ts index f85a876..039cb54 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -1,4 +1,4 @@ -export const throttle = (func: () => void, wait: number) => { +export const throttle = (func: (...args: any[]) => any, wait: number) => { var lastTime = 0; var timeoutId: ReturnType | undefined; var lastArgs: any[]; @@ -54,7 +54,7 @@ export const save = (id: string, v: journey) => export const query_nominatim = ( q: string, bb: any, - f: (v: string) => Boolean = () => true + f: (v: NominatimResult) => Boolean = () => true ) => { if (q.length == 0) return Promise.resolve([]) let url = new URL("/api/place/" + q, window.location.origin); diff --git a/src/client/api_helper.ts b/src/client/helper/api.ts similarity index 56% rename from src/client/api_helper.ts rename to src/client/helper/api.ts index 9ef0ac5..08e1c85 100644 --- a/src/client/api_helper.ts +++ b/src/client/helper/api.ts @@ -1,8 +1,8 @@ -import { getGeoLine } from "./types/geom"; +import { getGeoLine } from "../types/geom"; +import * as api from "../api"; - -export const filter_existing = function (tpe: "nominatim" | "travel", leg: leg, r: geoloc[]) { +const filter_existing = function (tpe: "nominatim" | "travel", leg: leg, r: geoloc[]) { switch (tpe) { case 'nominatim': return r.filter(e => { @@ -20,7 +20,7 @@ export const filter_existing = function (tpe: "nominatim" | "travel", leg: leg, } } -export const process_results = function (tpe: "nominatim" | "travel", r: geoloc[]) { +const process_results = function (tpe: "nominatim" | "travel", r: geoloc[]) { switch (tpe) { case 'nominatim': return r.map((rr) => { @@ -38,4 +38,24 @@ export const process_results = function (tpe: "nominatim" | "travel", r: geoloc[ return el; }); } -} \ No newline at end of file +} + +var _search_set_results: (...arg: any[]) => any; +export const set_search_set_results = function (f: (...arg: any[]) => any) { + _search_set_results = f; +} +export const search_nominatim = api.throttle( + (f: string, q: string, bb: [[number, number], [number, number]], leg: leg) => + api.query_nominatim(q, bb, api.get_filter(f)).catch((_err) => console.log(_err)).then((r) => { + r = process_results('nominatim', r) + r = filter_existing('nominatim', leg, r) + _search_set_results(r) + }), 1000); + +export const search_flight = api.throttle( + (f: string, q: string, leg: leg) => + api.query_flight(q).then((r) => { + r = process_results('travel', r) + r = filter_existing('travel', leg, r) + _search_set_results(r) + }), 2000) \ No newline at end of file diff --git a/src/client/journey_helper.ts b/src/client/helper/journey.ts similarity index 100% rename from src/client/journey_helper.ts rename to src/client/helper/journey.ts diff --git a/src/client/helper/nav.ts b/src/client/helper/nav.ts new file mode 100644 index 0000000..a14e920 --- /dev/null +++ b/src/client/helper/nav.ts @@ -0,0 +1,49 @@ +import journey_wrapper from "../types/wrapper"; + +var nav = { + scrollInterval: 0, + scrollDir: 'none', +}; + + +const sideScroll = function (element: Element, direction: 'left' | 'right' | 'none', speed: number, step: number) { + nav.scrollDir = direction + if (direction == 'none') return; + nav.scrollInterval = setInterval(() => { + element.scrollLeft += (direction == 'left') ? -step : step; + }, speed); +} + +export const focus_leg = function (journey: journey_wrapper, idx: number | null = null) { + const c = document.querySelector('.scroll-content.nav-leg')!! + console.log(idx, c, journey) + const item = c.children[(idx != null ? idx : journey.sel_leg) + 1]; + c.scrollLeft = (item as any).offsetLeft + ((item as any).offsetWidth / 2) - (c as any).offsetWidth / 2 +} + +export const focus_day = function (journey: journey_wrapper, idx: number | null = null) { + const c = document.querySelector('.scroll-content.nav-day')!! + console.log(idx, c, journey) + const item = c.children[(idx != null ? idx : journey.sel_day) + 1]; + c.scrollLeft = (item as any).offsetLeft + ((item as any).offsetWidth / 2) - (c as any).offsetWidth / 2; + //focus_leg(journey) // We dont render both navs anymore +} + +export const nav_mousemove = function (e: PointerEvent) { + if (e.pointerType != 'mouse') return; + const c = (e.target as any).closest('.scroll-content') || (e.target as any).firstChild; + const left = e.pageX - c.getBoundingClientRect().left; + const newDir = + left < c.offsetWidth * 0.1 ? 'left' : + (left > c.offsetWidth * 0.9 ? 'right' : 'none') + if (!nav.scrollInterval || nav.scrollDir != newDir) { + if (nav.scrollInterval) clearInterval(nav.scrollInterval) + sideScroll(c, newDir, 25, 10); + } +} +export const nav_mouseleave = function (_e: PointerEvent) { + clearInterval(nav.scrollInterval); + nav.scrollDir = 'none' + nav.scrollInterval = 0 +} + diff --git a/src/client/old.js b/src/client/old.js index 17ac0ee..93aa681 100644 --- a/src/client/old.js +++ b/src/client/old.js @@ -2,8 +2,10 @@ import * as api from "./api"; import journey_wrapper from "./types/wrapper"; import { migrator } from "./types/migration"; -import { journey_add_place, journey_del_place } from "./journey_helper"; -import { filter_existing, process_results } from "./api_helper"; +import { journey_add_place, journey_del_place } from "./helper/journey"; +import { search_nominatim, search_flight } from "./helper/api"; +import { focus_day, focus_leg, nav_mouseleave, nav_mousemove } from "./helper/nav"; +import { set_search_set_results } from "./helper/api"; Vue.component("l-map", window.Vue2Leaflet.LMap); Vue.component("l-tile-layer", window.Vue2Leaflet.LTileLayer); @@ -23,20 +25,16 @@ const app = new Vue({ query: { type: "", query: "", res: [], load: false, sub: false, note: false, drawer: false, addmarker: false, }, - nav: { - scrollInterval: null, - scrollDir: null, - useDay: false, - }, + useDay: false, toast: { - show: true, - title: 'Confirm deletion', - desc: 'Are you sure ? This is irreversible...', + show: false, + title: '', + desc: '', special: '', - acceptText: 'delete', - cancelText: 'cancel', + acceptText: '', + cancelText: '', + func: () => { }, }, - impexp: "", lang: { format: "ddd D MMM", formatLocale: { @@ -74,11 +72,10 @@ const app = new Vue({ return ``; }, - - import_data: function () { + import_data: function (v) { this.journey.data = Object.assign( {}, - JSON.parse(this.impexp.toDecoded()), + JSON.parse(v.toDecoded()), ); this.journey.data.main.forEach((e) => { if (e.date_range) { @@ -87,10 +84,26 @@ const app = new Vue({ } }); }, - export_data: function () { - this.impexp = JSON.stringify(this.journey.data).toEncoded(); + + toast_reset: function () { + this.toast.show = false; + this.toast.title = ''; + this.toast.desc = '' + this.toast.special = ''; + this.toast.acceptText = '' + this.toast.cancelText = '' + this.toast.func = () => { } }, + toast_impexp: function (is_import) { + this.toast.show = true; + this.toast.title = is_import ? 'Import' : 'Export'; + this.toast.desc = '' + this.toast.special = JSON.stringify(this.journey.data).toEncoded(); + this.toast.acceptText = is_import ? 'load' : '' + this.toast.cancelText = 'cancel' + this.toast.func = () => { this.import_data(this.toast.special); this.toast_reset(); } + }, search_set_results(r) { this.query.load = false; @@ -114,21 +127,6 @@ const app = new Vue({ setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); }, - search_nominatim: function (f) { - return (q) => api.query_nominatim(q, this.compute_bb(), api.get_filter(f)).catch((_err) => []).then((r) => { - r = process_results('nominatim', r) - r = filter_existing('nominatim', this.journey.leg_get(), r) - return this.search_set_results(r) - }); - }, - search_travel: function (f) { - return (q) => api.query_flight(q).then((r) => { - r = process_results('travel', r) - r = filter_existing('travel', this.journey.leg_get(), r) - return this.search_set_results(r) - }); - }, - drawer_hover_item: function (item) { if (item) { this.map_override.active = true @@ -143,88 +141,46 @@ const app = new Vue({ }, drawer_click_item: function (item) { - const tpe = this.query.type; this.search_set_clear(item ? true : false); this.drawer_hover_item(); if (item) { item.step = -1; - journey_add_place(this.journey, tpe, item) + journey_add_place(this.journey, this.query.type, item) } }, - place_delete(tpe, idx) { - journey_del_place(this.journey, tpe, idx) - }, + search_active: function (_e) { + const tpe = this.query.type; + const query = this.query.query; + switch (tpe) { + case 'hotel': + case 'restaurant': ; + case 'place': + case 'other': + return search_nominatim(tpe, query, this.compute_bb(), this.journey.leg_get()) - search_active: function (q) { - const txt = q.target.value - switch (this.query.type) { - case 'hotel': return this.search_hotel(txt); - case 'restaurant': return this.search_restaurant(txt); - case 'place': return this.search_place(txt); - case 'other': return this.search_other(txt); - case 'flight': return this.search_flight(txt); + case 'flight': + return search_flight(tpe, query, this.journey.leg_get()); } }, search_enable: function (f) { const is_notes = f == 'notes'; this.search_set_active(is_notes, f) - const query_in = document.getElementById(is_notes ? 'query_note' : 'query_input') - setTimeout(() => query_in.focus(), 500); - if (!is_notes) this.search_active({ target: query_in }) + setTimeout(() => document.getElementById(is_notes ? 'query_note' : 'query_input').focus(), 500); + if (!is_notes) this.search_active() }, - sideScroll: function (element, direction, speed, step) { - this.nav.scrollDir = direction - if (direction == 'none') return; - this.nav.scrollInterval = setInterval(() => { - element.scrollLeft += (direction == 'left') ? -step : step; - }, speed); - }, - - focus_leg(idx) { - const c = document.querySelector('.scroll-content.nav-leg') - const item = c.children[(idx != undefined ? idx : this.journey.sel_leg) + 1]; - c.scrollLeft = item.offsetLeft + (item.offsetWidth / 2) - c.offsetWidth / 2 - }, - focus_day(idx) { - const c = document.querySelector('.scroll-content.nav-day') - const item = c.children[(idx != undefined ? idx : this.journey.sel_day) + 1]; - c.scrollLeft = item.offsetLeft + (item.offsetWidth / 2) - c.offsetWidth / 2; - this.focus_leg() - }, - - nav_mousemove(e) { - if (e.pointerType != 'mouse') return; - const c = e.target.closest('.scroll-content') || e.target.firstChild; - const left = e.pageX - c.getBoundingClientRect().left; - const newDir = - left < c.offsetWidth * 0.1 ? 'left' : - (left > c.offsetWidth * 0.9 ? 'right' : 'none') - if (!this.nav.scrollInterval || this.nav.scrollDir != newDir) { - if (this.nav.scrollInterval) clearInterval(this.nav.scrollInterval) - this.sideScroll(c, newDir, 25, 10); - } - }, - nav_mouseleave(e) { - clearInterval(this.nav.scrollInterval); - this.nav.scrollDir = 'none' - this.nav.scrollInterval = null - }, - - refreshTextAreaHeight(event) { - event.target.style['height'] = 'auto'; - event.target.style['height'] = event.target.scrollHeight + 'px'; - event.target.style['max-height'] = "100%"; + refreshTextAreaHeight: function (e) { + e.target.style['height'] = 'auto'; + e.target.style['height'] = e.target.scrollHeight + 'px'; + e.target.style['max-height'] = "100%"; }, onMapClick(e) { if (this.query.addmarker) { const newMarker = { latlon: [e.latlng.lat, e.latlng.lng], - lat: e.latlng.lat, - lon: e.latlng.lng, sname: this.query.query, type: this.query.type, }; @@ -233,34 +189,31 @@ const app = new Vue({ }, }, created: function () { - this.search_hotel = api.throttle(this.search_nominatim("hotel"), 1000) - this.search_restaurant = api.throttle(this.search_nominatim("restaurant"), 1000) - this.search_place = api.throttle(this.search_nominatim("place"), 1000) - this.search_flight = api.throttle(this.search_travel("flight"), 2000) + set_search_set_results(this.search_set_results); + this.nav_mouseleave = nav_mouseleave; + this.nav_mousemove = nav_mousemove; + this.focus_day = focus_day; + this.focus_leg = focus_leg; + this.place_delete = (tpe, idx) => journey_del_place(this.journey, tpe, idx); + this.save_data = api.throttle(() => { - this.impexp = JSON.stringify(this.journey.data).toEncoded(); api.save(this.journey.id, this.journey.data); }, 1000); this.pressed_prev = api.throttle(() => { - if (this.nav.useDay) this.journey.day_prev(); + if (this.useDay) this.journey.day_prev(); else this.journey.leg_prev(); - }, 200) + }, 250) this.pressed_next = api.throttle(() => { - if (this.nav.useDay) this.journey.day_next(); + if (this.useDay) this.journey.day_next(); else this.journey.leg_next(); - }, 200) + }, 250) window.addEventListener("keydown", (e) => { switch (e.key) { - case "ArrowLeft": - this.pressed_prev() - break; - case "ArrowRight": - this.pressed_next() - break; - default: - console.log(e.key); + case "ArrowLeft": return this.pressed_prev() + case "ArrowRight": return this.pressed_next() + default: return console.log(e.key); } }); @@ -271,8 +224,7 @@ const app = new Vue({ watch: { journey: { handler: function (ndata, odata) { - if (this.edit_active) - this.save_data(); + if (this.edit_active) this.save_data(); }, deep: true, }, diff --git a/src/style/module/general.css b/src/style/module/general.css index 88a2419..9529345 100644 --- a/src/style/module/general.css +++ b/src/style/module/general.css @@ -1,3 +1,7 @@ +* { + box-sizing: border-box; +} + html, body, body, diff --git a/src/template/module/journey/impexp.pug b/src/template/module/journey/impexp.pug deleted file mode 100644 index 6e0c3a5..0000000 --- a/src/template/module/journey/impexp.pug +++ /dev/null @@ -1,13 +0,0 @@ -.impexp - .container-medium.section - .aligner - .input.col-sm-4 - input(v-model="impexp", type="text") - .col-sm-2 - button.button.button--primary.button--mobileFull( - v-on:click="import_data" - ) Import - .col-sm-2 - button.button.button--primary.button--mobileFull( - v-on:click="export_data" - ) Export \ No newline at end of file diff --git a/src/template/module/journey/leg/drawer.pug b/src/template/module/journey/leg/drawer.pug index fd3e544..a90d0a7 100644 --- a/src/template/module/journey/leg/drawer.pug +++ b/src/template/module/journey/leg/drawer.pug @@ -42,7 +42,7 @@ div(v-else-if="['flight'].indexOf(query.type)>=0") div( v-html="generate_icon('plane', 'var(--dark)')") .col-10() | {{ item.from }} => {{item.to}} - bg-dark.divider( + .bg-dark.divider( :key="'qdiv'+idx" style="height:1px" ) div(v-else) template() diff --git a/src/template/module/journey/leg/top_day.pug b/src/template/module/journey/leg/top_day.pug index d8833c3..8e11ac6 100644 --- a/src/template/module/journey/leg/top_day.pug +++ b/src/template/module/journey/leg/top_day.pug @@ -1,7 +1,7 @@ .row .mr-auto.col-auto .list-group.text-dark - .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.day_prev(); focus_day();") + .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.day_prev(); focus_day(journey);") i.fas.fa-angle-left .col-8.col-sm-8.col-md-10.scroll-handler( @pointerleave="nav_mouseleave" @@ -37,7 +37,7 @@ .ml-auto.col-auto .list-group.text-dark - .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.day_next(); focus_day();") + .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.day_next(); focus_day(journey);") i.fas.fa-angle-right .row .col-8.col-sm-6.col-md-4 diff --git a/src/template/module/journey/leg/top_leg.pug b/src/template/module/journey/leg/top_leg.pug index 7295fbc..f5dc1e4 100644 --- a/src/template/module/journey/leg/top_leg.pug +++ b/src/template/module/journey/leg/top_leg.pug @@ -1,7 +1,7 @@ .row .mr-auto.col-auto .list-group.text-dark - .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.leg_prev(); focus_leg();") + .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.leg_prev(); focus_leg(journey);") i.fas.fa-angle-left .col-8.col-sm-8.col-md-10.scroll-handler( @pointerleave="nav_mouseleave" @@ -26,14 +26,14 @@ .text {{ element.title || "Leg "+idx}} i.fa.fa-times.text-small.fright( style="top: 2px; right: 2px; position: absolute", - @click="journey.rm_leg(idx); focus_leg();" + @click="journey.rm_leg(idx); focus_leg(journey);" ) .list-group-item.bg-white.text-white.show.placeholder-right( :class="journey.sel_leg < journey.data.main.length-1? '': 'bg-dark'" slot="footer" ) .text {{'⋅'}} - .list-group-item.bg-white.text-dark.add(@click="journey.add_leg(); focus_leg();" + .list-group-item.bg-white.text-dark.add(@click="journey.add_leg(); focus_leg(journey);" slot="footer" :class="journey.sel_leg >= journey.data.main.length-1? 'show': ''") div @@ -41,7 +41,7 @@ .ml-auto.col-auto .list-group.text-dark - .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.leg_next(); focus_leg();") + .list-group-item.show.bg-white.rounded(v-on:click.prevent="journey.leg_next(); focus_leg(journey);") i.fas.fa-angle-right .row.text-center .col-6.col-sm-6.col-md-5.mr-auto diff --git a/src/template/module/journey/main.pug b/src/template/module/journey/main.pug index bf703cc..75bc6f8 100644 --- a/src/template/module/journey/main.pug +++ b/src/template/module/journey/main.pug @@ -6,12 +6,12 @@ include smallbar.pug .col-6.col-sm-4.col-md-3 .input.text-big input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text") - .col-6.col-sm-4.col-md-3( v-if="nav.useDay" ) + .col-6.col-sm-4.col-md-3( v-if="useDay" ) .input.text-small input.text-center(v-model="journey.leg_get().title" placeholder="Leg" type="text") //- input.small(type="text", :placeholder="journey.date_tot() + ' (' + journey.tot_len() + ')'" ) - template(v-if="nav.useDay") + template(v-if="useDay") include leg/top_day.pug template(v-else) include leg/top_leg.pug @@ -23,5 +23,3 @@ include smallbar.pug include leg/drawer.pug .h-100(:class="{ 'w-100': query.note, 'w-0': !query.note }") include leg/drawer-notes.pug - -//- include impexp.pug \ No newline at end of file diff --git a/src/template/module/journey/map/override.pug b/src/template/module/journey/map/override.pug index 5fe6755..c8ea10f 100644 --- a/src/template/module/journey/map/override.pug +++ b/src/template/module/journey/map/override.pug @@ -1,7 +1,7 @@ l-marker( v-if="map_override.active", v-for="(el, idx) in map_override.elements" - key="'ovr'+idx" + :key="'ovr'+idx" :lat-lng="el" ) l-icon(v-html="generate_marker('plus', 'darkgreen')") diff --git a/src/template/module/journey/smallbar.pug b/src/template/module/journey/smallbar.pug index 4ac4dfc..8c4a049 100644 --- a/src/template/module/journey/smallbar.pug +++ b/src/template/module/journey/smallbar.pug @@ -14,17 +14,17 @@ .tooltip-text Editor .col-1 .col-1 - a.text-white.tooltip(v-on:click.prevent="import_data") + a.text-white.tooltip(v-on:click.prevent="toast_impexp(true)") i.fas.fa-file-import .tooltip-text Import .col-1 - a.text-white.tooltip(v-on:click.prevent="export_data") + a.text-white.tooltip(v-on:click.prevent="toast_impexp(false)") i.fas.fa-file-export .tooltip-text Export .col-auto.ml-auto .switch.legday-switch - input(type="checkbox" v-model="nav.useDay") + input(type="checkbox" v-model="useDay") .rocker .rocker-left LEG .rocker-right DAY \ No newline at end of file diff --git a/src/template/module/view/toast.pug b/src/template/module/view/toast.pug index 57279d6..924d405 100644 --- a/src/template/module/view/toast.pug +++ b/src/template/module/view/toast.pug @@ -3,8 +3,12 @@ template .popup.bg-white(@click.stop) .row .col-auto.text-huge {{ toast.title }} - .row + .row(v-if="toast.desc") .col-auto.text-medium {{ toast.desc }} + .row(v-if="toast.special") + .col-auto.text-medium + .input + input(v-model="toast.special") .row.align - button.button.bg-white(@click="toast-accept" v-if="toast.acceptText") {{ toast.acceptText }} + button.button.bg-white(@click="toast.func" v-if="toast.acceptText") {{ toast.acceptText }} button.button.bg-white(@click="toast.show=false;" v-if="toast.cancelText") {{ toast.cancelText }} \ No newline at end of file