Compare commits

..

91 Commits

Author SHA1 Message Date
soraefir
7efb63764c wip
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-02 00:27:49 +01:00
soraefir
52ee73a4ac wip
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-01 23:40:45 +01:00
c98be7a4e6 Update template/module/journey/map/travel.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:53:58 +01:00
48363040d3 Update template/module/journey/map/right_menu.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:53:42 +01:00
6f3b005c5c Update template/module/journey/map/restaurants.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:53:33 +01:00
832bfa485b Update template/module/journey/map/override.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:53:26 +01:00
102b873e79 Add template/module/journey/map/mixin-marker.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:53:12 +01:00
2fbf2ec3e9 Update template/module/journey/map/hotel.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:52:59 +01:00
828b2ae3cb Update template/module/journey/map/activities.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:52:49 +01:00
22f4cbda18 Update template/module/journey/leg/top.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:52:24 +01:00
8820e4990a Update template/module/journey/leg/old_cfg.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:52:14 +01:00
4a10318b74 Update template/module/journey/leg/nav.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:52:03 +01:00
d68a148b40 Update template/module/journey/leg/drawer.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:51:42 +01:00
0ece752fd4 Update template/module/journey/map.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:51:32 +01:00
6260d396e5 Update src/client/types/migration.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:50:51 +01:00
208bf1805e Update src/client/types/geom.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:50:32 +01:00
b030286f9c Update src/client/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:50:21 +01:00
3ea48d69ab Update src/client/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 17:49:59 +01:00
62956dd4b1 Update src/client/api.ts 2025-02-28 17:49:41 +01:00
soraefir
7af6d04dd1 wip
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 01:43:32 +01:00
soraefir
1960036980 wip
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-28 01:05:10 +01:00
9060814609 Update template/journey.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:33:19 +01:00
50a98b9a31 Update template/module/map.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:32:59 +01:00
1f3db375a4 Update template/module/journey_leg.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:32:45 +01:00
f3381bf8f0 Update template/module/journey_nav.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:32:32 +01:00
503ff92d5e Update template/module/foot.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:31:54 +01:00
92032f60e9 Update src/client/types/wrapper.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:31:19 +01:00
1814a8a87d Update src/client/types/geom.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:31:09 +01:00
fe4190313b Update src/client/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:30:58 +01:00
52b3d98fec Update src/client/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:30:44 +01:00
366ca8b97f Update src/client/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 17:30:33 +01:00
soraefir
f1c702bc14 Wip
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-27 00:55:37 +01:00
b7b87507e3 Update src/client/types/geom.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 17:01:07 +01:00
f5b9ad05c7 Update template/module/map.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:58:21 +01:00
be46114dd3 Update template/module/journey_step.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:58:11 +01:00
951172ede3 Update src/server/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:57:27 +01:00
38f1300095 Update src/server/api_nominatim.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:56:53 +01:00
cbaaf4de5b Update src/client/types/wrapper.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:56:21 +01:00
d184068ae2 Add src/client/types/geoms.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:56:10 +01:00
a69d334782 Update src/client/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:55:50 +01:00
ad420b4528 Update src/client/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-26 16:55:29 +01:00
60508b99ae Update template/module/foot.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:03:22 +01:00
6ec4d426c9 Update src/server/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:03:07 +01:00
d63845ed9a Update src/server/api_nominatim.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:02:57 +01:00
58c6eaa868 Update src/server/api_flight.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:02:48 +01:00
dc23d5ba83 Update src/server/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:02:38 +01:00
c307dd0a22 Update src/client/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 18:02:19 +01:00
1df6f14f1a Update src/server/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:11:34 +01:00
e0ec2c0f46 Add src/server/api_nominatim.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:10:57 +01:00
ca9edace2f Add src/server/api_flight.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:10:41 +01:00
5a45dcb1b9 Update src/server/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:10:18 +01:00
7fdd467c8c Update src/server/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:10:01 +01:00
e8f0516ccb Update src/server/api.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:09:39 +01:00
b995cd45f0 Update tsconfig-client.json
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:09:16 +01:00
bfd13fd782 Update package.json
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:08:53 +01:00
3568de823d Update src/client/types/wrapper.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:08:18 +01:00
8e8ab398c9 Update src/client/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:08:00 +01:00
25a6fcf90d Update src/client/types/migration.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:07:04 +01:00
4083c4268f Update src/client/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:06:36 +01:00
aacefdce94 Update src/client/types/ext.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:06:20 +01:00
ca2e2c11d2 Update src/client/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:05:37 +01:00
a782f61e0b Update src/client/main.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:05:22 +01:00
5bb1622a0b Update src/client/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 16:05:00 +01:00
soraefir
0cc9d235ed Better nominatim
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-25 01:38:34 +01:00
b554eba76c Update template/module/map.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 17:31:48 +01:00
97f2a5ebb8 Add src/types/migration.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 17:31:32 +01:00
a13b3acbe5 Update src/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 17:31:15 +01:00
c2d1858e68 Update src/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 17:31:02 +01:00
f3bed6d21c Update template/module/view_step.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 16:46:42 +01:00
ecc9e83369 Update src/types/ext.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 16:46:22 +01:00
d689ceb511 Update src/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 16:46:11 +01:00
059dfec50d Update router/api.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 16:45:49 +01:00
cc527bc1ab Update template/module/view_step.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:16:59 +01:00
9d9e79b07d Update template/module/short_sec.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:16:48 +01:00
6048b30a18 Update template/module/nav.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:16:36 +01:00
8e547fd0cf Update template/module/nav_pub.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:16:21 +01:00
b3867d42b3 Update template/module/map.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:16:11 +01:00
923263e27f Update template/module/journey_step.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:15:59 +01:00
63eb47f855 Update template/module/journey_sec.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:15:49 +01:00
17cfcc77da Update template/module/foot.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:15:35 +01:00
0949635148 Update template/view.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:15:17 +01:00
6f96bff5b5 Update template/short.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:15:06 +01:00
c724167803 Update template/home.pug
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:14:54 +01:00
b8e71cd6fa Add src/types/wrapper.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:14:38 +01:00
c4537ca1b0 Add src/types/format.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:14:19 +01:00
d295c15504 Update src/types/ext.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:13:53 +01:00
ef00ba536d Update src/old.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:13:35 +01:00
d6890940d0 Update src/app.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:13:16 +01:00
9f88d80b68 Update src/api.ts
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:13:02 +01:00
1e10109bbe Update router/api.js
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:12:46 +01:00
8a46f473eb Update package.json
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-24 15:12:04 +01:00
57 changed files with 5333 additions and 4064 deletions

