diff --git a/package.json b/package.json index 1362059..08e99a6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build-client": "esbuild src/client/main.ts --outfile=public/main.js --tree-shaking=true --bundle --minify --sourcemap --tsconfig=tsconfig-client.json", "build-server": "esbuild src/server/**/*.ts --outdir=build --platform=node --format=cjs", "start": "node build/main.js", - "demon": "nodemon -e ts,js --watch src --watch template --watch router --exec \"yarn build && yarn start\"" + "demon": "nodemon -e ts,js,css --watch src --watch template --watch router --exec \"yarn build && yarn start\"" }, "repository": { "type": "git", @@ -30,4 +30,4 @@ "pug": "^3.0.2", "undici": "^7.3.0" } -} +} \ No newline at end of file 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 a1dee6b..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); @@ -141,7 +141,7 @@ export const icon_type = (item: string | NominatimResult): string => { "nature_reserve", ], "dice-five": ["water_park", "theme_park", "casino"], - "": ["?", "neighbourhood", "quarter", "highway"], + "": ["?", "neighbourhood", "quarter", "highway", "place"], }; for (let k in types) { @@ -150,3 +150,13 @@ export const icon_type = (item: string | NominatimResult): string => { console.log(item.display_name, item.category, item.type); return "question"; }; + +export const get_filter = function (f: string) { + switch (f) { + case "hotel": return is_hotel_type; + case "restaurant": return is_restauration_type; + case "place": return is_attraction_type; + case "other": + default: return () => true; + } +} diff --git a/src/client/helper/api.ts b/src/client/helper/api.ts new file mode 100644 index 0000000..08e1c85 --- /dev/null +++ b/src/client/helper/api.ts @@ -0,0 +1,61 @@ + +import { getGeoLine } from "../types/geom"; +import * as api from "../api"; + +const filter_existing = function (tpe: "nominatim" | "travel", leg: leg, r: geoloc[]) { + switch (tpe) { + case 'nominatim': + return r.filter(e => { + if (leg.hotel && leg.hotel.osm_id == e.osm_id) return false; + if (leg.places.restaurants.find(i => i.osm_id == e.osm_id)) return false; + if (leg.places.activities.find(i => i.osm_id == e.osm_id)) return false; + return true + }) + case 'travel': + console.log(r) + return r.filter(e => { + if (leg.travel.find(i => `${(e as any).from}->${(e as any).to}` == `${(i as any).from}->${(i as any).to}`)) return false; + return true + }) + } +} + +const process_results = function (tpe: "nominatim" | "travel", r: geoloc[]) { + switch (tpe) { + case 'nominatim': + return r.map((rr) => { + rr.latlon = [parseFloat((rr as any).lat), parseFloat((rr as any).lon)]; + rr.title = (rr as any).display_name.split(",")[0]; + return rr; + }); + case 'travel': + console.log(r) + return r.map(el => { + (el as any).path = getGeoLine( + { lat: (el as any).from_geo.lat, lng: (el as any).from_geo.lon }, + { lat: (el as any).to_geo.lat, lng: (el as any).to_geo.lon }, { dist: 2_500_000 }).map(v => [v.lat, v.lng]); + (el as any).type = "flight"; + return el; + }); + } +} + +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/helper/journey.ts b/src/client/helper/journey.ts new file mode 100644 index 0000000..a919a2e --- /dev/null +++ b/src/client/helper/journey.ts @@ -0,0 +1,41 @@ +import journey_wrapper from './types/wrapper'; + +/* LIST HELPERS */ +export const filter_selected = function (journey: journey_wrapper, list: geoloc[], step: boolean) { + return list.filter((e) => + step ? e.step == journey.sel_day : e.step >= 0, + ); +} + +export const filter_unselected = function (list: geoloc[]) { + return list.filter((e) => e.step == undefined || e.step < 0); +} + +export const remove_item = function (list: geoloc[], idx: number) { + list[idx].step = -1; + list.splice(idx, 1); +} + + +/* JOURNEY ADD/RM ITEM HELPER */ +export const journey_add_place = function (journey: journey_wrapper, tpe: String, item: geoloc) { + switch (tpe) { + case 'hotel': return journey.leg_get().hotel = item; + case 'restaurant': return journey.leg_get().places.restaurants.push(item); + case 'place': return journey.leg_get().places.activities.push(item); + case 'other': return; + case 'flight': return journey.leg_get().travel.push(item); + } +} + +export const journey_del_place = function (journey: journey_wrapper, tpe: String, idx: number) { + console.log(tpe) + switch (tpe) { + case "hotel": return journey.leg_get().hotel = null; + case "restaurants": return journey.leg_get().places.restaurants.splice(idx, 1); + case "activities": return journey.leg_get().places.activities.splice(idx, 1); + case "other": return; + case "flight": return journey.leg_get().travel.splice(idx, 1); + default: return true; + } +} \ No newline at end of file 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 419b1dd..93aa681 100644 --- a/src/client/old.js +++ b/src/client/old.js @@ -2,7 +2,10 @@ import * as api from "./api"; import journey_wrapper from "./types/wrapper"; import { migrator } from "./types/migration"; -import { getGeoLine } from "./types/geom"; +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); @@ -20,13 +23,18 @@ const app = new Vue({ journey: new journey_wrapper(window.location.pathname.split("/").pop() || String.gen_id(16)), map_override: { active: false, elements: [] }, query: { - type: "", res: [], load: false, sub: false, note: false, drawer: false, + type: "", query: "", res: [], load: false, sub: false, note: false, drawer: false, addmarker: false, }, - leg_nav: { - scrollInterval: null, - scrollDir: null + useDay: false, + toast: { + show: false, + title: '', + desc: '', + special: '', + acceptText: '', + cancelText: '', + func: () => { }, }, - impexp: "", lang: { format: "ddd D MMM", formatLocale: { @@ -64,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) { @@ -77,77 +84,47 @@ const app = new Vue({ } }); }, - export_data: function () { - this.impexp = JSON.stringify(this.journey.data).toEncoded(); - }, - filter_selected: function (list, step) { - return list.filter((e) => - step ? e.step == this.journey.sel_day : e.step >= 0, - ); - }, - filter_unselected: function (list) { - return list.filter((e) => e.step == undefined || e.step < 0); - }, - remove_item: function (list, idx) { - list[idx].step = -1; - list.splice(idx, 1); - }, - log: function (e) { - console.log(e); - }, - place_delete: function (f, idx) { - switch (f) { - case "hotel": return this.journey.leg_get().hotel = null; - case "restaurant": return this.journey.leg_get().places.restaurants.splice(idx, 1); - case "activities": return this.journey.leg_get().places.activities.splice(idx, 1); - case "other": return; - case "flight": return this.journey.leg_get().travel.splice(idx, 1); - default: return true; - } - }, - get_filter: function (f) { - switch (f) { - case "hotel": return api.is_hotel_type; - case "restaurant": return api.is_restauration_type; - case "place": return api.is_attraction_type; - case "other": - default: return () => true; - } + + 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 = () => { } }, - search_nominatim: function (f) { - return (q) => api.query_nominatim(q, this.compute_bb(), this.get_filter(f)).catch((_err) => []).then((r) => { - r.forEach((rr) => { - rr.latlon = [parseFloat(rr.lat), parseFloat(rr.lon)]; - rr.sname = rr.display_name.split(",")[0]; - }); - r = r.filter(e => { - if (this.journey.leg_get().hotel && this.journey.leg_get().hotel.osm_id == e.osm_id) return false; - if (this.journey.leg_get().places.restaurants.find(i => i.osm_id == e.osm_id)) return false; - if (this.journey.leg_get().places.activities.find(i => i.osm_id == e.osm_id)) return false; - return true - }) - this.query.load = false; - this.query.res = r; - return r - }); + 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_travel: function (f) { - return (q) => api.query_flight(q).then((r) => { - r.forEach(el => { - el.path = getGeoLine( - { lat: el.from_geo.lat, lng: el.from_geo.lon }, - { lat: el.to_geo.lat, lng: el.to_geo.lon }, { dist: 2_500_000 }).map(v => [v.lat, v.lng]) - el.type = "flight"; - }); - r = r.filter(e => { - if (this.journey.leg_get().travel.find(i => `${i.from}->${i.to}` == `${e.from}->${e.to}`)) return false; - return true - }) - this.query.load = false; - this.query.res = r; - return r; - }); + + search_set_results(r) { + this.query.load = false; + this.query.res = r; + return r + }, + search_set_clear(keep_drawer) { + this.query.res = []; + this.query.note = false; + this.query.type = null; + this.query.addmarker = false; + this.query.sub = false; + this.query.drawer = keep_drawer; + setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); + }, + search_set_active: function (is_notes, tpe) { + this.query.drawer = true; + this.query.note = is_notes; + this.query.type = !is_notes ? tpe : null; + this.query.load = !is_notes; + setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); }, drawer_hover_item: function (item) { @@ -164,112 +141,79 @@ const app = new Vue({ }, drawer_click_item: function (item) { - const tpe = this.query.type; - this.query.res = []; - this.query.note = false; - this.query.type = null; - this.query.drawer = item ? true : false; - setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); - this.query.sub = false; - this.drawer_hover_item() + this.search_set_clear(item ? true : false); + this.drawer_hover_item(); if (item) { item.step = -1; - switch (tpe) { - case 'hotel': return this.journey.leg_get().hotel = item; - case 'restaurant': return this.journey.leg_get().places.restaurants.push(item); - case 'place': return this.journey.leg_get().places.activities.push(item); - case 'other': return; - case 'flight': return this.journey.leg_get().travel.push(item); - } + journey_add_place(this.journey, this.query.type, item) } }, - search_active: function (q) { - const txt = q.target.value - this.query.load = true; - 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); + 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()) + + case 'flight': + return search_flight(tpe, query, this.journey.leg_get()); } }, search_enable: function (f) { - this.query.drawer = true; - setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); - if (f == "notes") { - this.query.note = true; - this.query.type = null; - const query_in = document.getElementById('query_note') - setTimeout(() => query_in.focus(), 500); - return; + const is_notes = f == 'notes'; + this.search_set_active(is_notes, f) + setTimeout(() => document.getElementById(is_notes ? 'query_note' : 'query_input').focus(), 500); + if (!is_notes) this.search_active() + }, + + 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], + sname: this.query.query, + type: this.query.type, + }; + this.drawer_click_item(newMarker) } - this.query.note = false; - this.query.type = f; - const query_in = document.getElementById('query_input') - setTimeout(() => query_in.focus(), 500); - this.search_active({ target: query_in }) - }, - - - sideScroll: function (element, direction, speed, step) { - this.leg_nav.scrollDir = direction - if (direction == 'none') return; - this.leg_nav.scrollInterval = setInterval(() => { - element.scrollLeft += (direction == 'left') ? -step : step; - }, speed); - }, - - keyboardEvent(e) { - if (e.which === 13) { - } - }, - nav_mousemove(e) { - const c = document.querySelector('.scroll-content') - const left = e.pageX - c.getBoundingClientRect().left; - const newDir = - left < c.offsetWidth * 0.1 ? 'left' : - (left > c.offsetWidth * 0.9 ? 'right' : 'none') - if (!this.leg_nav.scrollInterval || this.leg_nav.scrollDir != newDir) { - if (this.leg_nav.scrollInterval) clearInterval(this.leg_nav.scrollInterval) - this.sideScroll(c, newDir, 25, 10); - } - }, - nav_mouseleave(e) { - clearInterval(this.leg_nav.scrollInterval); - this.leg_nav.scrollDir = 'none' - this.leg_nav.scrollInterval = null - }, - - refreshTextAreaHeight(event) { - console.log("AAA", event.target.scrollHeight, event.target) - event.target.style['height'] = 'auto'; - event.target.style['height'] = event.target.scrollHeight + 'px'; - event.target.style['max-height'] = "100%"; }, }, 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) + 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.search_flight = api.throttle(this.search_travel("flight"), 2000) + + this.pressed_prev = api.throttle(() => { + if (this.useDay) this.journey.day_prev(); + else this.journey.leg_prev(); + }, 250) + this.pressed_next = api.throttle(() => { + if (this.useDay) this.journey.day_next(); + else this.journey.leg_next(); + }, 250) window.addEventListener("keydown", (e) => { switch (e.key) { - case "ArrowLeft": - this.journey.day_prev(); - break; - case "ArrowRight": - this.journey.day_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); } }); @@ -280,12 +224,9 @@ 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/client/types/format.ts b/src/client/types/format.ts index a0f1ff9..d89a1e6 100644 --- a/src/client/types/format.ts +++ b/src/client/types/format.ts @@ -6,6 +6,8 @@ declare global { lng: number } interface geoloc { + title: string + osm_id: number latlon: [number, number] notes: string step: -1 @@ -22,7 +24,7 @@ declare global { day_title: string[] date_range: [Date, Date] | null map: map - travel: unknown[] + travel: geoloc[] hotel: geoloc | null places: { restaurants: geoloc[] diff --git a/src/client/types/migration.ts b/src/client/types/migration.ts index 2ccf0c0..14f8963 100644 --- a/src/client/types/migration.ts +++ b/src/client/types/migration.ts @@ -8,6 +8,7 @@ function migrate_A_to_0(e: journey): journey { v.day_title = v.day_title || (v as any).step_title; v.places.activities = v.places.activities || (v as any).places.places; v.travel = v.travel || []; + v.day_title = typeof (v.day_title) == "string" ? [v.day_title] : [] }) console.log(e) return e; diff --git a/src/client/types/wrapper.ts b/src/client/types/wrapper.ts index ade7429..da59ac5 100644 --- a/src/client/types/wrapper.ts +++ b/src/client/types/wrapper.ts @@ -1,3 +1,4 @@ +import './ext.ts' import { journey_template, leg_template } from "./format" const date_day_diff = (d0: Date, d1: Date): number => @@ -56,6 +57,10 @@ class journey_wrapper { this.sel_leg = Math.max(this.sel_leg - 1, 0); this.sel_day = 0; } + + day_sel(idx: number): void { + this.sel_day = idx; + } day_next() { this.sel_day += 1 if (this.sel_day > this.leg_len() - 1) { @@ -78,6 +83,13 @@ class journey_wrapper { } } } + day_cycle() { + if (this.sel_day >= this.leg_len() - 1) { + this.sel_day = 0; + } else { + this.day_next() + } + } date_sel(): string { if (this.sel_day < 0) return "?"; let leg = this.leg_get() diff --git a/src/style/custom.css b/src/style/custom.css index e9b5b85..feeebad 100644 --- a/src/style/custom.css +++ b/src/style/custom.css @@ -1,3 +1,15 @@ +body { + background: var(--darkdark); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.leaflet-control-attribution, .leaflet-popup-close-button { visibility: hidden; } @@ -6,30 +18,6 @@ position: absolute; } -.list-group { - overflow: auto; - white-space: nowrap; - scrollbar-width: none; - padding: 1rem 0rem; -} - -.list-group-item { - border: 1px solid var(--darkdark); - display: inline-block; - position: relative; - cursor: pointer; - text-align: center; - padding: 0.5rem 0.8rem; -} - -.list-group-item:hover { - filter: brightness(85%); -} - -.leaflet-control-attribution { - display: none; -} - .display-none { display: none; } @@ -54,7 +42,7 @@ height: 100%; right: 0; top: 0; - overflow: scroll; + margin-left: 0; } .travel-path-icon { @@ -62,6 +50,9 @@ margin-top: -32px; } +.input { + max-height: 100%; +} .ml-auto { margin-left: auto; @@ -79,6 +70,20 @@ height: 100%; } +.h-0 { + height: 0%; + overflow: hidden; +} + +.w-100 { + width: 100%; +} + +.w-0 { + width: 0%; + overflow: hidden; +} + .map-menu { position: absolute; display: flex; @@ -113,7 +118,7 @@ } .map-menu-item:hover { - filter: brightness(150%); + background-color: var(--lightdark); } .map-menu-sub { @@ -141,14 +146,8 @@ .leaflet-popup-content { margin: 10px 20px !important; - /* display: flex; - align-items: center; - justify-content: center; - width: auto !important; - max-width: 100%; */ } - .leaflet-popup>.leaflet-popup-content-wrapper { border-radius: var(--border-radius); width: 350px; @@ -188,7 +187,36 @@ filter: brightness(85%); } -.scroll-content>div:first-child { +.scroll-content.list-group>.list-group-item { + display: none; +} + +.list-group-item.placeholder-left.show.bg-dark, +.list-group-item.placeholder-right.show.bg-dark { + background: var(--darkdark); +} + +.list-group-item.placeholder-left.show { + margin-left: auto; + color: transparent; + cursor: default; + background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, var(--white) 50%); +} + +.list-group-item.placeholder-right.show { + margin-right: auto; + color: transparent; + cursor: default; + background: linear-gradient(to left, rgba(0, 0, 0, 0) 0%, var(--white) 50%); +} + + +.scroll-content>.list-group-item.active, +.scroll-content>.list-group-item.show { + display: inline-block; +} + +.scroll-content>div:nth-child(2) { border-top-left-radius: var(--border-radius); border-bottom-left-radius: var(--border-radius); } @@ -202,6 +230,137 @@ border-radius: var(--border-radius); } +.list-group-item.add { + position: absolute; + right: 0; +} + +@media (min-width: 768px) { + .scroll-content.list-group>.list-group-item { + display: inline-block; + } + + .scroll-content>.list-group-item.placeholder-left, + .scroll-content>.list-group-item.placeholder-right { + display: none; + } + + .list-group-item.add { + position: relative; + right: 0; + margin-left: 1em; + } +} + +.list-group { + display: flex; + overflow: auto; + white-space: nowrap; + scrollbar-width: none; + padding: 1rem 0rem; +} + +.list-group-item>.text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.list-group-item.show { + min-width: 24px; +} + +.list-group-item { + border: 1px solid var(--darkdark); + display: inline-block; + position: relative; + cursor: pointer; + text-align: center; + padding: .5rem .5rem; + max-width: calc(100% - 48px); + display: inline-block; +} + +.list-group-item.bg-white:hover { + background: var(--blue); + /* filter: brightness(85%); */ +} + .mx-datepicker { width: 100% !important; +} + +.overlay { + position: fixed; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 5000; +} + +.popup { + padding: 20px; + border-radius: 10px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + z-index: 5001; +} + +.button { + background-color: var(--lightlight); + border: 0; + border-radius: var(--border-radius); + color: var(--darkdark); + display: inline-block; + text-align: center; + text-decoration: none; + text-transform: uppercase; + font-weight: 600; + white-space: nowrap; + line-height: 1.2em; + padding: 8px 16px; + margin: 8px 16px; + font-weight: 600; + cursor: pointer; +} + +.tooltip { + position: relative; + display: inline-block; + cursor: pointer; +} + +.tooltip .tooltip-text { + visibility: hidden; + background-color: var(--white); + color: var(--darkdark); + text-align: center; + border-radius: var(--border-radius); + padding: 2px; + position: absolute; + z-index: 20; + bottom: 0%; + left: 0%; + margin-left: 0px; + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; +} + +.tooltip:hover .tooltip-text { + visibility: visible; + opacity: 1; +} + +.smallbar { + z-index: 10; + position: absolute; + width: 100%; + top: 0; + display: flex; + flex-flow: row; + padding: 8px; + margin: 0 -12px; } \ No newline at end of file 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/style/module/input.css b/src/style/module/input.css index 5ebba66..c38e359 100644 --- a/src/style/module/input.css +++ b/src/style/module/input.css @@ -1,3 +1,4 @@ +switch, input, textarea { -webkit-appearance: none; @@ -15,6 +16,7 @@ input:-webkit-autofill { box-shadow: 0 0 0 1000px #eceff1 inset; } +.switch, .textarea, .input, .select { @@ -167,7 +169,7 @@ input:-webkit-autofill { display: none; } -.checkbox input[type="checkbox"]:checked+label::after { +.checkbox input[type="checkbox"]:checked::after { -webkit-animation: checkboxAndRadioAnimation 0.25s; animation: checkboxAndRadioAnimation 0.25s; content: ""; @@ -175,7 +177,7 @@ input:-webkit-autofill { transform: scale(1) rotate(45deg); } -.checkbox input[type="checkbox"]+label { +.checkbox input[type="checkbox"] { display: block; overflow: hidden; padding-left: 30px; @@ -183,7 +185,7 @@ input:-webkit-autofill { white-space: nowrap; } -.checkbox input[type="checkbox"]+label::before { +.checkbox input[type="checkbox"]::before { background-color: #eceff1; border: 1px solid var(--white); border-radius: var(--border-radius); @@ -197,7 +199,7 @@ input:-webkit-autofill { width: 20px; } -.checkbox input[type="checkbox"]+label::after { +.checkbox input[type="checkbox"]::after { border-bottom: 3px solid #03a9f4; border-right: 3px solid #03a9f4; display: block; @@ -220,7 +222,7 @@ input:-webkit-autofill { display: none; } -.radio input[type="radio"]:checked+label::after { +.radio input[type="radio"]:checked::after { -webkit-animation: checkboxAndRadioAnimation 0.25s; animation: checkboxAndRadioAnimation 0.25s; content: ""; @@ -228,7 +230,7 @@ input:-webkit-autofill { transform: scale(1) rotate(45deg); } -.radio input[type="radio"]+label { +.radio input[type="radio"] { display: block; overflow: hidden; padding-left: 30px; @@ -236,7 +238,7 @@ input:-webkit-autofill { white-space: nowrap; } -.radio input[type="radio"]+label::before { +.radio input[type="radio"]::before { background-color: #eceff1; border: 1px solid var(--white); border-radius: 20px; @@ -250,7 +252,7 @@ input:-webkit-autofill { width: 20px; } -.radio input[type="radio"]+label::after { +.radio input[type="radio"]::after { background-color: #03a9f4; border-radius: 20px; display: block; @@ -304,6 +306,7 @@ input:-webkit-autofill { border: 0 !important; } +.switch, .input { background-color: var(--white); padding: 0; @@ -316,6 +319,7 @@ input:-webkit-autofill { border-radius: var(--border-radius); } +.switch .rocker, .input input, .input textarea { background: transparent; @@ -359,4 +363,62 @@ input:-webkit-autofill { right: 16px; top: 50%; width: 16px; +} + +.switch { + overflow: hidden; + display: block; +} + +.switch input[type="checkbox"] { + position: absolute; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + z-index: 3; +} + +.switch .rocker { + padding: 0; + display: flex; +} + +.switch .rocker-left, +.switch .rocker-right { + max-width: 50%; + font-weight: bold; + text-align: center; + font-size: 10px; + line-height: 1; + width: 50%; + color: var(--darkdark); + padding: 8px 16px; + -webkit-transition: background-position .25s; + -moz-transition: background-position .25s; + transition: background-position .25s; + background-size: 200% 100%; + background-image: linear-gradient(to right, transparent 50%, var(--blue) 50%); +} + +.switch .rocker-left { + background-position-x: 100%; +} + +.switch .rocker-bg { + position: relative; + border-radius: 50%; + transition: 0.4s cubic-bezier(0.18, 0.89, 0.35, 1.15) all; + left: 0; + width: 50%; + background-color: var(--blue); + z-index: 1; +} + +.switch input:checked+.rocker .rocker-left { + background-position-x: 0%; +} + +.switch input:checked+.rocker .rocker-right { + background-position-x: -100%; } \ No newline at end of file diff --git a/src/style/module/layout.css b/src/style/module/layout.css index 58ec6d9..3b157f1 100644 --- a/src/style/module/layout.css +++ b/src/style/module/layout.css @@ -31,6 +31,12 @@ } } +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + @media (min-width: 992px) { .container { max-width: 960px; @@ -47,7 +53,6 @@ .container { padding-left: 24px; padding-right: 24px; - max-width: 720px; } } diff --git a/src/style/module/typography.css b/src/style/module/typography.css index 96a1fa2..d403906 100644 --- a/src/style/module/typography.css +++ b/src/style/module/typography.css @@ -22,11 +22,6 @@ a:focus { color: var(--blue); } -.text-huge, -.text-big, -.text-medium { - margin-bottom: 1em; -} .text-huge { font-size: 36px; diff --git a/src/template/journey.pug b/src/template/journey.pug index 3b3e81b..7beb558 100644 --- a/src/template/journey.pug +++ b/src/template/journey.pug @@ -3,5 +3,6 @@ doctype html include module/head.pug main#app(v-cloak) + include module/view/toast.pug include module/journey/main.pug include module/foot.pug 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-notes.pug b/src/template/module/journey/leg/drawer-notes.pug index 0f4a1c7..fbce435 100644 --- a/src/template/module/journey/leg/drawer-notes.pug +++ b/src/template/module/journey/leg/drawer-notes.pug @@ -1,8 +1,9 @@ -.col-12.input.text-dark() +.w-100.input() textarea.text-small#query_note( v-model="journey.leg_get().notes" + rows="1" @input="refreshTextAreaHeight" @focus="refreshTextAreaHeight" - rows="1" placeholder="...", - ) \ No newline at end of file + ) + \ 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 cc30f3e..a90d0a7 100644 --- a/src/template/module/journey/leg/drawer.pug +++ b/src/template/module/journey/leg/drawer.pug @@ -1,5 +1,5 @@ -.col-12.input.text-dark +.input.w-100.text-dark input#query_input( type="search" @input="search_active" @@ -7,6 +7,7 @@ placeholder="Search ... " style="width:85%;" :disabled="query.note" + v-model="query.query" ) .spinner(v-if="query.load") @@ -23,7 +24,8 @@ div(v-if="['hotel', 'restaurant', 'place','other', 'travel'].indexOf(query.type) .bg-dark.divider( :key="'qdiv'+idx" style="height:1px" ) .query-result.col-12.bg-white.text-dark( - v-if="query.load==false && query.res.length==0" ) + v-if="query.load==false && query.res.length>=0 && query.query!=''" + @click="query.addmarker=true" ) div( v-html="generate_icon('star', 'var(--dark)')") .col-10() | Add custom @@ -40,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/nav.pug b/src/template/module/journey/leg/nav.pug deleted file mode 100644 index 736c95e..0000000 --- a/src/template/module/journey/leg/nav.pug +++ /dev/null @@ -1,34 +0,0 @@ -.scroll-handler.row( - @mouseleave="nav_mouseleave" - @mousemove="nav_mousemove") - - .col-3.col-sm-2.col-md-1 - .list-group.text-dark.h-100 - .fleft.list-group-item.bg-white.text-small.rounded.h-100(v-on:click.prevent="journey.leg_prev()") - i.fas.fa-angle-left - .col-6.col-sm-8.col-md-10 - draggable.scroll-content.list-group.bg-dark( - tag="div", - :list="journey.data.main", - handle=".handle" - ) - .list-group-item.handle.text-dark( - v-for="(element, idx) in journey.data.main", - :key="idx", - @click="journey.leg_sel(idx)", - :class="journey.sel_leg == idx ? 'bg-primary' : 'bg-white'" - ) - .text {{ element.title || "Leg "+idx}} - i.fa.fa-times.close.fright( - style="top: 2px; right: 2px; position: absolute", - @click="journey.rm_leg(idx)" - ) - .list-group-item.bg-dark - .list-group-item.bg-white.text-dark(@click="journey.add_leg()") - div - i.fa.fa-plus.add() - - .col-3.col-sm-2.col-md-1 - .list-group.text-dark.h-100 - a.fright.list-group-item.bg-white.text-small.rounded.h-100(v-on:click.prevent="journey.leg_next()") - i.fas.fa-angle-right \ No newline at end of file diff --git a/src/template/module/journey/leg/top.pug b/src/template/module/journey/leg/top.pug deleted file mode 100644 index bd63bec..0000000 --- a/src/template/module/journey/leg/top.pug +++ /dev/null @@ -1,36 +0,0 @@ -.row.text-center.align - .col-5.col-sm-4.col-md-2 - .input - input( - placeholder="Leg" - v-model="journey.leg_get().title") - - .col-5.col-sm-4.col-md-2.mr-auto - .input - input( - placeholder="Day" - v-model="journey.leg_get().day_title[journey.sel_day]" - ) - .col-8.col-sm-6.col-md-4 - .input - //- label Date Range ({{ journey.leg_len() }}) - date-picker( - :lang="lang", - v-model="journey.leg_get().date_range", - range="", - format="ddd D MMM", - placeholder="Date Range", - v-on:change="journey.date_update(journey.sel_leg)" - ) - .col-4.col-sm-4.col-md-3.ml-auto - .input - input( - disabled="", - :value="journey.date_sel() + ' (' + journey.sel_day + ')'" - ) - //- .col-6.list-group-item.align.center.bg-white(style="padding: 0.5rem 0;") - //- i.fas.fa-angle-double-right(v-on:click.prevent="journey.day_next()") - .col-sm-1.text-small - - //- a(href="#prev", v-on:click.prevent="journey.day_prev()") - i.fas.fa-angle-left \ No newline at end of file diff --git a/src/template/module/journey/leg/top_day.pug b/src/template/module/journey/leg/top_day.pug new file mode 100644 index 0000000..8e11ac6 --- /dev/null +++ b/src/template/module/journey/leg/top_day.pug @@ -0,0 +1,54 @@ +.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(journey);") + i.fas.fa-angle-left + .col-8.col-sm-8.col-md-10.scroll-handler( + @pointerleave="nav_mouseleave" + @pointermove="nav_mousemove") + + draggable.scroll-content.nav-day.list-group.bg-dark( + tag="div", + :list="journey.leg_get().day_title", + handle=".handle" + ) + .list-group-item.bg-white.text-white.show.placeholder-left( + :class="journey.sel_day >0? '': 'bg-dark'" + slot="header" + ) + .text {{'⋅'}} + .list-group-item.handle.text-dark( + v-for="(element, idx) in journey.leg_len()", + :key="idx", + @click="journey.day_sel(idx)", + :class="journey.sel_day == idx ? 'bg-primary active' : 'bg-white'" + ) + .text {{ journey.leg_get().day_title[idx] || "Leg "+idx}} + .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-dark.text-white( + style="display:none" + slot="footer" + ) + .text {{'⋅'}} + + .ml-auto.col-auto + .list-group.text-dark + .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 + .input.w-100 + input.col-8( + placeholder="Day" + v-model="journey.leg_get().day_title[journey.sel_day]" + ) + .ml-auto.col-4.col-sm-4.col-md-3 + .input.w-100 + input.text-right( + disabled="", + :value="journey.date_sel() + ' (' + journey.sel_day + ')'" + ) diff --git a/src/template/module/journey/leg/top_leg.pug b/src/template/module/journey/leg/top_leg.pug new file mode 100644 index 0000000..f5dc1e4 --- /dev/null +++ b/src/template/module/journey/leg/top_leg.pug @@ -0,0 +1,61 @@ +.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(journey);") + i.fas.fa-angle-left + .col-8.col-sm-8.col-md-10.scroll-handler( + @pointerleave="nav_mouseleave" + @pointermove="nav_mousemove") + + draggable.scroll-content.nav-leg.list-group.bg-dark( + tag="div", + :list="journey.data.main", + handle=".handle" + ) + .list-group-item.bg-white.text-white.show.placeholder-left( + :class="journey.sel_leg >0? '': 'bg-dark'" + slot="header" + ) + .text {{'⋅'}} + .list-group-item.handle.text-dark( + v-for="(element, idx) in journey.data.main", + :key="idx", + @click="journey.leg_sel(idx)", + :class="journey.sel_leg == idx ? 'bg-primary active' : 'bg-white'" + ) + .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(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(journey);" + slot="footer" + :class="journey.sel_leg >= journey.data.main.length-1? 'show': ''") + div + i.fa.fa-plus() + + .ml-auto.col-auto + .list-group.text-dark + .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 + .input.w-100 + input( + placeholder="Leg" + v-model="journey.leg_get().title") + .col-6.col-sm-6.col-md-4 + .input.w-100 + date-picker( + :lang="lang", + v-model="journey.leg_get().date_range", + range="", + format="ddd D MMM", + placeholder="Date Range", + v-on:change="journey.date_update(journey.sel_leg)" + ) diff --git a/src/template/module/journey/main.pug b/src/template/module/journey/main.pug index fb8d244..75bc6f8 100644 --- a/src/template/module/journey/main.pug +++ b/src/template/module/journey/main.pug @@ -1,31 +1,25 @@ -.row.fleft(style="position:absolute;right:0;") - .col-1 - a(:href="'/short/' + journey.id") - i.fas.fa-file-contract - .col-1 - a(:href="'/view/' + journey.id") - i.fas.fa-camera - .col-1 - a(href="#", v-on:click.prevent="first_step") - i.fas.fa-tools - +include smallbar.pug .bg-dark.text-white(v-if="journey && journey.leg_get()") .container - .row.align(style="padding-top:45px;") - .col-6.col-sm-4.col-md-3.input.text-big - input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text") + .row.align(style="padding-top:45px;position:relative") + .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="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() + ')'" ) - include leg/nav.pug - include leg/top.pug - .row(style="aspect-ratio:1.25;") + template(v-if="useDay") + include leg/top_day.pug + template(v-else) + include leg/top_leg.pug + .row(style="aspect-ratio:1.25;min-height:432px;max-height:calc(100vh - 125px);width:calc(100% + 24px);") .map-container(:class=" { 'col-2 col-sm-5 col-md-8': query.type, 'col-2 col-sm-5 col-md-6': query.note , 'col-12': (!query.type && !query.note) }" ) include map.pug .row.drawer-container(:class="{ 'col-10 col-sm-7 col-md-4': query.type, 'col-10 col-sm-7 col-md-6': query.note, 'col-0': (!query.type && !query.note) }") - .drawer-container(:class="{ 'col-12 ': query.type, 'col-0': !query.type }") + .h-100(:class="{ 'w-100 ': query.type, 'w-0': !query.type }") include leg/drawer.pug - .drawer-container(:class="{ 'col-12': query.note, 'col-0': !query.note }") + .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.pug b/src/template/module/journey/map.pug index 5087f8a..9dc3004 100644 --- a/src/template/module/journey/map.pug +++ b/src/template/module/journey/map.pug @@ -1,15 +1,17 @@ l-map( - :zoom.sync="journey.leg_get().map.zoom", - :center.sync="journey.leg_get().map.center", + :zoom.sync="journey.leg_get().map.zoom" + :center.sync="journey.leg_get().map.center" + @click="onMapClick" style="height:100%" no-blocking-animations=true + :style="query.addmarker?'cursor:crosshair':''" ref="map" ) l-tile-layer( url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution="© OpenStreetMap contributors" ) - l-control-scale(position="bottomleft", :imperial="false", :metric="true") + l-control-scale(position="bottomleft" :imperial="false" :metric="true") include map/override.pug include map/hotel.pug 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 new file mode 100644 index 0000000..8c4a049 --- /dev/null +++ b/src/template/module/journey/smallbar.pug @@ -0,0 +1,30 @@ +.smallbar + .col-auto.mr-auto(style="display:flex; flex-flow: row") + .col-1 + a.text-white.tooltip(:href="'/short/' + journey.id") + i.fas.fa-file-contract + .tooltip-text Summary + .col-1 + a.text-white.tooltip(:href="'/view/' + journey.id") + i.fas.fa-camera + .tooltip-text Daily + .col-1 + a.text-white.tooltip(href="#", v-on:click.prevent="first_step") + i.fas.fa-tools + .tooltip-text Editor + .col-1 + .col-1 + 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="toast_impexp(false)") + i.fas.fa-file-export + .tooltip-text Export + + .col-auto.ml-auto + .switch.legday-switch + 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/short_leg.pug b/src/template/module/view/short_leg.pug index 756cabb..9ab1b76 100644 --- a/src/template/module/view/short_leg.pug +++ b/src/template/module/view/short_leg.pug @@ -11,7 +11,7 @@ ) .col-1.col-sm-2 .input.col-5.col-sm-3.text-dark - .text(disabled="" placeholder="No Hotel" :value="item.hotel?item.hotel.sname:''") {{item.hotel?item.hotel.sname:'No Hotel'}} + .text(disabled="" placeholder="No Hotel" :value="item.hotel?item.hotel.sname:''") {{(item.hotel?item.hotel.sname:'') || 'No Hotel'}} //- .row.text-center .input.col-sm-3(v-if="item.travel") div(v-for="(item, idx) in item.travel") diff --git a/src/template/module/view/toast.pug b/src/template/module/view/toast.pug new file mode 100644 index 0000000..924d405 --- /dev/null +++ b/src/template/module/view/toast.pug @@ -0,0 +1,14 @@ +template + .overlay.text-dark(v-if="toast.show" @click="") + .popup.bg-white(@click.stop) + .row + .col-auto.text-huge {{ toast.title }} + .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.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 diff --git a/src/template/short.pug b/src/template/short.pug index 61662f6..8d8c097 100644 --- a/src/template/short.pug +++ b/src/template/short.pug @@ -5,7 +5,7 @@ main#app(v-cloak) .bg-dark.text-white(v-if="journey && journey.leg_get()") .container - .row.align(style="padding-top:45px;") + .row.align(style="padding:45px;") .col-7.col-sm-5.col-md-4.input.text-big input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text" disabled) div(