1
.gitignore vendored
View File

@@ -6,7 +6,6 @@ db/
.yarn/ .yarn/
public/*.js public/*.js
public/*.map public/*.map
public/*.css
.yarnrc.yml .yarnrc.yml
.pnp* .pnp*
build/ build/

View File

@@ -5,10 +5,8 @@ WORKDIR /usr/src/app
# Install app dependencies # Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied # A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+) # where available (npm@5+)
COPY *.json ./ COPY package*.json ./
COPY src ./src
RUN yarn RUN yarn
RUN yarn build
# If you are building your code for production # If you are building your code for production
# RUN npm ci --only=production # RUN npm ci --only=production
# Bundle app source # Bundle app source

View File

@@ -4,12 +4,11 @@
"description": "Open Travel Mapper", "description": "Open Travel Mapper",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build": "yarn build-server && yarn build-client && yarn build-style", "build": "yarn build-server && yarn build-client",
"build-style": "esbuild src/style/index.css --outfile=public/index.css --bundle --minify ",
"build-client": "esbuild src/client/main.ts --outfile=public/main.js --tree-shaking=true --bundle --minify --sourcemap --tsconfig=tsconfig-client.json", "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", "build-server": "esbuild src/server/**/*.ts --outdir=build --platform=node --format=cjs",
"start": "node build/main.js", "start": "node build/main.js",
"demon": "nodemon -e ts,js,css --watch src --watch template --watch router --exec \"yarn build && yarn start\"" "demon": "nodemon -e ts,js --watch src --watch template --watch router --exec \"yarn build && yarn start\""
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -20,8 +19,8 @@
"dependencies": { "dependencies": {
"@fastify/leveldb": "^6.0.0", "@fastify/leveldb": "^6.0.0",
"@fastify/static": "^8.0.0", "@fastify/static": "^8.0.0",
"@fastify/view": "^11.0.0", "@fastify/view": "^10.0.0",
"@types/node": "^24.0.0", "@types/node": "^22.13.5",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fastify": "^5.2.1", "fastify": "^5.2.1",
"jsdom": "^26.0.0", "jsdom": "^26.0.0",

1860
public/css/index.css Normal file

File diff suppressed because it is too large Load Diff

BIN
public/fonts/fa-brands.ttf Normal file

Binary file not shown.

BIN
public/fonts/fa-regular.ttf Normal file

Binary file not shown.

BIN
public/fonts/fa-solid.ttf Normal file

Binary file not shown.

BIN
public/img/customizable.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
public/img/lightweight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
public/img/opensource.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,4 +1,4 @@
export const throttle = (func: (...args: any[]) => any, wait: number) => { export const throttle = (func: () => void, wait: number) => {
var lastTime = 0; var lastTime = 0;
var timeoutId: ReturnType<typeof setTimeout> | undefined; var timeoutId: ReturnType<typeof setTimeout> | undefined;
var lastArgs: any[]; var lastArgs: any[];
@@ -41,25 +41,20 @@ export const load = (id: string) =>
return res; return res;
}); });
var version_add = 1 export const save = (id: string, v: journey) =>
export const save = async (id: string, v: journey) => { fetch("/api/" + id, { method: "post", body: JSON.stringify(v) })
let body = JSON.parse(JSON.stringify(v))
body.version +=version_add;
return fetch("/api/" + id, { method: "post", body: JSON.stringify(body) })
.then((res) => { .then((res) => {
if (!res.ok) throw new Error("Error " + res.statusText); if (!res.ok) throw new Error("Error " + res.statusText);
return res.json(); return res.json();
}) })
.then((_res) => { .then((_res) => {
version_add+=1;
console.log("Saved..."); console.log("Saved...");
}); });
}
export const query_nominatim = ( export const query_nominatim = (
q: string, q: string,
bb: any, bb: any,
f: (v: NominatimResult) => Boolean = () => true f: (v: string) => Boolean = () => true
) => { ) => {
if (q.length == 0) return Promise.resolve([]) if (q.length == 0) return Promise.resolve([])
let url = new URL("/api/place/" + q, window.location.origin); let url = new URL("/api/place/" + q, window.location.origin);
@@ -146,7 +141,7 @@ export const icon_type = (item: string | NominatimResult): string => {
"nature_reserve", "nature_reserve",
], ],
"dice-five": ["water_park", "theme_park", "casino"], "dice-five": ["water_park", "theme_park", "casino"],
"": ["?", "neighbourhood", "quarter", "highway", "place"], "": ["?", "neighbourhood", "quarter", "highway"],
}; };
for (let k in types) { for (let k in types) {
@@ -155,13 +150,3 @@ export const icon_type = (item: string | NominatimResult): string => {
console.log(item.display_name, item.category, item.type); console.log(item.display_name, item.category, item.type);
return "question"; 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;
}
}

View File

@@ -1,96 +0,0 @@
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);
}),
3000
);

View File

@@ -1,41 +0,0 @@
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;
}
}

View File

@@ -1,49 +0,0 @@
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
}

View File

@@ -2,10 +2,7 @@
import * as api from "./api"; import * as api from "./api";
import journey_wrapper from "./types/wrapper"; import journey_wrapper from "./types/wrapper";
import { migrator } from "./types/migration"; import { migrator } from "./types/migration";
import { journey_add_place, journey_del_place } from "./helper/journey"; import { getGeoLine } from "./types/geom";
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-map", window.Vue2Leaflet.LMap);
Vue.component("l-tile-layer", window.Vue2Leaflet.LTileLayer); Vue.component("l-tile-layer", window.Vue2Leaflet.LTileLayer);
@@ -23,18 +20,13 @@ const app = new Vue({
journey: new journey_wrapper(window.location.pathname.split("/").pop() || String.gen_id(16)), journey: new journey_wrapper(window.location.pathname.split("/").pop() || String.gen_id(16)),
map_override: { active: false, elements: [] }, map_override: { active: false, elements: [] },
query: { query: {
type: "", query: "", res: [], load: false, sub: false, note: false, drawer: false, addmarker: false, type: "", res: [], load: false, sub: false, note: false, drawer: false,
}, },
useDay: false, leg_nav: {
toast: { scrollInterval: null,
show: false, scrollDir: null
title: '',
desc: '',
special: '',
acceptText: '',
cancelText: '',
func: () => { },
}, },
impexp: "",
lang: { lang: {
format: "ddd D MMM", format: "ddd D MMM",
formatLocale: { formatLocale: {
@@ -72,10 +64,11 @@ const app = new Vue({
return `<i class="fa fa-${api.icon_type(item) || "star"} fa-2x ${classes}" style="${styling}; color:${fcolor || "white"}; text-align:center; align-content:center;"></i>`; return `<i class="fa fa-${api.icon_type(item) || "star"} fa-2x ${classes}" style="${styling}; color:${fcolor || "white"}; text-align:center; align-content:center;"></i>`;
}, },
import_data: function (v) {
import_data: function () {
this.journey.data = Object.assign( this.journey.data = Object.assign(
{}, {},
JSON.parse(v.toDecoded()), JSON.parse(this.impexp.toDecoded()),
); );
this.journey.data.main.forEach((e) => { this.journey.data.main.forEach((e) => {
if (e.date_range) { if (e.date_range) {
@@ -84,47 +77,77 @@ const app = new Vue({
} }
}); });
}, },
export_data: function () {
toast_reset: function () { this.impexp = JSON.stringify(this.journey.data).toEncoded();
this.toast.show = false; },
this.toast.title = ''; filter_selected: function (list, step) {
this.toast.desc = '' return list.filter((e) =>
this.toast.special = ''; step ? e.step == this.journey.sel_day : e.step >= 0,
this.toast.acceptText = '' );
this.toast.cancelText = '' },
this.toast.func = () => { } 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_impexp: function (is_import) { search_nominatim: function (f) {
this.toast.show = true; return (q) => api.query_nominatim(q, this.compute_bb(), this.get_filter(f)).catch((_err) => []).then((r) => {
this.toast.title = is_import ? 'Import' : 'Export'; r.forEach((rr) => {
this.toast.desc = '' rr.latlon = [parseFloat(rr.lat), parseFloat(rr.lon)];
this.toast.special = JSON.stringify(this.journey.data).toEncoded(); rr.sname = rr.display_name.split(",")[0];
this.toast.acceptText = is_import ? 'load' : '' });
this.toast.cancelText = 'cancel' r = r.filter(e => {
this.toast.func = () => { this.import_data(this.toast.special); this.toast_reset(); } 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;
search_set_results(r) { return true
})
this.query.load = false; this.query.load = false;
this.query.res = r; this.query.res = r;
return r return r
});
}, },
search_set_clear(keep_drawer) { search_travel: function (f) {
this.query.res = []; return (q) => api.query_flight(q).then((r) => {
this.query.note = false; r.forEach(el => {
this.query.type = null; el.path = getGeoLine(
this.query.addmarker = false; { lat: el.from_geo.lat, lng: el.from_geo.lon },
this.query.sub = false; { lat: el.to_geo.lat, lng: el.to_geo.lon }, { dist: 2_500_000 }).map(v => [v.lat, v.lng])
this.query.drawer = keep_drawer; el.type = "flight";
setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); });
}, r = r.filter(e => {
search_set_active: function (is_notes, tpe) { if (this.journey.leg_get().travel.find(i => `${i.from}->${i.to}` == `${e.from}->${e.to}`)) return false;
this.query.drawer = true; return true
this.query.note = is_notes; })
this.query.type = !is_notes ? tpe : null; this.query.load = false;
this.query.load = !is_notes; this.query.res = r;
setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500); return r;
});
}, },
drawer_hover_item: function (item) { drawer_hover_item: function (item) {
@@ -141,80 +164,112 @@ const app = new Vue({
}, },
drawer_click_item: function (item) { drawer_click_item: function (item) {
const tpe = this.query.type const tpe = this.query.type;
this.search_set_clear(item ? true : false); this.query.res = [];
this.drawer_hover_item(); 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()
if (item) { if (item) {
item.step = -1; item.step = -1;
journey_add_place(this.journey, tpe, item) 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);
}
} }
}, },
search_active: function (_e) { search_active: function (q) {
const tpe = this.query.type; const txt = q.target.value
const query = this.query.query; this.query.load = true;
switch (tpe) { switch (this.query.type) {
case 'hotel': case 'hotel': return this.search_hotel(txt);
case 'restaurant': ; case 'restaurant': return this.search_restaurant(txt);
case 'place': case 'place': return this.search_place(txt);
case 'other': case 'other': return this.search_other(txt);
return search_nominatim(tpe, query, this.compute_bb(), this.journey.leg_get()) case 'flight': return this.search_flight(txt);
case 'flight':
return search_flight(tpe, query, this.journey.leg_get());
} }
}, },
search_enable: function (f) { search_enable: function (f) {
const is_notes = f == 'notes'; this.query.drawer = true;
this.search_set_active(is_notes, f) setTimeout(() => this.$refs.map.mapObject.invalidateSize(), 500);
setTimeout(() => document.getElementById(is_notes ? 'query_note' : 'query_input').focus(), 500); if (f == "notes") {
if (!is_notes) this.search_active() this.query.note = true;
}, this.query.type = null;
const query_in = document.getElementById('query_note')
refreshTextAreaHeight: function (e) { setTimeout(() => query_in.focus(), 500);
e.target.style['height'] = 'auto'; return;
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 () { created: function () {
set_search_set_results(this.search_set_results); this.search_hotel = api.throttle(this.search_nominatim("hotel"), 1000)
this.nav_mouseleave = nav_mouseleave; this.search_restaurant = api.throttle(this.search_nominatim("restaurant"), 1000)
this.nav_mousemove = nav_mousemove; this.search_place = api.throttle(this.search_nominatim("place"), 1000)
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.save_data = api.throttle(() => {
this.impexp = JSON.stringify(this.journey.data).toEncoded();
api.save(this.journey.id, this.journey.data); api.save(this.journey.id, this.journey.data);
}, 1000); }, 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) => { window.addEventListener("keydown", (e) => {
switch (e.key) { switch (e.key) {
case "ArrowLeft": return this.pressed_prev() case "ArrowLeft":
case "ArrowRight": return this.pressed_next() this.journey.day_prev();
default: return console.log(e.key); break;
case "ArrowRight":
this.journey.day_next();
break;
default:
console.log(e.key);
} }
}); });
@@ -225,9 +280,12 @@ const app = new Vue({
watch: { watch: {
journey: { journey: {
handler: function (ndata, odata) { handler: function (ndata, odata) {
if (this.edit_active) this.save_data(); if (this.edit_active)
this.save_data();
}, },
deep: true, deep: true,
}, },
}, },
}); });

View File

@@ -6,8 +6,6 @@ declare global {
lng: number lng: number
} }
interface geoloc { interface geoloc {
title: string
osm_id: number
latlon: [number, number] latlon: [number, number]
notes: string notes: string
step: -1 step: -1
@@ -24,7 +22,7 @@ declare global {
day_title: string[] day_title: string[]
date_range: [Date, Date] | null date_range: [Date, Date] | null
map: map map: map
travel: geoloc[] travel: unknown[]
hotel: geoloc | null hotel: geoloc | null
places: { places: {
restaurants: geoloc[] restaurants: geoloc[]
@@ -35,7 +33,6 @@ declare global {
interface journey { interface journey {
fmt_ver: number fmt_ver: number
version: number
title: string title: string
main: leg[] main: leg[]
} }
@@ -53,7 +50,6 @@ const leg_template: leg = {
} }
const journey_template: journey = { const journey_template: journey = {
fmt_ver: 1, fmt_ver: 1,
version: 0,
title: "New Journey", title: "New Journey",
main: [leg_template], main: [leg_template],
} }

View File

@@ -8,9 +8,7 @@ function migrate_A_to_0(e: journey): journey {
v.day_title = v.day_title || (v as any).step_title; v.day_title = v.day_title || (v as any).step_title;
v.places.activities = v.places.activities || (v as any).places.places; v.places.activities = v.places.activities || (v as any).places.places;
v.travel = v.travel || []; v.travel = v.travel || [];
v.day_title = typeof (v.day_title) == "string" ? [v.day_title] : []
}) })
e.version = e.version || 0;
console.log(e) console.log(e)
return e; return e;
} }

View File

@@ -1,4 +1,3 @@
import './ext.ts'
import { journey_template, leg_template } from "./format" import { journey_template, leg_template } from "./format"
const date_day_diff = (d0: Date, d1: Date): number => const date_day_diff = (d0: Date, d1: Date): number =>
@@ -57,10 +56,6 @@ class journey_wrapper {
this.sel_leg = Math.max(this.sel_leg - 1, 0); this.sel_leg = Math.max(this.sel_leg - 1, 0);
this.sel_day = 0; this.sel_day = 0;
} }
day_sel(idx: number): void {
this.sel_day = idx;
}
day_next() { day_next() {
this.sel_day += 1 this.sel_day += 1
if (this.sel_day > this.leg_len() - 1) { if (this.sel_day > this.leg_len() - 1) {
@@ -83,13 +78,6 @@ class journey_wrapper {
} }
} }
} }
day_cycle() {
if (this.sel_day >= this.leg_len() - 1) {
this.sel_day = 0;
} else {
this.day_next()
}
}
date_sel(): string { date_sel(): string {
if (this.sel_day < 0) return "?"; if (this.sel_day < 0) return "?";
let leg = this.leg_get() let leg = this.leg_get()

View File

@@ -60,27 +60,31 @@ export default function (server, opts, done) {
if (req.params.id == undefined) if (req.params.id == undefined)
return reply.code(400).send({ error: "No ID query parameter" }); return reply.code(400).send({ error: "No ID query parameter" });
return server.level.db.get(req.params.id) server.level.db.get(req.params.id, (err, val) => {
.then(r=> reply.send(JSON.parse(r))) if (err) {
.catch(err=>{
console.warn(err); console.warn(err);
return reply.code(500).send({error: "Error with DB"}); reply.code(500).send();
}) } else {
reply.send(JSON.parse(val));
}
});
return reply;
}); });
server.post("/:id", async (req, reply) => { server.post("/:id", async (req, reply) => {
if (req.params.id == undefined) if (req.params.id == undefined)
return reply.code(400).send({ error: "No ID query parameter" }); return reply.code(400).send({ error: "No ID query parameter" });
return server.level.db.get(req.params.id).then(r=>JSON.parse(r)).then(r=>r.version||-1).catch(_=>-1).then(db_version=>{ server.level.db.put(req.params.id, req.body, (err) => {
if(db_version+1 == req.body.version || db_version == -1) if (err) {
return server.level.db.put(req.params.id, req.body) console.warn(err);
.then(_=>reply.send({ content: "ok" })) reply.code(500).send({ error: "Error with DB" });
.catch(_=>reply.code(500).send({ error: "Error with DB" })) } else {
reply.send({ content: "ok" });
return reply.code(409).send({error:"Too old version, please refresh."}); }
}) });
}) return reply;
});
done(); done();
}; };

View File

@@ -25,10 +25,10 @@ server.register(fastify_view, {
server.register(api, { prefix: "/api" }); server.register(api, { prefix: "/api" });
server.get("/", (req, reply) => reply.view("/src/template/home.pug")); server.get("/", (req, reply) => reply.view("/template/home.pug"));
server.get("/:id", (req, reply) => reply.view("/src/template/journey.pug")); server.get("/:id", (req, reply) => reply.view("/template/journey.pug"));
server.get("/view/:id", (req, reply) => reply.view("/src/template/view.pug")); server.get("/view/:id", (req, reply) => reply.view("/template/view.pug"));
server.get("/short/:id", (req, reply) => reply.view("/src/template/short.pug")); server.get("/short/:id", (req, reply) => reply.view("/template/short.pug"));
server.listen({ port: 8080, host: "0.0.0.0" }, (err, address) => { server.listen({ port: 8080, host: "0.0.0.0" }, (err, address) => {
if (err) throw err; if (err) throw err;

View File

@@ -1,366 +0,0 @@
body {
background: var(--darkdark);
min-height: 100vh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.leaflet-control-attribution,
.leaflet-popup-close-button {
visibility: hidden;
}
.p-abs {
position: absolute;
}
.display-none {
display: none;
}
.col-0 {
flex: 0 0 0%;
max-width: 0%;
overflow: hidden;
}
.map-container,
.drawer-container {
transition: flex 0.5s ease-in-out, max-width 0.5s ease-in-out;
}
.map-container {
min-width: 52px;
height: 100%;
}
.drawer-container {
height: 100%;
right: 0;
top: 0;
margin-left: 0;
}
.travel-path-icon {
margin-left: -12px;
margin-top: -32px;
}
.input {
max-height: 100%;
}
.ml-auto {
margin-left: auto;
}
.mr-auto {
margin-right: auto;
}
.input .mx-input {
height: 40px;
}
.h-100 {
height: 100%;
}
.h-0 {
height: 0%;
overflow: hidden;
}
.w-100 {
width: 100%;
}
.w-0 {
width: 0%;
overflow: hidden;
}
.map-menu {
position: absolute;
display: flex;
z-index: 1100;
right: 0;
flex-direction: column;
gap: 10px;
margin: 5px;
}
.map-menu-top {
top: 0;
}
.map-menu-bottom {
bottom: 0;
}
.map-menu-center {
top: 50%;
}
.padding-1 {
padding: 5px;
}
.map-menu-item {
background-color: var(--darkdark);
padding: 5px;
border-radius: 50%;
cursor: pointer;
}
.map-menu-item:hover {
background-color: var(--lightdark);
}
.map-menu-sub {
display: flex;
flex-direction: row-reverse;
gap: 5px;
}
.map-menu-item,
.map-menu-sub-item {
background-color: var(--darkdark);
padding: 5px;
border-radius: 50%;
cursor: pointer;
float: right;
width: 42px;
height: 42px;
align-content: center;
text-align: center;
}
.vue2leaflet-map {
border-radius: var(--border-radius);
}
.leaflet-popup-content {
margin: 10px 20px !important;
}
.leaflet-popup>.leaflet-popup-content-wrapper {
border-radius: var(--border-radius);
width: 350px;
}
.leaflet-popup-button-group {
position: absolute;
top: 0;
right: 0;
display: flex;
flex-direction: row;
gap: 5px;
margin: 2px 6px;
}
.leaflet-popup-button-group>a {
cursor: pointer;
}
.leaflet-popup-button-group>a:hover {
cursor: pointer;
filter: brightness(150%);
}
.leaflet-popup-button-group>a>i {
font-size: 1em;
}
.query-result {
display: flex;
align-items: center;
border-radius: var(--border-radius);
cursor: pointer;
}
.query-result:hover {
filter: brightness(85%);
}
.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);
}
.scroll-content>div:nth-last-child(3) {
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
.scroll-content>div:last-child {
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;
}

View File

@@ -1,19 +0,0 @@
:root {
--black: #030B12;
--darkdark: #0C1D2E;
--dark: #203A53;
--lightdark: #425F7C;
--light: #93A9BE;
--lightlight: #B6C5D5;
--white: #F0F3F7;
--orange: ##F5B97D;
--yellow: #F5F57D;
--green: #B9F57D;
--turquoise: #7DF5B9;
--blue: #7DB9F5;
--purple: #B97DF5;
--pink: #F57DB9;
--red: #F57D7D;
--border-radius: 3px;
}

View File

@@ -1,13 +0,0 @@
@import './define.css';
@import './module/input.css';
@import './module/load_n_spin.css';
@import './module/typography.css';
@import './module/layout.css';
@import './module/general.css';
@import './custom.css';
[v-cloak] {
display: none;
}

View File

@@ -1,206 +0,0 @@
* {
box-sizing: border-box;
}
html,
body,
body,
div,
span,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
abbr,
address,
cite,
code,
del,
dfn,
em,
ins,
kbd,
q,
samp,
small,
strong,
sub,
sup,
var,
b,
i,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
figure,
footer,
header,
hgroup,
menu,
nav,
section,
time,
mark,
audio,
video {
background: transparent;
border: 0;
font-size: 100%;
margin: 0;
outline: 0;
padding: 0;
vertical-align: baseline;
}
article,
aside,
figure,
footer,
header,
main,
nav,
section {
display: block;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
*,
*::after,
*::before {
box-sizing: border-box;
outline: none;
}
body {
background-color: #fff;
min-height: 100%;
overflow-x: hidden;
position: relative;
}
p {
font-weight: normal;
margin-bottom: 1.5em;
}
img {
max-width: 100%;
}
strong {
font-weight: 600;
}
ul {
margin-bottom: 1em;
}
li {
list-style: none;
margin-bottom: 0.5em;
}
/**
* BACKGROUND
*/
.bg-primary {
background-color: var(--blue);
}
.bg-dark {
background-color: var(--darkdark);
}
.bg-secondary {
background-color: var(--pink);
}
.bg-white {
background-color: var(--white);
}
.bg-success {
background-color: var(--green);
}
.bg-info {
background-color: var(--yellow);
}
.bg-warning {
background-color: var(--orange);
}
.bg-error {
background-color: var(--red);
}
.bg-gray {
background-color: var(--lightdark);
}
.bg-gray-light {
background-color: var(--lightlight);
}
.align {
align-items: center;
justify-content: center;
}
.fleft {
float: left;
}
.fright {
float: right;
}
.clearfix ::after {
clear: both;
content: "";
display: table;
}
.no-wrap {
white-space: nowrap;
}
.overflow-hidden {
overflow: hidden;
}
.rounded {
border-radius: var(--border-radius);
}

View File

@@ -1,424 +0,0 @@
switch,
input,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
resize: none;
}
label {
display: block;
font-weight: normal;
}
input:-webkit-autofill {
box-shadow: 0 0 0 1000px #eceff1 inset;
}
.switch,
.textarea,
.input,
.select {
border: 1px solid var(--white);
border-radius: var(--border-radius);
box-shadow: none;
display: inline-block;
font-weight: normal;
overflow: hidden;
}
.textarea :focus,
.input :focus,
.select :focus {
outline: none;
}
.textarea.has-error,
.input.has-error,
.select.has-error {
background: #eceff1;
border: 1px solid #e74c3c;
margin-bottom: 0;
}
.select {
background-color: #eceff1;
display: inline-block;
margin-right: 16px;
position: relative;
}
.select:last-child {
margin-right: 0;
}
.select-fullWidth {
display: block;
margin-left: 0;
margin-right: 0;
width: 100%;
}
.select select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: transparent;
border: 0;
border-radius: 0;
color: #272727;
display: block;
font-size: 16px;
line-height: 1.5em;
margin: 0;
padding: 8px 16px;
padding-right: 30px;
transition: background-color 0.2s ease-in-out;
width: 100%;
}
.select select:active,
.select select:focus {
background-color: #fbfbfc;
border: 0;
outline: none;
}
.select select::-ms-expand {
display: none;
}
.select::after,
.select::before {
background: #03a9f4;
content: "";
display: block;
height: 2px;
margin-top: 2px;
position: absolute;
right: 5px;
top: 50%;
-webkit-transform-origin: 1px;
transform-origin: 1px;
width: 10px;
}
.select::after {
-webkit-transform: rotate(-135deg);
transform: rotate(-135deg);
}
.select::before {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.textarea {
background-color: #eceff1;
padding: 0;
}
.textarea-fullWidth {
display: block;
margin-left: 0;
margin-right: 0;
width: 100%;
}
.textarea textarea {
background: transparent;
border: 0;
color: #272727;
display: block;
font-family: "Lato", sans-serif;
font-size: 16px;
line-height: 1.5em;
margin: 0;
min-height: 120px;
padding: 8px 16px;
transition: background-color 0.2s ease-in-out;
width: 100%;
}
.textarea textarea::-webkit-input-placeholder {
color: #969da6;
}
.textarea textarea::-ms-input-placeholder {
color: #969da6;
}
.textarea textarea::placeholder {
color: #969da6;
}
.textarea textarea:focus,
.textarea textarea:active {
background-color: #fbfbfc;
border: 0;
outline: none;
}
.checkbox {
margin-bottom: 8px;
position: relative;
}
.checkbox input[type="checkbox"] {
display: none;
}
.checkbox input[type="checkbox"]:checked::after {
-webkit-animation: checkboxAndRadioAnimation 0.25s;
animation: checkboxAndRadioAnimation 0.25s;
content: "";
-webkit-transform: scale(1) rotate(45deg);
transform: scale(1) rotate(45deg);
}
.checkbox input[type="checkbox"] {
display: block;
overflow: hidden;
padding-left: 30px;
text-overflow: ellipsis;
white-space: nowrap;
}
.checkbox input[type="checkbox"]::before {
background-color: #eceff1;
border: 1px solid var(--white);
border-radius: var(--border-radius);
content: "";
display: inline-block;
height: 20px;
left: 0;
margin-top: -10px;
position: absolute;
top: 50%;
width: 20px;
}
.checkbox input[type="checkbox"]::after {
border-bottom: 3px solid #03a9f4;
border-right: 3px solid #03a9f4;
display: block;
height: 12px;
left: 11px;
margin-left: -4px;
margin-top: -7px;
position: absolute;
top: 50%;
width: 7px;
z-index: 1;
}
.radio {
margin-bottom: 8px;
position: relative;
}
.radio input[type="radio"] {
display: none;
}
.radio input[type="radio"]:checked::after {
-webkit-animation: checkboxAndRadioAnimation 0.25s;
animation: checkboxAndRadioAnimation 0.25s;
content: "";
-webkit-transform: scale(1) rotate(45deg);
transform: scale(1) rotate(45deg);
}
.radio input[type="radio"] {
display: block;
overflow: hidden;
padding-left: 30px;
text-overflow: ellipsis;
white-space: nowrap;
}
.radio input[type="radio"]::before {
background-color: #eceff1;
border: 1px solid var(--white);
border-radius: 20px;
content: "";
display: inline-block;
height: 20px;
left: 0;
margin-top: -10px;
position: absolute;
top: 50%;
width: 20px;
}
.radio input[type="radio"]::after {
background-color: #03a9f4;
border-radius: 20px;
display: block;
height: 10px;
left: 11px;
margin-left: -6px;
margin-top: -6px;
position: absolute;
top: 13px;
width: 10px;
z-index: 1;
}
@-webkit-keyframes checkboxAndRadioAnimation {
0% {
-webkit-transform: scale(0) rotate(45deg);
transform: scale(0) rotate(45deg);
}
50% {
-webkit-transform: scale(1.5) rotate(45deg);
transform: scale(1.5) rotate(45deg);
}
100% {
-webkit-transform: scale(1) rotate(45deg);
transform: scale(1) rotate(45deg);
}
}
@keyframes checkboxAndRadioAnimation {
0% {
-webkit-transform: scale(0) rotate(45deg);
transform: scale(0) rotate(45deg);
}
50% {
-webkit-transform: scale(1.5) rotate(45deg);
transform: scale(1.5) rotate(45deg);
}
100% {
-webkit-transform: scale(1) rotate(45deg);
transform: scale(1) rotate(45deg);
}
}
.input-invis {
background-color: transparent !important;
margin: auto !important;
border: 0 !important;
}
.switch,
.input {
background-color: var(--white);
padding: 0;
position: relative;
}
.input :focus,
.input :active {
background-color: var(--white);
border-radius: var(--border-radius);
}
.switch .rocker,
.input input,
.input textarea {
background: transparent;
border: 0;
box-shadow: none;
color: #272727;
font-size: 16px;
line-height: 1.5em;
margin: 0;
outline: none;
padding: 8px 16px;
width: 100%;
}
.input input::-webkit-input-placeholder {
color: #969da6;
}
.input input::-ms-input-placeholder {
color: #969da6;
}
.input input::placeholder {
color: #969da6;
}
.input input.small {
line-height: 1em;
padding: 0;
}
.input-withIcon input {
padding-right: 32px;
}
.input-icon {
fill: #969da6;
height: 16px;
margin-top: -8px;
position: absolute;
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%;
}

View File

@@ -1,359 +0,0 @@
/**
* LAYOUT
*/
.section {
padding-bottom: 36px;
padding-top: 36px;
}
@media (min-width: 768px) {
.section {
padding-bottom: 72px;
padding-top: 72px;
}
}
.section+.section {
padding-top: 0;
}
.container {
margin: 0 auto;
max-width: 1380px;
padding-left: 12px;
padding-right: 12px;
width: 100%;
}
@media (min-width: 576px) {
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container {
max-width: 1140px;
}
}
@media (min-width: 768px) {
.container {
padding-left: 24px;
padding-right: 24px;
}
}
.container-medium {
margin: 0 auto;
max-width: 944px;
padding-left: 12px;
padding-right: 12px;
}
@media (min-width: 768px) {
.container-medium {
padding-left: 24px;
padding-right: 24px;
}
}
.container {
width: 100%;
padding-right: 12px;
padding-left: 12px;
margin-right: auto;
margin-left: auto;
}
.container-fluid {
width: 100%;
padding-right: 12px;
padding-left: 12px;
margin-right: auto;
margin-left: auto;
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -12px;
margin-left: -12px;
}
.col-1,
.col-2,
.col-3,
.col-4,
.col-5,
.col-6,
.col-7,
.col-8,
.col-9,
.col-10,
.col-11,
.col-12,
.col,
.col-auto,
.col-sm-1,
.col-sm-2,
.col-sm-3,
.col-sm-4,
.col-sm-5,
.col-sm-6,
.col-sm-7,
.col-sm-8,
.col-sm-9,
.col-sm-10,
.col-sm-11,
.col-sm-12,
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11,
.col-md-12 {
position: relative;
width: 100%;
min-height: 1px;
padding-right: 12px;
padding-left: 12px;
}
.col {
flex-basis: 0;
flex-grow: 1;
max-width: 100%;
}
.col-auto {
flex: 0 0 auto;
width: auto;
max-width: none;
}
.col-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-12 {
flex: 0 0 100%;
max-width: 100%;
}
@media (min-width: 576px) {
.col-sm {
flex-basis: 0;
flex-grow: 1;
max-width: 100%;
}
.col-sm-auto {
flex: 0 0 auto;
width: auto;
max-width: none;
}
.col-sm-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-sm-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-sm-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-sm-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-sm-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-sm-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-sm-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-sm-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-sm-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-sm-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-sm-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-sm-12 {
flex: 0 0 100%;
max-width: 100%;
}
}
@media (min-width: 768px) {
.col-md {
flex-basis: 0;
flex-grow: 1;
max-width: 100%;
}
.col-md-auto {
flex: 0 0 auto;
width: auto;
max-width: none;
}
.col-md-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-md-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
.col-md-3 {
flex: 0 0 25%;
max-width: 25%;
}
.col-md-4 {
flex: 0 0 33.33333%;
max-width: 33.33333%;
}
.col-md-5 {
flex: 0 0 41.66667%;
max-width: 41.66667%;
}
.col-md-6 {
flex: 0 0 50%;
max-width: 50%;
}
.col-md-7 {
flex: 0 0 58.33333%;
max-width: 58.33333%;
}
.col-md-8 {
flex: 0 0 66.66667%;
max-width: 66.66667%;
}
.col-md-9 {
flex: 0 0 75%;
max-width: 75%;
}
.col-md-10 {
flex: 0 0 83.33333%;
max-width: 83.33333%;
}
.col-md-11 {
flex: 0 0 91.66667%;
max-width: 91.66667%;
}
.col-md-12 {
flex: 0 0 100%;
max-width: 100%;
}
}

View File

@@ -1,166 +0,0 @@
/**
* LOADING BAR
*
* Markup:
* ---------
* <div class="loadingBar"></div>
*
*/
.loadingBar {
height: 6px;
left: 0;
overflow: hidden;
position: fixed;
right: 0;
top: 0;
width: 100%;
z-index: 1000;
}
.loadingBar::before {
-webkit-animation: loading 2s linear infinite;
animation: loading 2s linear infinite;
background-color: #03a9f4;
content: "";
display: block;
height: 6px;
left: -300px;
position: absolute;
width: 300px;
}
@-webkit-keyframes loading {
from {
left: -300px;
width: 30%;
}
50% {
width: 30%;
}
70% {
width: 70%;
}
80% {
left: 50%;
}
95% {
left: 120%;
}
to {
left: 100%;
}
}
@keyframes loading {
from {
left: -300px;
width: 30%;
}
50% {
width: 30%;
}
70% {
width: 70%;
}
80% {
left: 50%;
}
95% {
left: 120%;
}
to {
left: 100%;
}
}
.spinner {
position: absolute;
right: 0;
top: 0;
width: 40px;
height: 40px;
display: block
}
.spinner:after,
.spinner:before {
position: absolute;
content: "";
top: 50%;
left: 50%;
margin: -12px 0 0 -12px;
width: 24px;
height: 24px;
border-radius: 100%;
border: 3px solid transparent;
border-top-color: var(--blue);
}
.spinner:before {
-webkit-animation: spinning 2.4s cubic-bezier(.41, .26, .2, .62);
animation: spinning 2.4s cubic-bezier(.41, .26, .2, .62);
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite
}
.spinner:after {
-webkit-animation: spinning 2.4s cubic-bezier(.51, .09, .21, .8);
animation: spinning 2.4s cubic-bezier(.51, .09, .21, .8);
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite
}
@-webkit-keyframes spinning {
0% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
25% {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
75% {
-webkit-transform: rotate(270deg);
transform: rotate(270deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spinning {
0% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
50% {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}

View File

@@ -1,143 +0,0 @@
/**
* TYPOGRAPHY
*/
body {
color: #272727;
font-family: "Lato", sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 1.5em;
}
a {
color: var(--blue);
text-decoration: none;
}
a:hover {
color: color-mix(in srgb, var(--color-primary), #FFF 15%);
}
a:focus {
color: var(--blue);
}
.text-huge {
font-size: 36px;
line-height: 1.3em;
}
.text-big {
font-size: 24px;
line-height: 1.3em;
}
.text-medium {
font-size: 16px;
line-height: 1.5em;
}
.text-small {
font-size: 12px;
line-height: 1.3em;
}
.text-body {
font-size: 16px;
line-height: 1.5em;
}
.text-primary {
color: #03a9f4;
}
.text-dark {
color: #18232f;
}
.text-secondary {
color: #e91e63;
}
.text-white {
color: #fff;
}
.text-success {
color: #4caf50;
}
.text-info {
color: #5bc0de;
}
.text-warning {
color: #f0ad4e;
}
.text-error {
color: #e74c3c;
}
.text-gray {
color: #969da6;
}
.text-gray-light {
color: #eceff1;
}
.text-light {
font-weight: 300;
}
.text-normal {
font-weight: 400;
}
.text-lineThrough {
text-decoration: line-through;
}
.text-italic {
font-style: italic;
}
.text-underline {
text-decoration: underline;
}
.text-uppercase {
text-transform: uppercase;
}
.text-withSubtitle {
margin-bottom: 0 !important;
}
.text-withSubtitle+.text-huge,
.text-withSubtitle+.text-big,
.text-withSubtitle+.text-medium,
.text-withSubtitle+.text-small {
margin-top: 0.5em;
}
h1,
h2,
h3,
h4 {
font-weight: 300;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}

View File

@@ -1,54 +0,0 @@
.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 + ')'"
)

View File

@@ -1,61 +0,0 @@
.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)"
)

View File

@@ -1,25 +0,0 @@
include smallbar.pug
.bg-dark.text-white(v-if="journey && journey.leg_get()")
.container
.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() + ')'" )
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) }")
.h-100(:class="{ 'w-100 ': query.type, 'w-0': !query.type }")
include leg/drawer.pug
.h-100(:class="{ 'w-100': query.note, 'w-0': !query.note }")
include leg/drawer-notes.pug

View File

@@ -1,30 +0,0 @@
.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

View File

@@ -1,14 +0,0 @@
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 }}

View File

@@ -3,6 +3,5 @@ doctype html
include module/head.pug include module/head.pug
main#app(v-cloak) main#app(v-cloak)
include module/view/toast.pug
include module/journey/main.pug include module/journey/main.pug
include module/foot.pug include module/foot.pug

View File

@@ -8,7 +8,7 @@ head
href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,700,300", href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,700,300",
type="text/css" type="text/css"
) )
link(rel="stylesheet", href="/public/index.css") link(rel="stylesheet", href="/public/css/index.css")
link(rel="stylesheet", href="https://unpkg.com/vue2-datepicker/index.css") link(rel="stylesheet", href="https://unpkg.com/vue2-datepicker/index.css")
link(rel="stylesheet", href="https://unpkg.com/leaflet/dist/leaflet.css") link(rel="stylesheet", href="https://unpkg.com/leaflet/dist/leaflet.css")

View File

@@ -0,0 +1,13 @@
.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

View File

@@ -1,9 +1,8 @@
.w-100.input() .col-12.input.text-dark()
textarea.text-small#query_note( textarea.text-small#query_note(
v-model="journey.leg_get().notes" v-model="journey.leg_get().notes"
rows="1"
@input="refreshTextAreaHeight" @input="refreshTextAreaHeight"
@focus="refreshTextAreaHeight" @focus="refreshTextAreaHeight"
rows="1"
placeholder="...", placeholder="...",
) )

View File

@@ -1,5 +1,5 @@
.input.w-100.text-dark .col-12.input.text-dark
input#query_input( input#query_input(
type="search" type="search"
@input="search_active" @input="search_active"
@@ -7,7 +7,6 @@
placeholder="Search ... " placeholder="Search ... "
style="width:85%;" style="width:85%;"
:disabled="query.note" :disabled="query.note"
v-model="query.query"
) )
.spinner(v-if="query.load") .spinner(v-if="query.load")
@@ -24,8 +23,7 @@ div(v-if="['hotel', 'restaurant', 'place','other', 'travel'].indexOf(query.type)
.bg-dark.divider( .bg-dark.divider(
:key="'qdiv'+idx" style="height:1px" ) :key="'qdiv'+idx" style="height:1px" )
.query-result.col-12.bg-white.text-dark( .query-result.col-12.bg-white.text-dark(
v-if="query.load==false && query.res.length>=0 && query.query!=''" v-if="query.load==false && query.res.length==0" )
@click="query.addmarker=true" )
div( v-html="generate_icon('star', 'var(--dark)')") div( v-html="generate_icon('star', 'var(--dark)')")
.col-10() .col-10()
| Add custom | Add custom
@@ -42,7 +40,7 @@ div(v-else-if="['flight'].indexOf(query.type)>=0")
div( v-html="generate_icon('plane', 'var(--dark)')") div( v-html="generate_icon('plane', 'var(--dark)')")
.col-10() .col-10()
| {{ item.from }} => {{item.to}} | {{ item.from }} => {{item.to}}
.bg-dark.divider( bg-dark.divider(
:key="'qdiv'+idx" style="height:1px" ) :key="'qdiv'+idx" style="height:1px" )
div(v-else) div(v-else)
template() template()

View File

@@ -0,0 +1,34 @@
.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

View File

@@ -0,0 +1,36 @@
.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

View File

@@ -0,0 +1,31 @@
.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
.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")
//- 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;")
.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 }")
include leg/drawer.pug
.drawer-container(:class="{ 'col-12': query.note, 'col-0': !query.note }")
include leg/drawer-notes.pug
//- include impexp.pug

View File

@@ -1,17 +1,15 @@
l-map( l-map(
:zoom.sync="journey.leg_get().map.zoom" :zoom.sync="journey.leg_get().map.zoom",
:center.sync="journey.leg_get().map.center" :center.sync="journey.leg_get().map.center",
@click="onMapClick"
style="height:100%" style="height:100%"
no-blocking-animations=true no-blocking-animations=true
:style="query.addmarker?'cursor:crosshair':''"
ref="map" ref="map"
) )
l-tile-layer( l-tile-layer(
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution="© <a href=\"http://osm.org/copyright\">OpenStreetMap</a> contributors" attribution="© <a href=\"http://osm.org/copyright\">OpenStreetMap</a> 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/override.pug
include map/hotel.pug include map/hotel.pug

View File

@@ -1,7 +1,7 @@
l-marker( l-marker(
v-if="map_override.active", v-if="map_override.active",
v-for="(el, idx) in map_override.elements" v-for="(el, idx) in map_override.elements"
:key="'ovr'+idx" key="'ovr'+idx"
:lat-lng="el" :lat-lng="el"
) )
l-icon(v-html="generate_marker('plus', 'darkgreen')") l-icon(v-html="generate_marker('plus', 'darkgreen')")

View File

@@ -11,7 +11,7 @@
) )
.col-1.col-sm-2 .col-1.col-sm-2
.input.col-5.col-sm-3.text-dark .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 //- .row.text-center
.input.col-sm-3(v-if="item.travel") .input.col-sm-3(v-if="item.travel")
div(v-for="(item, idx) in item.travel") div(v-for="(item, idx) in item.travel")

View File

@@ -5,7 +5,7 @@ main#app(v-cloak)
.bg-dark.text-white(v-if="journey && journey.leg_get()") .bg-dark.text-white(v-if="journey && journey.leg_get()")
.container .container
.row.align(style="padding:45px;") .row.align(style="padding-top:45px;")
.col-7.col-sm-5.col-md-4.input.text-big .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) input.text-center(v-model="journey.data.name" placeholder="My Journey" type="text" disabled)
div( div(

4948
yarn.lock

File diff suppressed because it is too large Load Diff