init
This commit is contained in:
commit
3c0bd54d54
12
.drone.yml
Normal file
12
.drone.yml
Normal file
@ -0,0 +1,12 @@
|
||||
pipeline:
|
||||
deploy-production:
|
||||
image: docker/compose
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
commands:
|
||||
- docker-compose -p otm down
|
||||
- docker-compose build --no-cache otm
|
||||
- docker-compose -p otm up -d
|
||||
when:
|
||||
event: push
|
||||
branch: [master]
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
package-lock.json
|
||||
node_modules/*
|
||||
auth/*
|
||||
db/*
|
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM node
|
||||
|
||||
RUN apt-get update && apt-get install -y pandoc pandoc-data texlive-latex-recommended pandoc-citeproc texlive-latex-extra python python-pip
|
||||
RUN pip install pandocfilters
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
# Install app dependencies
|
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||
# where available (npm@5+)
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
# If you are building your code for production
|
||||
# RUN npm ci --only=production
|
||||
# Bundle app source
|
||||
COPY . .
|
||||
EXPOSE 8080
|
||||
CMD [ "node", "server.js" ]
|
36
auth_db.js
Normal file
36
auth_db.js
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict'
|
||||
|
||||
const fp = require('fastify-plugin')
|
||||
const levelup = require('levelup')
|
||||
const leveldown = require('leveldown')
|
||||
const encode = require('encoding-down')
|
||||
|
||||
// mostly from level-packager
|
||||
const levelMore = (location) => {
|
||||
;['destroy', 'repair'].forEach(function (m) {
|
||||
if (typeof leveldown[m] === 'function') {
|
||||
levelMore[m] = () => leveldown[m].apply(leveldown, arguments)
|
||||
}
|
||||
})
|
||||
|
||||
return levelup(encode(leveldown(location), { }), { })
|
||||
}
|
||||
|
||||
levelMore.errors = levelup.errors
|
||||
|
||||
function levelPlugin (fastify, opt, next) {
|
||||
fastify
|
||||
.decorate('lauth', levelMore('auth'))
|
||||
.addHook('onClose', close)
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
function close (fastify, done) {
|
||||
fastify.level.close(done)
|
||||
}
|
||||
|
||||
module.exports = fp(levelPlugin, {
|
||||
fastify: '>=1.0.0',
|
||||
name: 'level_auth'
|
||||
});
|
21
compile_pandoc.js
Normal file
21
compile_pandoc.js
Normal file
@ -0,0 +1,21 @@
|
||||
const pandoc = require('node-pandoc');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = (KEY)=>{
|
||||
|
||||
this.key = KEY;
|
||||
|
||||
this.compile_cv = (src,style)=>{
|
||||
return new Promise((resolve,reject)=>{
|
||||
style = "default-otm";
|
||||
let args = `-s --section-divs --template template/${style}.html -f markdown+raw_html+header_attributes+definition_lists+yaml_metadata_block -t html5`;
|
||||
/* --variable=date:'$(DATE)' \ */
|
||||
pandoc(src, args, (err, res)=>{
|
||||
if (err) reject(err);
|
||||
return resolve(res);
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
70
compile_sass.js
Normal file
70
compile_sass.js
Normal file
@ -0,0 +1,70 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const mkdirp = require('mkdirp');
|
||||
const sass = require('node-sass');
|
||||
|
||||
const nodeEnv = process.env.NODE_ENV;
|
||||
|
||||
module.exports = {
|
||||
compileSass,
|
||||
compileSassProduction,
|
||||
compileSassMain
|
||||
};
|
||||
|
||||
function compileSass(sassFile) {
|
||||
const sassOptions = {
|
||||
file: sassFile
|
||||
};
|
||||
|
||||
if (nodeEnv !== 'production') {
|
||||
sassOptions.sourceMapEmbed = true;
|
||||
}
|
||||
else {
|
||||
sassOptions.outputStyle = 'compressed';
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
sass.render(sassOptions, (error, result) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve(result.css.toString());
|
||||
});
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
function compileSassProduction(sassFile) {
|
||||
const fullSassPath = path.join(__dirname, 'public/scss/', sassFile);
|
||||
const cssFile = sassFile.replace('.scss', '.css');
|
||||
const cssPath = path.join(__dirname, 'public/css/');
|
||||
const fullCssPath = path.join(cssPath, cssFile);
|
||||
|
||||
return compileSass(fullSassPath).then(css => {
|
||||
return new Promise((resolve, reject) => {
|
||||
mkdirp(cssPath, error => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}).then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.writeFile(fullCssPath, css, error => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
resolve(cssFile);
|
||||
});
|
||||
});
|
||||
}).catch(console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function compileSassMain() {
|
||||
return compileSassProduction('index.scss').then(() => {
|
||||
console.log('Created index.css');
|
||||
}).catch(console.error);
|
||||
}
|
19
docker-compose.yml
Normal file
19
docker-compose.yml
Normal file
@ -0,0 +1,19 @@
|
||||
version: "3.7"
|
||||
networks:
|
||||
external:
|
||||
external: true
|
||||
|
||||
services:
|
||||
otm:
|
||||
build: ./
|
||||
container_name: otm
|
||||
networks:
|
||||
- external
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.otm.entrypoints=web-secure"
|
||||
- "traefik.http.routers.otm.rule=Host(`otm.helcel.net`)"
|
||||
- "traefik.http.routers.otm.tls=true"
|
||||
- "traefik.http.services.otm.loadbalancer.server.port=8080"
|
||||
- "traefik.docker.network=external"
|
||||
restart: always
|
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "volp",
|
||||
"version": "1.0.0",
|
||||
"description": "Visual Online Language Processor",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.helcel.net:sora/volp.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"ejs": "^2.7.4",
|
||||
"fastify": "^2.10.0",
|
||||
"fastify-cookie": "^3.2.0",
|
||||
"fastify-jwt": "^1.2.0",
|
||||
"fastify-leveldb": "^2.1.0",
|
||||
"fastify-static": "^2.5.0",
|
||||
"fastify-websocket": "^1.0.0",
|
||||
"node-pandoc": "^0.3.0",
|
||||
"node-sass": "^6.0.0",
|
||||
"point-of-view": "^3.7.0",
|
||||
"ws": "^7.2.0"
|
||||
}
|
||||
}
|
4
public/css/font-awesome.min.css
vendored
Normal file
4
public/css/font-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
4009
public/css/index.css
Normal file
4009
public/css/index.css
Normal file
File diff suppressed because one or more lines are too long
BIN
public/fonts/fa-brands.ttf
Normal file
BIN
public/fonts/fa-brands.ttf
Normal file
Binary file not shown.
BIN
public/fonts/fa-regular.ttf
Normal file
BIN
public/fonts/fa-regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/fa-solid.ttf
Normal file
BIN
public/fonts/fa-solid.ttf
Normal file
Binary file not shown.
BIN
public/img/customizable.png
Normal file
BIN
public/img/customizable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
BIN
public/img/helcel.ico
Normal file
BIN
public/img/helcel.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
public/img/helcel.png
Executable file
BIN
public/img/helcel.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
15073
public/img/helcel.svg
Executable file
15073
public/img/helcel.svg
Executable file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 1.4 MiB |
BIN
public/img/lightweight.png
Normal file
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
BIN
public/img/opensource.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
BIN
public/js/.DS_Store
vendored
Normal file
BIN
public/js/.DS_Store
vendored
Normal file
Binary file not shown.
7
public/js/jquery.min.js
vendored
Normal file
7
public/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
242
public/js/main.js
Normal file
242
public/js/main.js
Normal file
@ -0,0 +1,242 @@
|
||||
const gen_id = (length)=>{
|
||||
var result = '';
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const len = characters.length;
|
||||
for (let i = 0; i < length; i++ ) {
|
||||
result += characters.charAt(Math.floor(Math.random() * len));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const query_nominatim = (q,f)=>{
|
||||
const ENDPOINT = 'https://nominatim.openstreetmap.org/';
|
||||
const FORMAT = 'jsonv2';
|
||||
return axios.get(ENDPOINT, {
|
||||
params: {
|
||||
format: FORMAT,
|
||||
q:q,
|
||||
},
|
||||
}).then(res=>res.data).then(res=>res.filter(f));
|
||||
}
|
||||
|
||||
const query_flight = (q)=>{
|
||||
const ENDPOINT = '/api/flight/'+q;
|
||||
const FORMAT = '-';
|
||||
return axios.get(ENDPOINT).then(res=>res.data);
|
||||
}
|
||||
|
||||
const is_restauration_type = (t)=>{
|
||||
const arr = ["restaurant", "cafe", "pub", "bar", "fast_food", "food_court"];
|
||||
return arr.indexOf(t)!=-1;
|
||||
}
|
||||
|
||||
const is_attraction_type = (item)=>{
|
||||
const arr = ["tourism"];
|
||||
return arr.indexOf(item.category)!=-1;
|
||||
}
|
||||
|
||||
const icon_type = (item)=>{
|
||||
let t = item.type
|
||||
const arr = ["restaurant", "cafe", "pub", "bar", "fast_food", "food_court"];
|
||||
if(arr.indexOf(t)!=-1){
|
||||
return 'utensils';
|
||||
}else if(t=='hotel'){
|
||||
return 'bed';
|
||||
}else if(t=='museum'){
|
||||
return 'landmark';
|
||||
}else if(t=='peak'){
|
||||
return 'mountain';
|
||||
}else if(t=='parking'){
|
||||
return 'parking';
|
||||
}else if(t=='water' || t=='river'){
|
||||
return 'water';
|
||||
}else if(t=='community_centre'){
|
||||
return 'building';
|
||||
}else if(t=='attraction'){
|
||||
return 'landmark';
|
||||
}else if(t=='information'){
|
||||
return 'landmark';
|
||||
}else if(t=='bridge'){
|
||||
return 'archway';
|
||||
}else if(t=='a'){
|
||||
return '';
|
||||
}else if(t=='a'){
|
||||
return '';
|
||||
}else{
|
||||
console.log(item.category, item.type);
|
||||
return 'question';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vue.component('l-map', window.Vue2Leaflet.LMap);
|
||||
Vue.component('l-tile-layer', window.Vue2Leaflet.LTileLayer);
|
||||
Vue.component('l-marker', window.Vue2Leaflet.LMarker);
|
||||
Vue.component('l-icon', window.Vue2Leaflet.LIcon);
|
||||
Vue.component('l-popup', window.Vue2Leaflet.LPopup);
|
||||
Vue.component('l-tooltip', window.Vue2Leaflet.LTooltip);
|
||||
Vue.component('l-control-scale', window.Vue2Leaflet.LControlScale);
|
||||
Vue.component('l-draggable', window.vuedraggable);
|
||||
Vue.use(window.VueTextareaAutosize.install)
|
||||
|
||||
Vue.component('multiselect', window.VueMultiselect.default)
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
journey_id : window.location.pathname.slice(1) || gen_id(16),
|
||||
journey_step: window.location.hash.slice(1)==""?-1:parseInt(window.location.hash.slice(1)),
|
||||
journey_step_data: {day:-1, section:-1},
|
||||
journey_data : {
|
||||
name: "New Journey",
|
||||
main:[],
|
||||
step_title:[],
|
||||
},
|
||||
query:{hotel:[],flight:[],nominatim:[]},
|
||||
querying:{hotel:false,flight:false,place:false,food:false},
|
||||
impexp:"",
|
||||
activities_tmp:[],
|
||||
},
|
||||
methods: {
|
||||
start_journey: function(event){
|
||||
window.location.href = '/'+this.journey_id;
|
||||
},
|
||||
add_section: function(event){
|
||||
if(this.journey_data.main===undefined) this.journey_data.main=[];
|
||||
this.journey_data.main.push({map:{zoom:2}, hotel:{latlon:[0,0]},places:{restaurants:[],places:[]}});
|
||||
},
|
||||
next_step: function(){
|
||||
this.journey_step+=1;
|
||||
this.journey_step_data = this.step_convert(this.journey_step);
|
||||
this.activities_tmp = this.journey_data.main[this.journey_step_data.section].places.activities.filter(e=>e.step==this.journey_step)
|
||||
window.location.hash = '#' + this.journey_step;
|
||||
|
||||
},
|
||||
first_step: function(){
|
||||
this.journey_step=-1;
|
||||
this.journey_step_data = {day:-1, section:-1};
|
||||
this.activities_tmp = [];
|
||||
window.location.hash = '';
|
||||
},
|
||||
step_convert: function(step){
|
||||
let steps_left = step+1;
|
||||
for(let e in this.journey_data.main){
|
||||
if(this.journey_data.main[e].dateRange && (this.journey_data.main[e].dateRange[0]-(new Date(0))) != 0){
|
||||
let cd = (this.journey_data.main[e].dateRange[1]-this.journey_data.main[e].dateRange[0])/(1000*60*60*24);
|
||||
if(cd>=steps_left){
|
||||
return {day: steps_left, section:e,start: (steps_left==1), end: (steps_left==cd)};
|
||||
}else{
|
||||
steps_left -= cd;
|
||||
}
|
||||
}else{
|
||||
steps_left -= 1;
|
||||
}
|
||||
if(steps_left==0){
|
||||
return {day:0,section:e, start:true,end:true};
|
||||
}
|
||||
}
|
||||
return {day:-1, section:-1};
|
||||
},
|
||||
rm_section: function(idx){
|
||||
this.journey_data.main.splice(idx,1);
|
||||
},
|
||||
search_nominatim: function(txt,f){
|
||||
if(txt==""){
|
||||
this.query.nominatim=[];
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return query_nominatim(txt,f)
|
||||
.then((results) =>{
|
||||
results.forEach(r=>r.latlon=[parseFloat(r.lat),parseFloat(r.lon)])
|
||||
this.query.nominatim=results;
|
||||
});
|
||||
},
|
||||
search_flight: function(txt){
|
||||
if(txt=="") return;
|
||||
this.querying.flight=true;
|
||||
query_flight(txt.replace(" ", ""))
|
||||
.then((results) =>{
|
||||
if(results.results==""){
|
||||
this.query.flight=[];this.querying.flight=false;return;
|
||||
}
|
||||
this.query.flight=results.results;
|
||||
this.querying.flight=false;
|
||||
});
|
||||
|
||||
},
|
||||
generate_icon: function(item){
|
||||
return L.AwesomeMarkers.icon(
|
||||
{icon: icon_type(item) || 'star', prefix: 'fa',markerColor: item.color || 'blue'}
|
||||
).createIcon().outerHTML
|
||||
},
|
||||
|
||||
save_data: function(){
|
||||
this.impexp = window.btoa(JSON.stringify(this.journey_data));
|
||||
axios.post('/api/'+this.journey_id, this.journey_data).then(response => {
|
||||
console.log("Saved...")
|
||||
}).catch(error => {
|
||||
console.warn('Error! Could not reach the API.')
|
||||
})
|
||||
},
|
||||
import_data:function(){
|
||||
this.journey_data = JSON.parse(window.atob(this.impexp));
|
||||
},
|
||||
export_data:function(){
|
||||
this.impexp = window.btoa(JSON.stringify(this.journey_data));
|
||||
},
|
||||
filter_selected:function(list,step){
|
||||
return list.filter(e=>(step?(e.step==this.journey_step):(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)},
|
||||
},
|
||||
created: function () {
|
||||
axios.get('/api/'+this.journey_id).then(response =>{
|
||||
app.journey_data = response.data;
|
||||
for(let e of app.journey_data.main){
|
||||
if(e.dateRange && e.dateRange.length===2){
|
||||
e.dateRange[0]= new Date(e.dateRange[0]);
|
||||
e.dateRange[1]= new Date(e.dateRange[1]);
|
||||
}
|
||||
}
|
||||
this.journey_step_data = this.step_convert(this.journey_step);
|
||||
this.activities_tmp = this.journey_data.main[this.journey_step_data.section].places.activities.filter(e=>e.step==this.journey_step);
|
||||
});
|
||||
this.debounceSave = _.debounce(this.save_data, 500)
|
||||
this.debounceSearch = {"hotel":_.debounce((q)=>{this.querying.hotel=true;this.search_nominatim(q,(r)=>(r.type=="hotel")).then((r)=>{this.querying.hotel=false});}, 500),
|
||||
"restaurants":_.debounce((q)=>{this.querying.food=true;this.search_nominatim(q,(r)=>(is_restauration_type(r.type))).then((r)=>{this.querying.food=false});}, 500),
|
||||
"places":_.debounce((q)=>{this.querying.place=true;this.search_nominatim(q,(r)=>(true)).then((r)=>{this.querying.place=false});}, 500),
|
||||
"other":_.debounce((q)=>{this.querying.any=true;this.search_nominatim(q,(r)=>(true)).then((r)=>{this.querying.any=false});}, 500),
|
||||
"flight":_.debounce((q)=>this.search_flight(q), 500)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
journey_data: {
|
||||
handler:function (ndata, odata){
|
||||
this.debounceSave()
|
||||
if(ndata.main.length == odata.main.length){
|
||||
for(let d in ndata.main){
|
||||
if(ndata.main[d].hotel != odata.main[d].hotel){
|
||||
if(ndata.main[d].hotel.latlon){
|
||||
this.journey_data.main[d].map.center=ndata.main[d].hotel.latlon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
activities_tmp:{
|
||||
handler:function (nd,od){
|
||||
nd.forEach(e=>e.step=this.journey_step);
|
||||
},
|
||||
deep: false,
|
||||
},
|
||||
},
|
||||
})
|
BIN
public/scss/.DS_Store
vendored
Normal file
BIN
public/scss/.DS_Store
vendored
Normal file
Binary file not shown.
261
public/scss/_settings.scss
Normal file
261
public/scss/_settings.scss
Normal file
@ -0,0 +1,261 @@
|
||||
/**
|
||||
* SETTINGS
|
||||
*/
|
||||
|
||||
$enable-grid-classes: true !default; // generate grid classes
|
||||
$enable-reset-defaults: true !default; // reset default browser values
|
||||
|
||||
// colors
|
||||
$c-white: #fff !default;
|
||||
$c-black: #000 !default;
|
||||
$c-success: #4caf50 !default;
|
||||
$c-info: #5bc0de !default;
|
||||
$c-warning: #f0ad4e !default;
|
||||
$c-error: #e74c3c !default;
|
||||
$c-gray: #969da6 !default;
|
||||
$c-gray-light: #eceff1 !default;
|
||||
|
||||
$c-primary: #03a9f4 !default;
|
||||
$c-secondary: #e91e63 !default;
|
||||
$c-dark: #18232f !default;
|
||||
$c-light: $c-gray-light !default;
|
||||
|
||||
$c-text: #272727 !default;
|
||||
$c-link: $c-primary !default;
|
||||
$c-link-hover: rgba($c-primary, .8) !default;
|
||||
|
||||
$bgc-body: $c-white !default;
|
||||
|
||||
$c-map: (
|
||||
'primary': $c-primary,
|
||||
'dark': $c-dark,
|
||||
'secondary': $c-secondary,
|
||||
'white': $c-white,
|
||||
'success': $c-success,
|
||||
'info': $c-info,
|
||||
'warning': $c-warning,
|
||||
'error': $c-error,
|
||||
'gray': $c-gray,
|
||||
'gray-light': $c-gray-light
|
||||
) !default;
|
||||
|
||||
// Social colors
|
||||
$c-fb: rgb(59, 89, 182) !default;
|
||||
$c-tw: rgb(64, 153, 255) !default;
|
||||
$c-googleplus: rgb(215, 61, 50) !default;
|
||||
$c-pinterest: rgb(203, 32, 39) !default;
|
||||
$c-skype: rgb(18, 165, 244) !default;
|
||||
$c-spotify: rgb(129, 183, 26) !default;
|
||||
$c-instagram: #4e433c !default;
|
||||
$c-tumblr: #2b4964 !default;
|
||||
$c-vimeo: #86b32d !default;
|
||||
$c-soundcloud: #f76600 !default;
|
||||
$c-youtube: #ff3333 !default;
|
||||
$c-linkedin: #4875b4 !default;
|
||||
$c-flickr: #fe0883 !default;
|
||||
$c-foursquare: #8fd400 !default;
|
||||
|
||||
// Shadows
|
||||
$bxsh-base: 0 0 5px rgba(0, 0, 0, .2) !default;
|
||||
$bxsh-medium: $bxsh-base !default;
|
||||
|
||||
// Typography
|
||||
$fw-light: 300 !default;
|
||||
$fw-normal: 400 !default;
|
||||
$fw-bold: 600 !default;
|
||||
$fw-headings: $fw-light !default;
|
||||
|
||||
$ff-base: 'IBM Plex Sans', sans-serif !default;
|
||||
$fw-base: 400 !default;
|
||||
$fz-base: 16px !default;
|
||||
$lh-base: 1.5em !default;
|
||||
|
||||
$fz-huge: 36px !default;
|
||||
$fz-big: 24px !default;
|
||||
$fz-medium: $fz-base !default;
|
||||
$fz-small: 12px !default;
|
||||
|
||||
$lh-huge: 1.3em !default;
|
||||
$lh-big: 1.3em !default;
|
||||
$lh-medium: $lh-base !default;
|
||||
$lh-small: 1.3em !default;
|
||||
|
||||
// Body
|
||||
$c-body: $c-text !default;
|
||||
$ff-body: $ff-base !default;
|
||||
$fz-body: $fz-base !default;
|
||||
$fw-body: $fw-base !default;
|
||||
$lh-body: $lh-base !default;
|
||||
|
||||
// Border
|
||||
$bdc-base: #d5d9db !default;
|
||||
$bd-medium: 1px solid $bdc-base !default;
|
||||
$bdr-base: 3px !default;
|
||||
$bdr-medium: $bdr-base !default;
|
||||
$bdr-rounded-corners: 5px !default;
|
||||
|
||||
// Defaults (HTML elements)
|
||||
$fw-strong: $fw-bold !default;
|
||||
$mb-p: 1.5em !default;
|
||||
$mb-ul: 1em !default;
|
||||
$mb-li: .5em !default;
|
||||
|
||||
// Margin
|
||||
$m-xsmall: 4px !default;
|
||||
$m-small: 8px !default;
|
||||
$m-medium: 16px !default;
|
||||
$m-big: 36px !default;
|
||||
$m-huge: 48px !default;
|
||||
|
||||
// Padding
|
||||
$p-small: 4px !default;
|
||||
$p-medium: 8px !default;
|
||||
$p-big: 16px !default;
|
||||
$p-huge: 36px !default;
|
||||
|
||||
// Layout
|
||||
$bgc-container: transparent !default;
|
||||
$mwi-container: 1380px !default;
|
||||
$mwi-container-medium: 944px !default;
|
||||
$mwi-container-small: 400px !default;
|
||||
$w-gutter: 24px !default;
|
||||
|
||||
// Grid
|
||||
$grid-breakpoints: (
|
||||
xs: 0,
|
||||
sm: 576px,
|
||||
md: 768px,
|
||||
lg: 992px,
|
||||
xl: 1200px
|
||||
) !default;
|
||||
|
||||
$grid-columns: 12 !default;
|
||||
$grid-gutter-width: $w-gutter !default;
|
||||
|
||||
$container-max-widths: (
|
||||
sm: 540px,
|
||||
md: 720px,
|
||||
lg: 960px,
|
||||
xl: 1140px
|
||||
) !default;
|
||||
|
||||
/**
|
||||
* COMPONENTS
|
||||
*/
|
||||
|
||||
// Badges
|
||||
$c-badge: $c-text !default;
|
||||
$c-badge-colored: $c-white !default;
|
||||
$bg-badge: $c-light !default;
|
||||
$bdr-badge: $bdr-medium !default;
|
||||
$lh-badge: 1.2em !default;
|
||||
$p-badge: $p-medium $p-big !default;
|
||||
$fz-badge-big: 1.3em !default;
|
||||
$fz-badge-small: .7em !default;
|
||||
$badges-color-map: (
|
||||
primary: $c-primary,
|
||||
secondary: $c-secondary,
|
||||
dark: $c-dark,
|
||||
light: $c-gray,
|
||||
success: $c-success,
|
||||
error: $c-error,
|
||||
warning: $c-warning
|
||||
) !default;
|
||||
|
||||
// Buttons
|
||||
$bg-button: $c-primary !default;
|
||||
$c-button: $c-white !default;
|
||||
$c-button-transparent: $c-primary !default;
|
||||
$c-button-outlined: $c-primary !default;
|
||||
$bdc-button: $bg-button !default;
|
||||
$bd-button: 1px solid $bdc-button !default;
|
||||
$bdr-button: 200px !default;
|
||||
$ff-button: $ff-body !default;
|
||||
$fz-button: $fz-medium !default;
|
||||
$fw-button: $fw-bold !default;
|
||||
$lh-button: $lh-body !default;
|
||||
$m-button: 0 $m-small $m-medium $m-small !default;
|
||||
$p-button: $p-medium $p-big !default;
|
||||
$tt-button: uppercase !default;
|
||||
|
||||
$fz-button-big: $fz-big !default;
|
||||
$p-button-big: $p-big $p-huge !default;
|
||||
|
||||
$fz-button-small: $fz-small !default;
|
||||
$p-button-small: $p-small $p-big !default;
|
||||
|
||||
// Forms
|
||||
$bgc-form: $c-light !default;
|
||||
$c-form: $c-text !default;
|
||||
$bdr-form: $bdr-medium !default;
|
||||
$bd-form: $bd-medium !default;
|
||||
$fz-form: $fz-body !default;
|
||||
$bgc-form-focus: lighten($bgc-form, 5%) !default;
|
||||
$trs-form: background-color .2s ease-in-out !default;
|
||||
$c-form-placeholder: $c-gray !default;
|
||||
$mih-form-textarea: 120px !default;
|
||||
$p-form: $p-medium $p-big !default;
|
||||
$size-form-input-icon: 16px !default;
|
||||
$c-form-input-icon: $c-gray !default;
|
||||
$bd-form-error: 1px solid $c-error !default;
|
||||
$bgc-form-error: $bgc-form !default;
|
||||
|
||||
// Loading bar
|
||||
$h-loading-bar: 6px !default;
|
||||
$w-loading-bar: 300px !default;
|
||||
$z-loading-bar: 1000 !default;
|
||||
|
||||
// Loading spinner
|
||||
$bg-loading-spinner: $c-secondary !default;
|
||||
$size-loading-spinner-inner: 9px !default;
|
||||
$size-loading-spinner: 30px !default;
|
||||
$z-loading-spinner: 1000 !default;
|
||||
|
||||
// Notification
|
||||
$bdr-notification: $bdr-medium !default;
|
||||
$c-notification: $c-white !default;
|
||||
$mb-notification: $m-big !default;
|
||||
$p-notification: $p-big !default;
|
||||
|
||||
// Paginator
|
||||
$mr-paginator-item: $m-xsmall !default;
|
||||
$bdr-paginator-item: $bdr-medium !default;
|
||||
$bg-paginator-item: $c-light !default;
|
||||
$c-paginator-item: $c-text !default;
|
||||
$bg-paginator-item-active: $c-primary !default;
|
||||
$c-paginator-item-active: $c-white !default;
|
||||
$p-paginator-item: $p-medium $p-big !default;
|
||||
|
||||
// Table
|
||||
$bgc-table-header: $bgc-body !default;
|
||||
$bgc-table: $c-light !default;
|
||||
$bd-table: $bd-medium !default;
|
||||
$c-table-heading: $c-gray !default;
|
||||
$c-table-text: $c-text !default;
|
||||
$fw-table-heading: normal !default;
|
||||
$mwi-table: 100% !default;
|
||||
$p-table-cell: $p-medium !default;
|
||||
$p-table-heading: $p-medium !default;
|
||||
$w-table: 100% !default;
|
||||
$fz-table-heading: $fz-small !default;
|
||||
|
||||
// Tabs
|
||||
$bdb-tabs-item-selected: 3px solid $c-primary !default;
|
||||
$bdb-tabs-item: 3px solid transparent !default;
|
||||
$bdb-tabs: $bd-medium !default;
|
||||
$c-tabs-item-hover: $c-primary !default;
|
||||
$c-tabs-item-selected: $c-primary !default;
|
||||
$c-tabs-item: $c-gray !default;
|
||||
$m-tabs-item: 0 $m-medium 0 0 !default;
|
||||
$miw-tabs-item: 70px !default;
|
||||
$p-tabs-item: $p-big !default;
|
||||
|
||||
// Tags
|
||||
$bg-tag-dot: $bgc-body !default;
|
||||
$bg-tag: $c-light !default;
|
||||
$bdr-tag: $bdr-medium 0 0 $bdr-medium !default;
|
||||
$c-tag: $c-text !default;
|
||||
$lh-tag: 16px !default;
|
||||
$m-tag: 0 $m-medium $m-medium 0 !default;
|
||||
$p-tag: $p-medium !default;
|
||||
$size-tag-dot: 5px !default;
|
36
public/scss/components/_badge.scss
Normal file
36
public/scss/components/_badge.scss
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* BADGE
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <ul>
|
||||
* <li class="badge badge--primary">Badge 1</li>
|
||||
* <li class="badge badge--secondary badge--big">Badge 1</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
|
||||
.badge {
|
||||
background-color: $bg-badge;
|
||||
border-radius: $bdr-badge;
|
||||
color: $c-badge;
|
||||
display: inline-block;
|
||||
line-height: $lh-badge;
|
||||
padding: $p-badge;
|
||||
|
||||
&--big {
|
||||
font-size: $fz-badge-big;
|
||||
}
|
||||
|
||||
&--small {
|
||||
font-size: $fz-badge-small;
|
||||
}
|
||||
|
||||
@each $type, $color in $badges-color-map {
|
||||
&--#{$type} {
|
||||
background-color: $color;
|
||||
color: $c-badge-colored;
|
||||
}
|
||||
}
|
||||
}
|
149
public/scss/components/_buttons.scss
Normal file
149
public/scss/components/_buttons.scss
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* BUTTON
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <button class="button">Default</button>
|
||||
* <button class="button button--big">Button big</button>
|
||||
* <button class="button button--secondary">Button secondary</button>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// reset default buttons
|
||||
button {
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: $bg-button;
|
||||
border: 0;
|
||||
border-radius: $bdr-button;
|
||||
color: $c-button;
|
||||
display: inline-block;
|
||||
font-family: $ff-button;
|
||||
font-size: $fz-button;
|
||||
font-weight: $fw-button;
|
||||
line-height: $lh-button;
|
||||
margin: $m-button;
|
||||
padding: $p-button;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: $tt-button;
|
||||
transition: opacity .2s ease-in-out;
|
||||
white-space: nowrap;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $c-button;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($c-primary, .8);
|
||||
color: $c-white;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
// sizes
|
||||
&--big {
|
||||
font-size: $fz-button-big;
|
||||
padding: $p-button-big;
|
||||
}
|
||||
|
||||
&--small {
|
||||
font-size: $fz-button-small;
|
||||
padding: $p-button-small;
|
||||
}
|
||||
|
||||
&--mobileFull {
|
||||
@include media-breakpoint-down(md) {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// colors
|
||||
&--secondary {
|
||||
background-color: $c-secondary;
|
||||
color: $c-white;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($c-secondary, .8);
|
||||
color: $c-white;
|
||||
}
|
||||
}
|
||||
|
||||
&--white {
|
||||
background-color: $c-white;
|
||||
color: $c-primary;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($c-white, .8);
|
||||
color: rgba($c-primary, .8);
|
||||
}
|
||||
}
|
||||
|
||||
&--green {
|
||||
background-color: $c-success;
|
||||
color: $c-white;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($c-success, .8);
|
||||
color: $c-white;
|
||||
}
|
||||
}
|
||||
|
||||
&--red {
|
||||
background-color: $c-error;
|
||||
color: $c-white;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($c-error, .8);
|
||||
color: $c-white;
|
||||
}
|
||||
}
|
||||
|
||||
&--transparent {
|
||||
background-color: transparent;
|
||||
color: $c-button-transparent;
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
color: rgba($c-button-transparent, .8);
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
&--outlined {
|
||||
background-color: transparent;
|
||||
border: $bd-medium;
|
||||
color: $c-button-outlined;
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
color: rgba($c-button-outlined, .8);
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
}
|
398
public/scss/components/_forms.scss
Normal file
398
public/scss/components/_forms.scss
Normal file
@ -0,0 +1,398 @@
|
||||
/**
|
||||
* FORMS
|
||||
*
|
||||
* Markup:
|
||||
* ---------
|
||||
* <div class="input input-fullWidth">
|
||||
* <input id="#" placeholder="#" type="text">
|
||||
* </div>
|
||||
*
|
||||
* <div class="select select-fullWidth">
|
||||
* <select name="#" id="#">
|
||||
* <option>Option 1</option>
|
||||
* <option>Option 2</option>
|
||||
* </select>
|
||||
* </div>
|
||||
*
|
||||
* <div class="textarea">
|
||||
* <textarea id="#"></textarea>
|
||||
* </div>
|
||||
*
|
||||
* <div class="radio">
|
||||
* <input id="#" name="#" type="radio" value="">
|
||||
* <label for="#">Radio button</label>
|
||||
* </div>
|
||||
*
|
||||
* <div class="checkbox">
|
||||
* <input id="#" name="#" type="checkbox" value="">
|
||||
* <label for="#">Checkbox</label>
|
||||
* </div>
|
||||
*/
|
||||
|
||||
|
||||
// prevent input default appearence on webkit
|
||||
input,
|
||||
textarea {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
// Disable webkit background color
|
||||
input:-webkit-autofill {
|
||||
box-shadow: 0 0 0 1000px $bgc-form inset;
|
||||
}
|
||||
|
||||
// Common styles
|
||||
.textarea,
|
||||
.input,
|
||||
.select {
|
||||
border: $bd-form;
|
||||
border-radius: $bdr-form;
|
||||
box-shadow: none;
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.has-error {
|
||||
background: $bgc-form-error;
|
||||
border: $bd-form-error;
|
||||
margin-bottom: 0; // we should display an error message
|
||||
}
|
||||
}
|
||||
|
||||
.select {
|
||||
background-color: $bgc-form;
|
||||
display: inline-block;
|
||||
margin-right: $m-medium;
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&-fullWidth {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
color: $c-form;
|
||||
display: block;
|
||||
font-size: $fz-form;
|
||||
line-height: $lh-medium;
|
||||
margin: 0;
|
||||
padding: $p-form;
|
||||
padding-right: 30px;
|
||||
transition: $trs-form;
|
||||
width: 100%;
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $bgc-form-focus;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
background: $c-primary;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
margin-top: 2px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 50%;
|
||||
transform-origin: 1px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: rotate(-135deg);
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
|
||||
// textarea
|
||||
.textarea {
|
||||
background-color: $bgc-form;
|
||||
padding: 0;
|
||||
|
||||
&-fullWidth {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: $c-form;
|
||||
display: block;
|
||||
font-family: $ff-body;
|
||||
font-size: $fz-form;
|
||||
line-height: $lh-medium;
|
||||
margin: 0;
|
||||
min-height: $mih-form-textarea;
|
||||
padding: $p-form;
|
||||
transition: $trs-form;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: $c-form-placeholder;
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
background-color: $bgc-form-focus;
|
||||
border: 0;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.checkbox {
|
||||
margin-bottom: $m-small;
|
||||
position: relative;
|
||||
|
||||
input[type='checkbox'] {
|
||||
display: none;
|
||||
|
||||
&:checked + label::after {
|
||||
animation: checkboxAndRadioAnimation .25s;
|
||||
content: '';
|
||||
transform: scale(1) rotate(45deg);
|
||||
}
|
||||
|
||||
& + label {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding-left: 30px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
background-color: $bgc-form;
|
||||
border: $bd-form;
|
||||
border-radius: $bdr-medium;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
margin-top: -10px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-bottom: 3px solid $c-primary;
|
||||
border-right: 3px solid $c-primary;
|
||||
display: block;
|
||||
height: 12px;
|
||||
left: 11px;
|
||||
margin-left: -4px;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 7px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio {
|
||||
margin-bottom: $m-small;
|
||||
position: relative;
|
||||
|
||||
input[type='radio'] {
|
||||
display: none;
|
||||
|
||||
&:checked + label::after {
|
||||
animation: checkboxAndRadioAnimation .25s;
|
||||
content: '';
|
||||
transform: scale(1) rotate(45deg);
|
||||
}
|
||||
|
||||
& + label {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding-left: 30px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&::before {
|
||||
background-color: $bgc-form;
|
||||
border: $bd-form;
|
||||
border-radius: 20px;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
left: 0;
|
||||
margin-top: -10px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $c-primary;
|
||||
border-radius: 20px;
|
||||
display: block;
|
||||
height: 10px;
|
||||
left: 11px;
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes checkboxAndRadioAnimation {
|
||||
0% {
|
||||
transform: scale(0) rotate(45deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.5) rotate(45deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.input {
|
||||
background-color: $bgc-form;
|
||||
margin-right: 10px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
:focus,
|
||||
:active {
|
||||
background-color: $bgc-form-focus;
|
||||
border-radius: $bdr-form;
|
||||
}
|
||||
|
||||
input {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
color: $c-form;
|
||||
font-size: $fz-form;
|
||||
line-height: $lh-medium;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: $p-form;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: $c-form-placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
&-withIcon {
|
||||
input {
|
||||
padding-right: $size-form-input-icon * 2;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
fill: $c-form-input-icon;
|
||||
height: $size-form-input-icon;
|
||||
margin-top: -$size-form-input-icon / 2;
|
||||
position: absolute;
|
||||
right: $m-medium;
|
||||
top: 50%;
|
||||
width: $size-form-input-icon;
|
||||
}
|
||||
|
||||
// input variations
|
||||
&-fullWidth {
|
||||
display: block;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FORM COLLAPSED (items in row without gap between them)
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <div class="formCollapsed">
|
||||
* <div class="input formCollapsed-item formCollapsed-itemPrimary">
|
||||
* <input id="name" placeholder="Klingon search" type="text" />
|
||||
* </div>
|
||||
* <div class="select formCollapsed-item">
|
||||
* <select name="country-code" id="country-code">
|
||||
* <option value="AO"> Angola</option>
|
||||
* </select>
|
||||
* </div>
|
||||
* <button class="formCollapsed-item button button-primary">
|
||||
* Search
|
||||
* </button>
|
||||
* </div>
|
||||
**/
|
||||
|
||||
.formCollapsed {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&-item {
|
||||
border-radius: 0 !important;
|
||||
margin: 0 !important;
|
||||
|
||||
|
||||
&:first-child {
|
||||
border-bottom-left-radius: $bdr-form !important;
|
||||
border-top-left-radius: $bdr-form !important;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-right-radius: $bdr-form !important;
|
||||
border-top-right-radius: $bdr-form !important;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
&Primary {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
57
public/scss/components/_loading-bar.scss
Normal file
57
public/scss/components/_loading-bar.scss
Normal file
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* LOADING BAR
|
||||
*
|
||||
* Markup:
|
||||
* ---------
|
||||
* <div class="loadingBar"></div>
|
||||
*
|
||||
*/
|
||||
|
||||
.loadingBar {
|
||||
height: $h-loading-bar;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: $z-loading-bar;
|
||||
|
||||
&::before {
|
||||
animation: loading 2s linear infinite;
|
||||
background-color: $c-primary;
|
||||
content: '';
|
||||
display: block;
|
||||
height: $h-loading-bar;
|
||||
left: -$w-loading-bar;
|
||||
position: absolute;
|
||||
width: $w-loading-bar;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
from {
|
||||
left: -$w-loading-bar;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
70% {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
80% {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
95% {
|
||||
left: 120%;
|
||||
}
|
||||
|
||||
to {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
139
public/scss/components/_loading-spinner.scss
Normal file
139
public/scss/components/_loading-spinner.scss
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
* LOADING SPINNER
|
||||
*
|
||||
* Markup:
|
||||
* ---------
|
||||
* <div class='loadingSpinner'>
|
||||
* <span class='loadingSpinner-inner'></span>
|
||||
* <span class='loadingSpinner-inner'></span>
|
||||
* <span class='loadingSpinner-inner'></span>
|
||||
* <span class='loadingSpinner-inner'></span>
|
||||
* </div>
|
||||
*
|
||||
*/
|
||||
|
||||
.loadingSpinner {
|
||||
animation: rotateLoader 4s infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
display: block;
|
||||
height: $size-loading-spinner;
|
||||
left: 50%;
|
||||
margin-left: -$size-loading-spinner / 2;
|
||||
margin-top: -$size-loading-spinner / 2;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
width: $size-loading-spinner;
|
||||
z-index: $z-loading-spinner;
|
||||
|
||||
&-inner {
|
||||
animation-timing-function: ease-in-out;
|
||||
background-color: $bg-loading-spinner;
|
||||
border-radius: 100%;
|
||||
display: block;
|
||||
height: $size-loading-spinner-inner;
|
||||
position: absolute;
|
||||
width: $size-loading-spinner-inner;
|
||||
|
||||
&:nth-child(1) {
|
||||
animation: translateBall1 1s infinite;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translate3d($size-loading-spinner-inner / 2, $size-loading-spinner-inner / 2, 0);
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
animation: translateBall2 1s infinite;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation: translateBall3 1s infinite;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
animation: translateBall4 1s infinite;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateLoader {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes translateBall1 {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d($size-loading-spinner-inner / 2, $size-loading-spinner-inner / 2, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
@keyframes translateBall2 {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(-$size-loading-spinner-inner / 2, $size-loading-spinner-inner / 2, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes translateBall3 {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d(-$size-loading-spinner-inner / 2, -$size-loading-spinner-inner / 2, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes translateBall4 {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate3d($size-loading-spinner-inner / 2, -$size-loading-spinner-inner / 2, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
31
public/scss/components/_notification.scss
Normal file
31
public/scss/components/_notification.scss
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* NOTIFICATION
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <div class="notification notification--sucess">Success notification</div>
|
||||
* <div class="notification notification--info">Success info</div>
|
||||
* <div class="notification notification--error">Success error</div>
|
||||
* <div class="notification notification--warning">Success warning</div>
|
||||
*
|
||||
*/
|
||||
|
||||
.notification {
|
||||
border-radius: $bdr-notification;
|
||||
color: $c-notification;
|
||||
margin-bottom: $mb-notification;
|
||||
padding: $p-notification;
|
||||
|
||||
p {
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@each $type, $color in $c-map {
|
||||
&--#{$type} {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
}
|
45
public/scss/components/_paginator.scss
Normal file
45
public/scss/components/_paginator.scss
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* PAGINATOR
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <ul class="paginator">
|
||||
* <li class="paginator-item">
|
||||
* <a href="#" class="paginator-itemLink">< Prev</a>
|
||||
* </li>
|
||||
* <li class="paginator-item">
|
||||
* <a href="#" class="paginator-itemLink">1</a>
|
||||
* </li>
|
||||
* <li class="paginator-item">
|
||||
* <a href="#" class="paginator-itemLink is-active">2</a>
|
||||
* </li>
|
||||
* <li class="paginator-item">
|
||||
* <a href="#" class="paginator-itemLink">3</a>
|
||||
* </li>
|
||||
* <li class="paginator-item">
|
||||
* <a href="#" class="paginator-itemLink">Next ></a>
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
|
||||
.paginator {
|
||||
|
||||
&-item {
|
||||
display: inline-block;
|
||||
margin-right: $mr-paginator-item;
|
||||
|
||||
&Link {
|
||||
background-color: $bg-paginator-item;
|
||||
border-radius: $bdr-paginator-item;
|
||||
display: block;
|
||||
padding: $p-paginator-item;
|
||||
|
||||
&.is-active {
|
||||
background-color: $bg-paginator-item-active;
|
||||
color: $c-paginator-item-active;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
public/scss/components/_table.scss
Normal file
123
public/scss/components/_table.scss
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* TABLE
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <table class="table">
|
||||
* <tr>
|
||||
* <th>First column</th>
|
||||
* <th>Second column</th>
|
||||
* <th>Third column</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td="First column">Blue</td>
|
||||
* <td="Second column">One</td>
|
||||
* <td="Third column">My life fades</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
.table {
|
||||
background-color: $bgc-table;
|
||||
border: $bd-table;
|
||||
border-collapse: collapse;
|
||||
color: $c-table-text;
|
||||
max-width: $mwi-table;
|
||||
width: $w-table;
|
||||
|
||||
th,
|
||||
td {
|
||||
border-bottom: $bd-table;
|
||||
padding: $p-table-cell;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
thead {
|
||||
border-bottom: $bd-table;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: $bgc-table-header;
|
||||
color: $c-table-heading;
|
||||
font-size: $fz-table-heading;
|
||||
font-weight: $fw-table-heading;
|
||||
padding: $p-table-heading;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TABLE RESPONSIVE
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <table class="table table--responsive">
|
||||
* <tr>
|
||||
* <th>First column</th>
|
||||
* <th>Second column</th>
|
||||
* <th>Third column</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td data-th="First column">Blue</td>
|
||||
* <td data-th="Second column">One</td>
|
||||
* <td data-th="Third column">My life fades</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td data-th="First column">Green</td>
|
||||
* <td data-th="Second column">Two</td>
|
||||
* <td data-th="Third column">
|
||||
* when the world was powered by the black fuel... and the desert
|
||||
* sprouted great cities of pipe and steel.
|
||||
* </td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td data-th="First column">Yellow</td>
|
||||
* <td data-th="Second column">Three</td>
|
||||
* <td data-th="Third column">
|
||||
* A whirlwind of looting, a firestorm of fear.
|
||||
* </td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
*/
|
||||
|
||||
.table--responsive {
|
||||
|
||||
th {
|
||||
display: none;
|
||||
}
|
||||
|
||||
td {
|
||||
@include media-breakpoint-down(sm) {
|
||||
border: 0;
|
||||
}
|
||||
display: block;
|
||||
|
||||
&::before {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: none;
|
||||
}
|
||||
color: $c-table-heading;
|
||||
content: attr(data-th)': ';
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: $bd-table;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
@include media-breakpoint-up(sm) {
|
||||
border-top: $bd-table;
|
||||
display: table-cell;
|
||||
}
|
||||
text-align: left;
|
||||
}
|
||||
}
|
37
public/scss/components/_tabs.scss
Normal file
37
public/scss/components/_tabs.scss
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* TABS
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <div class="tabs">
|
||||
* <a href="#" title="#" class="tabs-item">[...]</a>
|
||||
* <a href="#" title="#" class="tabs-item is-selected">[...]</a>
|
||||
* </div>
|
||||
*
|
||||
*/
|
||||
|
||||
.tabs {
|
||||
border-bottom: $bdb-tabs;
|
||||
text-align: center;
|
||||
|
||||
&-item {
|
||||
border-bottom: $bdb-tabs-item;
|
||||
color: $c-tabs-item;
|
||||
display: inline-block;
|
||||
margin: $m-tabs-item;
|
||||
min-width: $miw-tabs-item;
|
||||
padding: $p-tabs-item;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
color: $c-tabs-item-hover;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.is-selected {
|
||||
border-bottom: $bdb-tabs-item-selected;
|
||||
color: $c-tabs-item-selected;
|
||||
}
|
||||
}
|
||||
}
|
62
public/scss/components/_tag.scss
Normal file
62
public/scss/components/_tag.scss
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* TAG
|
||||
*
|
||||
* Markup:
|
||||
* -------
|
||||
*
|
||||
* <ul>
|
||||
* <li class="tag">Fantasy</li>
|
||||
* <li class="tag">Fiction</li>
|
||||
* <li class="tag">Contemporary</li>
|
||||
* </ul>
|
||||
*
|
||||
* <ul>
|
||||
* <li class="tag">
|
||||
* <a href="#">Fantasy</a>
|
||||
* </li>
|
||||
* <li class="tag">
|
||||
* <a href="#">Fiction</a>
|
||||
* </li>
|
||||
* <li class="tag">
|
||||
* <a href="#">Contemporary</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
|
||||
.tag {
|
||||
background-color: $bg-tag;
|
||||
border-radius: $bdr-tag;
|
||||
color: $c-tag;
|
||||
display: inline-block;
|
||||
line-height: $lh-tag;
|
||||
margin: $m-tag;
|
||||
padding: $p-tag;
|
||||
position: relative;
|
||||
|
||||
// triangle arrow
|
||||
&::before {
|
||||
border-bottom: ($lh-tag / 2) + $p-tag solid transparent;
|
||||
border-left: $p-tag solid $bg-tag;
|
||||
border-top: ($lh-tag / 2) + $p-tag solid transparent;
|
||||
content: '';
|
||||
height: 0;
|
||||
position: absolute;
|
||||
right: -$p-tag;
|
||||
top: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
// dot
|
||||
&::after {
|
||||
background: $bg-tag-dot;
|
||||
border-radius: 100%;
|
||||
content: '';
|
||||
height: $size-tag-dot;
|
||||
margin-top: -$size-tag-dot / 2;
|
||||
position: absolute;
|
||||
right: -$size-tag-dot / 2;
|
||||
top: 50%;
|
||||
width: $size-tag-dot;
|
||||
}
|
||||
}
|
2072
public/scss/custom.scss
Normal file
2072
public/scss/custom.scss
Normal file
File diff suppressed because it is too large
Load Diff
69
public/scss/grid/_grid-framework.scss
Normal file
69
public/scss/grid/_grid-framework.scss
Normal file
@ -0,0 +1,69 @@
|
||||
// Framework grid generation
|
||||
//
|
||||
// Used only by Bootstrap to generate the correct number of grid classes given
|
||||
// any value of `$grid-columns`.
|
||||
|
||||
@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {
|
||||
// Common properties for all breakpoints
|
||||
%grid-column {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 1px; // Prevent columns from collapsing when empty
|
||||
padding-right: ($gutter / 2);
|
||||
padding-left: ($gutter / 2);
|
||||
}
|
||||
|
||||
@each $breakpoint in map-keys($breakpoints) {
|
||||
$infix: breakpoint-infix($breakpoint, $breakpoints);
|
||||
|
||||
// Allow columns to stretch full width below their breakpoints
|
||||
@for $i from 1 through $columns {
|
||||
.col#{$infix}-#{$i} {
|
||||
@extend %grid-column;
|
||||
}
|
||||
}
|
||||
.col#{$infix},
|
||||
.col#{$infix}-auto {
|
||||
@extend %grid-column;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up($breakpoint, $breakpoints) {
|
||||
// Provide basic `.col-{bp}` classes for equal-width flexbox columns
|
||||
.col#{$infix} {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
.col#{$infix}-auto {
|
||||
flex: 0 0 auto;
|
||||
width: auto;
|
||||
max-width: none; // Reset earlier grid tiers
|
||||
}
|
||||
|
||||
@for $i from 1 through $columns {
|
||||
.col#{$infix}-#{$i} {
|
||||
@include make-col($i, $columns);
|
||||
}
|
||||
}
|
||||
|
||||
.order#{$infix}-first {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
@for $i from 1 through $columns {
|
||||
.order#{$infix}-#{$i} {
|
||||
order: $i;
|
||||
}
|
||||
}
|
||||
|
||||
// `$columns - 1` because offsetting by the width of an entire row isn't possible
|
||||
@for $i from 0 through ($columns - 1) {
|
||||
@if not ($infix == "" and $i == 0) { // Avoid emitting useless .offset-0
|
||||
.offset#{$infix}-#{$i} {
|
||||
@include make-col-offset($i, $columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
public/scss/grid/_grid.scss
Normal file
52
public/scss/grid/_grid.scss
Normal file
@ -0,0 +1,52 @@
|
||||
/// Grid system
|
||||
//
|
||||
// Generate semantic grid columns with these mixins.
|
||||
|
||||
@mixin make-container() {
|
||||
width: 100%;
|
||||
padding-right: ($grid-gutter-width / 2);
|
||||
padding-left: ($grid-gutter-width / 2);
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
|
||||
// For each breakpoint, define the maximum width of the container in a media query
|
||||
@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {
|
||||
@each $breakpoint, $container-max-width in $max-widths {
|
||||
@include media-breakpoint-up($breakpoint, $breakpoints) {
|
||||
max-width: $container-max-width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin make-row() {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: ($grid-gutter-width / -2);
|
||||
margin-left: ($grid-gutter-width / -2);
|
||||
}
|
||||
|
||||
@mixin make-col-ready() {
|
||||
position: relative;
|
||||
// Prevent columns from becoming too narrow when at smaller grid tiers by
|
||||
// always setting `width: 100%;`. This works because we use `flex` values
|
||||
// later on to override this initial width.
|
||||
width: 100%;
|
||||
min-height: 1px; // Prevent collapsing
|
||||
padding-right: ($grid-gutter-width / 2);
|
||||
padding-left: ($grid-gutter-width / 2);
|
||||
}
|
||||
|
||||
@mixin make-col($size, $columns: $grid-columns) {
|
||||
flex: 0 0 percentage($size / $columns);
|
||||
// Add a `max-width` to ensure content within each column does not blow out
|
||||
// the width of the column. Applies to IE10+ and Firefox. Chrome and Safari
|
||||
// do not appear to require this.
|
||||
max-width: percentage($size / $columns);
|
||||
}
|
||||
|
||||
@mixin make-col-offset($size, $columns: $grid-columns) {
|
||||
$num: $size / $columns;
|
||||
margin-left: if($num == 0, 0, percentage($num));
|
||||
}
|
34
public/scss/index.scss
Normal file
34
public/scss/index.scss
Normal file
@ -0,0 +1,34 @@
|
||||
@charset 'UTF-8';
|
||||
|
||||
// Settings
|
||||
@import 'settings';
|
||||
@import 'custom';
|
||||
|
||||
// Utils
|
||||
@import 'utils/breakpoints';
|
||||
@import 'utils/reset';
|
||||
@import 'utils/default';
|
||||
@import 'utils/background';
|
||||
@import 'utils/border';
|
||||
@import 'utils/aligner';
|
||||
@import 'utils/helpers';
|
||||
@import 'utils/layout';
|
||||
@import 'utils/typography';
|
||||
|
||||
// Components
|
||||
@import 'components/badge';
|
||||
@import 'components/buttons';
|
||||
@import 'components/forms';
|
||||
@import 'components/loading-bar';
|
||||
@import 'components/loading-spinner';
|
||||
@import 'components/notification';
|
||||
@import 'components/paginator';
|
||||
@import 'components/table';
|
||||
@import 'components/tabs';
|
||||
@import 'components/tag';
|
||||
|
||||
// Twitter bootstrap's grid v4.1.0
|
||||
// More info: https://getbootstrap.com/examples/grid/
|
||||
@import 'grid/grid-framework';
|
||||
@import 'grid/grid';
|
||||
@import 'utils/grid';
|
48
public/scss/utils/_aligner.scss
Normal file
48
public/scss/utils/_aligner.scss
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* ALIGNERS
|
||||
*/
|
||||
|
||||
// container
|
||||
.aligner {
|
||||
display: flex;
|
||||
|
||||
&--spaceBetween {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&--spaceAround {
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&--centerVertical {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--centerHoritzontal {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&--contentStart {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&--contentEnd {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
// item
|
||||
.aligner--itemTop {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.aligner--itemBottom {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.flex-grow, // deprecated
|
||||
.aligner--grow {
|
||||
flex-grow: 1;
|
||||
}
|
11
public/scss/utils/_background.scss
Normal file
11
public/scss/utils/_background.scss
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* BACKGROUND
|
||||
*/
|
||||
|
||||
.bg {
|
||||
@each $type, $color in $c-map {
|
||||
&-#{$type} {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
}
|
23
public/scss/utils/_border.scss
Normal file
23
public/scss/utils/_border.scss
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* BORDER
|
||||
*/
|
||||
|
||||
.border {
|
||||
border: $bd-medium;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: $bd-medium;
|
||||
}
|
||||
|
||||
.border-left {
|
||||
border-left: $bd-medium;
|
||||
}
|
||||
|
||||
.border-right {
|
||||
border-right: $bd-medium;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: $bd-medium;
|
||||
}
|
119
public/scss/utils/_breakpoints.scss
Normal file
119
public/scss/utils/_breakpoints.scss
Normal file
@ -0,0 +1,119 @@
|
||||
// Breakpoint viewport sizes and media queries.
|
||||
//
|
||||
// Breakpoints are defined as a map of (name: minimum width), order from small to large:
|
||||
//
|
||||
// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)
|
||||
//
|
||||
// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.
|
||||
|
||||
// Name of the next breakpoint, or null for the last breakpoint.
|
||||
//
|
||||
// >> breakpoint-next(sm)
|
||||
// md
|
||||
// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
|
||||
// md
|
||||
// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
|
||||
// md
|
||||
@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
|
||||
$n: index($breakpoint-names, $name);
|
||||
@return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
|
||||
}
|
||||
|
||||
// Minimum breakpoint width. Null for the smallest (first) breakpoint.
|
||||
//
|
||||
// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
|
||||
// 576px
|
||||
@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
|
||||
$min: map-get($breakpoints, $name);
|
||||
@return if($min != 0, $min, null);
|
||||
}
|
||||
|
||||
// Maximum breakpoint width. Null for the largest (last) breakpoint.
|
||||
// The maximum value is calculated as the minimum of the next one less 0.1.
|
||||
//
|
||||
// >> breakpoint-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
|
||||
// 767px
|
||||
@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
|
||||
$next: breakpoint-next($name, $breakpoints);
|
||||
@return if($next, breakpoint-min($next, $breakpoints) - 1px, null);
|
||||
}
|
||||
|
||||
// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
|
||||
// Useful for making responsive utilities.
|
||||
//
|
||||
// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
|
||||
// "" (Returns a blank string)
|
||||
// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
|
||||
// "-sm"
|
||||
@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
|
||||
@return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}");
|
||||
}
|
||||
|
||||
// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
|
||||
// Makes the @content apply to the given breakpoint and wider.
|
||||
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
|
||||
$min: breakpoint-min($name, $breakpoints);
|
||||
@if $min {
|
||||
@media (min-width: $min) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
|
||||
// Makes the @content apply to the given breakpoint and narrower.
|
||||
@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
|
||||
$max: breakpoint-max($name, $breakpoints);
|
||||
@if $max {
|
||||
@media (max-width: $max) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Media that spans multiple breakpoint widths.
|
||||
// Makes the @content apply between the min and max breakpoints
|
||||
@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
|
||||
$min: breakpoint-min($lower, $breakpoints);
|
||||
$max: breakpoint-max($upper, $breakpoints);
|
||||
|
||||
@if $min != null and $max != null {
|
||||
@media (min-width: $min) and (max-width: $max) {
|
||||
@content;
|
||||
}
|
||||
} @else if $max == null {
|
||||
@include media-breakpoint-up($lower) {
|
||||
@content;
|
||||
}
|
||||
} @else if $min == null {
|
||||
@include media-breakpoint-down($upper) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media between the breakpoint's minimum and maximum widths.
|
||||
// No minimum for the smallest breakpoint, and no maximum for the largest one.
|
||||
// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
|
||||
@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {
|
||||
$min: breakpoint-min($name, $breakpoints);
|
||||
$max: breakpoint-max($name, $breakpoints);
|
||||
|
||||
@if $min != null and $max != null {
|
||||
@media (min-width: $min) and (max-width: $max) {
|
||||
@content;
|
||||
}
|
||||
} @else if $max == null {
|
||||
@include media-breakpoint-up($name) {
|
||||
@content;
|
||||
}
|
||||
} @else if $min == null {
|
||||
@include media-breakpoint-down($name) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
39
public/scss/utils/_default.scss
Normal file
39
public/scss/utils/_default.scss
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* MAIN RULES
|
||||
*/
|
||||
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: $bgc-body;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
p {
|
||||
font-weight: normal;
|
||||
margin-bottom: $mb-p;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: $fw-strong;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: $mb-ul;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
margin-bottom: $mb-li;
|
||||
}
|
52
public/scss/utils/_grid.scss
Normal file
52
public/scss/utils/_grid.scss
Normal file
@ -0,0 +1,52 @@
|
||||
// Container widths
|
||||
//
|
||||
// Set the container width, and override it for fixed navbars in media queries.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.container {
|
||||
@include make-container;
|
||||
@include make-container-max-widths;
|
||||
}
|
||||
}
|
||||
|
||||
// Fluid container
|
||||
//
|
||||
// Utilizes the mixin meant for fixed width containers, but with 100% width for
|
||||
// fluid, full width layouts.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.container-fluid {
|
||||
@include make-container;
|
||||
}
|
||||
}
|
||||
|
||||
// Row
|
||||
//
|
||||
// Rows contain and clear the floats of your columns.
|
||||
|
||||
@if $enable-grid-classes {
|
||||
.row {
|
||||
@include make-row;
|
||||
}
|
||||
|
||||
// Remove the negative margin from default .row, then the horizontal padding
|
||||
// from all immediate children columns (to prevent runaway style inheritance).
|
||||
.no-gutters {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
||||
> .col,
|
||||
> [class*='col-'] {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
//
|
||||
// Common styles for small and large grid columns
|
||||
|
||||
@if $enable-grid-classes {
|
||||
@include make-grid-columns;
|
||||
}
|
126
public/scss/utils/_helpers.scss
Normal file
126
public/scss/utils/_helpers.scss
Normal file
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* FLOATS
|
||||
*/
|
||||
|
||||
.fleft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
::after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MARGINS
|
||||
*/
|
||||
|
||||
.m-xsmall {
|
||||
margin: $m-xsmall;
|
||||
}
|
||||
|
||||
.mb-xsmall {
|
||||
margin-bottom: $m-xsmall;
|
||||
}
|
||||
|
||||
.m-small {
|
||||
margin: $m-small;
|
||||
}
|
||||
|
||||
.mb-small {
|
||||
margin-bottom: $m-small;
|
||||
}
|
||||
|
||||
.m-medium {
|
||||
margin: $m-medium;
|
||||
}
|
||||
|
||||
.mb-medium {
|
||||
margin-bottom: $m-medium;
|
||||
}
|
||||
|
||||
.m-big {
|
||||
margin: $m-big;
|
||||
}
|
||||
|
||||
.mb-big {
|
||||
margin-bottom: $m-big;
|
||||
}
|
||||
|
||||
.m-huge {
|
||||
margin: $m-huge;
|
||||
}
|
||||
|
||||
.mb-huge {
|
||||
margin-bottom: $m-huge;
|
||||
}
|
||||
|
||||
.m-none {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* PADDINGS
|
||||
*/
|
||||
.p-small {
|
||||
padding: $p-small;
|
||||
}
|
||||
|
||||
.pb-small {
|
||||
padding-bottom: $p-small;
|
||||
}
|
||||
|
||||
.p-medium {
|
||||
padding: $p-medium;
|
||||
}
|
||||
|
||||
.pb-medium {
|
||||
padding-bottom: $p-medium;
|
||||
}
|
||||
|
||||
.p-big {
|
||||
padding: $p-big;
|
||||
}
|
||||
|
||||
.pb-big {
|
||||
padding-bottom: $p-big;
|
||||
}
|
||||
|
||||
.p-huge {
|
||||
padding: $p-huge;
|
||||
}
|
||||
|
||||
.pb-huge {
|
||||
padding-bottom: $p-huge;
|
||||
}
|
||||
|
||||
/**
|
||||
* OTHERS
|
||||
*/
|
||||
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.opacity-low {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.rounded-corners {
|
||||
border-radius: $bdr-rounded-corners;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 100%;
|
||||
}
|
51
public/scss/utils/_layout.scss
Normal file
51
public/scss/utils/_layout.scss
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* LAYOUT
|
||||
*/
|
||||
|
||||
.section {
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-bottom: $p-huge * 2;
|
||||
padding-top: $p-huge * 2;
|
||||
}
|
||||
padding-bottom: $p-huge;
|
||||
padding-top: $p-huge;
|
||||
|
||||
& + & {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-left: $w-gutter;
|
||||
padding-right: $w-gutter;
|
||||
}
|
||||
background-color: $bgc-container;
|
||||
margin: 0 auto;
|
||||
max-width: $mwi-container;
|
||||
padding-left: $w-gutter / 2;
|
||||
padding-right: $w-gutter / 2;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container-medium {
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-left: $w-gutter;
|
||||
padding-right: $w-gutter;
|
||||
}
|
||||
margin: 0 auto;
|
||||
max-width: $mwi-container-medium;
|
||||
padding-left: $w-gutter / 2;
|
||||
padding-right: $w-gutter / 2;
|
||||
}
|
||||
|
||||
.container-small {
|
||||
@include media-breakpoint-up(md) {
|
||||
padding-left: $w-gutter;
|
||||
padding-right: $w-gutter;
|
||||
}
|
||||
margin: 0 auto;
|
||||
max-width: $mwi-container-small;
|
||||
padding-left: $w-gutter / 2;
|
||||
padding-right: $w-gutter / 2;
|
||||
}
|
34
public/scss/utils/_reset.scss
Normal file
34
public/scss/utils/_reset.scss
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Reset
|
||||
*/
|
||||
|
||||
@if $enable-reset-defaults {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
124
public/scss/utils/_typography.scss
Normal file
124
public/scss/utils/_typography.scss
Normal file
@ -0,0 +1,124 @@
|
||||
/**
|
||||
* TYPOGRAPHY
|
||||
*/
|
||||
|
||||
body {
|
||||
color: $c-body;
|
||||
font-family: $ff-body;
|
||||
font-size: $fz-body;
|
||||
font-weight: $fw-body;
|
||||
line-height: $lh-body;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $c-link;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $c-link-hover;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: $c-primary;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sizes
|
||||
.text {
|
||||
&-huge, &-big, &-medium {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
&-huge {
|
||||
font-size: $fz-huge;
|
||||
line-height: $lh-huge;
|
||||
}
|
||||
|
||||
&-big {
|
||||
font-size: $fz-big;
|
||||
line-height: $lh-big;
|
||||
}
|
||||
|
||||
&-medium {
|
||||
font-size: $fz-medium;
|
||||
line-height: $lh-medium;
|
||||
}
|
||||
|
||||
&-small {
|
||||
font-size: $fz-small;
|
||||
line-height: $lh-small;
|
||||
}
|
||||
|
||||
// set default font state.
|
||||
&-body {
|
||||
font-size: $fz-body;
|
||||
line-height: $lh-body;
|
||||
}
|
||||
}
|
||||
|
||||
// Colors
|
||||
.text {
|
||||
@each $type, $color in $c-map {
|
||||
&-#{$type} {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Styles
|
||||
.text-light {
|
||||
font-weight: $fw-light;
|
||||
}
|
||||
|
||||
.text-normal {
|
||||
font-weight: $fw-normal;
|
||||
}
|
||||
|
||||
.text-lineThrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.text-italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.text-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.text-uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
// titles after titles
|
||||
.text-withSubtitle {
|
||||
margin-bottom: 0 !important;
|
||||
|
||||
+ .text-huge,
|
||||
+ .text-big,
|
||||
+ .text-medium,
|
||||
+ .text-small, {
|
||||
margin-top: .5em;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
font-weight: $fw-headings;
|
||||
}
|
||||
|
||||
// Aligners
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
76
router/api.js
Normal file
76
router/api.js
Normal file
@ -0,0 +1,76 @@
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
const pandocc = require('../compile_pandoc.js')('otm');
|
||||
|
||||
|
||||
module.exports = (fastify, opts, done) => {
|
||||
fastify.get('/flight/:id', async (req,reply) => {
|
||||
const ENDPOINT = 'https://www.flightradar24.com/v1/search/web/find';
|
||||
const FORMAT = '-';
|
||||
if(req.params.id){
|
||||
axios.get(ENDPOINT,{
|
||||
params: {
|
||||
format: FORMAT,
|
||||
query: req.params.id,
|
||||
limit:16,
|
||||
type: 'schedule'
|
||||
}
|
||||
}).then(res=>{reply.send(res.data)});
|
||||
}else{
|
||||
reply.send([]);
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/:id', (req, reply) => {
|
||||
if(req.params.id == undefined){
|
||||
reply.code(400).send({error:"No ID query parameter"});
|
||||
} else {
|
||||
const { level } = fastify
|
||||
level.get(req.params.id, (err, val) => {
|
||||
if(err){
|
||||
console.warn(err);
|
||||
reply.send({name:"New Journey", main:[]});
|
||||
} else {
|
||||
reply.send(JSON.parse(val));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fastify.post('/:id', (req, reply) => {
|
||||
if(req.params.id == undefined){
|
||||
reply.code(400).send({error:"No ID query parameter"});
|
||||
} else {
|
||||
const { level } = fastify
|
||||
level.put(req.params.id, JSON.stringify(req.body), (err) => {
|
||||
if(err){
|
||||
console.warn(err);
|
||||
reply.code(500).send({error:"Error with DB"});
|
||||
} else {
|
||||
reply.send({content:"ok"});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
fastify.delete('/:id',(req, reply) => {
|
||||
if(req.params.id == undefined){
|
||||
reply.code(400).send({error:"No ID query parameter"});
|
||||
} else {
|
||||
const { level } = fastify
|
||||
level.delete(req.params.id,(err) => {
|
||||
if(err){
|
||||
console.warn(err);
|
||||
reply.code(500).send({error:"Error with DB"});
|
||||
} else {
|
||||
reply.send({content:"ok"});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
done();
|
||||
};
|
90
server.js
Normal file
90
server.js
Normal file
@ -0,0 +1,90 @@
|
||||
const fastify = require('fastify')();//{ logger: true });
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const csass = require("./compile_sass").compileSassMain();
|
||||
|
||||
const static_opts = {
|
||||
root: path.join(__dirname, 'public'),
|
||||
prefix: '/public/',
|
||||
};
|
||||
const pov_opts = {
|
||||
engine: {
|
||||
ejs: require('ejs')
|
||||
}
|
||||
};
|
||||
|
||||
fastify.hashgen = (str)=>{
|
||||
return crypto.createHash('md5').update('CHELCEL_PRE_SALT_##'+str+'##_SALT_POST_CHELCEL').digest('hex');
|
||||
};
|
||||
|
||||
fastify.cookiegen = (request, type, file_hash)=>{
|
||||
let tk = {};
|
||||
if(request.cookies.token ){
|
||||
tk = fastify.jwt.decode(request.cookies.token);
|
||||
}
|
||||
|
||||
if(tk[type]== undefined){
|
||||
tk[type]=[];
|
||||
}
|
||||
if(tk[type].indexOf(file_hash)== -1){
|
||||
tk[type].push(file_hash);
|
||||
}
|
||||
const token = fastify.jwt.sign(tk)
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
fastify.register(require('fastify-static'), static_opts);
|
||||
fastify.register(require('fastify-cookie'));
|
||||
//fastify.register(require('fastify-websocket'));
|
||||
fastify.register(require('point-of-view'), pov_opts);
|
||||
|
||||
fastify.register(require('fastify-leveldb'), {
|
||||
name: 'db'
|
||||
}, err => {
|
||||
if (err) throw err
|
||||
});
|
||||
|
||||
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.view('/template/home.html');
|
||||
});
|
||||
|
||||
fastify.get('/dbg', (req, reply) => {
|
||||
reply.view('/template/templates.html');
|
||||
});
|
||||
|
||||
fastify.get('/:id', (req, reply) => {
|
||||
try{
|
||||
const ec = parseInt(req.params.id);
|
||||
switch(ec){
|
||||
case 400:
|
||||
case 401:
|
||||
case 402:
|
||||
case 403:
|
||||
case 404:
|
||||
case 405:
|
||||
reply.code(ec).send("Client Error");
|
||||
break;
|
||||
case 500:
|
||||
reply.code(ec).send("Internal Error");
|
||||
break;
|
||||
default:
|
||||
throw undefined;
|
||||
}
|
||||
}catch(e){
|
||||
reply.view('/template/journey.html');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
fastify.register(require('./router/api'), { prefix: '/api' });
|
||||
|
||||
|
||||
fastify.listen(8080,'0.0.0.0' ,(err,address) => {
|
||||
if (err) throw err;
|
||||
console.log("Listening on", address);
|
||||
|
||||
});
|
117
template/default.html
Normal file
117
template/default.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html$if(lang)$ lang="$lang$"$endif$ >
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<link rel='shortcut icon' href='/public/img/helcel.ico' type='image/x-icon'/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
$for(author-meta)$
|
||||
<meta name="author" content="$author-meta$">
|
||||
$endfor$
|
||||
$if(date-meta)$
|
||||
<meta name="dcterms.date" content="$date-meta$">
|
||||
$endif$
|
||||
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
$if(quotes)$
|
||||
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style>
|
||||
$endif$
|
||||
$for(css)$
|
||||
<link rel="stylesheet" href="$css$">
|
||||
$endfor$
|
||||
$for(header-includes)$
|
||||
$header-includes$
|
||||
$endfor$
|
||||
</head>
|
||||
<body>
|
||||
<header itemscope itemtype="https://schema.org/Person" class="$if(photo)$with-photo$endif$">
|
||||
<div id="title">
|
||||
$if(qrcode)$
|
||||
<img class="logo" src="$qrcode$" />
|
||||
$endif$
|
||||
<h1 class="fullname">
|
||||
<span itemprop="givenName">$firstname$</span>
|
||||
<span itemprop="familyName">$lastname$</span>
|
||||
</h1>
|
||||
<h2 class="title">$description$</h2>
|
||||
</div>
|
||||
$if(photo)$
|
||||
<img class="portrait" src="$photo$"></div>
|
||||
$endif$
|
||||
<ul class="details">
|
||||
<!-- phone -->
|
||||
$if(phone)$
|
||||
$if(settings.protect-phone)$
|
||||
$if(privatecv)$
|
||||
<li><a class='phone' href="phoneto:$phone$">$phone$</a></li>
|
||||
$endif$
|
||||
$else$
|
||||
<li><a class='phone' href="phoneto:$phone$">$phone$</a></li>
|
||||
$endif$
|
||||
$endif$
|
||||
<!-- mobile -->
|
||||
$if(mobile)$
|
||||
$if(settings.protect-mobile)$
|
||||
$if(privatecv)$
|
||||
<li><a class='mobile' href="phoneto:$mobile$">$mobile$</a></li>
|
||||
$endif$
|
||||
$else$
|
||||
<li><a class='mobile' href="phoneto:$mobile$">$mobile$</a></li>
|
||||
$endif$
|
||||
$endif$
|
||||
<!-- email -->
|
||||
$if(email)$
|
||||
$if(settings.protect-email)$
|
||||
$if(privatecv)$
|
||||
<li><a href="mailto:$email$">$email$</a></li>
|
||||
$endif$
|
||||
$else$
|
||||
<li><a href="mailto:$email$">$email$</a></li>
|
||||
$endif$
|
||||
$endif$
|
||||
<!-- homepage -->
|
||||
$if(homepage)$
|
||||
<li><a href="$homepage$" itemprop="url" title="homepage">$homepage$</a></li>
|
||||
$endif$
|
||||
<!--if(address)-->
|
||||
$if(address)$
|
||||
$if(settings.protect-address)$
|
||||
$if(privatecv)$
|
||||
<li itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
|
||||
$if(address.city)$
|
||||
<span itemprop="addressLocality">$address.city$</span>
|
||||
$endif$
|
||||
$if(address.country)$
|
||||
<span itemprop="addressCountry">$address.country$</span>
|
||||
$endif$
|
||||
</li>
|
||||
$endif$
|
||||
$else$
|
||||
<li itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
|
||||
$if(address.city)$
|
||||
<span itemprop="addressLocality">$address.city$</span>
|
||||
$endif$
|
||||
$if(address.country)$
|
||||
<span itemprop="addressCountry">$address.country$</span>
|
||||
$endif$
|
||||
</li>
|
||||
$endif$
|
||||
$endif$
|
||||
<!--endif-->
|
||||
</ul>
|
||||
</header>
|
||||
$for(include-before)$
|
||||
$include-before$
|
||||
$endfor$
|
||||
$body$
|
||||
$for(include-after)$
|
||||
$include-after$
|
||||
$endfor$
|
||||
<footer>
|
||||
$footer$
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
139
template/home.html
Normal file
139
template/home.html
Normal file
@ -0,0 +1,139 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Helcel-OTM</title>
|
||||
|
||||
<link rel='shortcut icon' href='/public/img/helcel.ico' type='image/x-icon'/>
|
||||
<meta charset='utf-8'/>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,700,300" type="text/css">
|
||||
<link rel='stylesheet' href='/public/css/index.css'/>
|
||||
|
||||
<script src="/public/js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/vue2-datepicker/index.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/vue2-datepicker/index.css">
|
||||
|
||||
<link rel="stylesheet" href="//unpkg.com/leaflet/dist/leaflet.css" />
|
||||
<script src="//unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="//unpkg.com/vue2-leaflet"></script>
|
||||
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<script src="https://unpkg.com/lodash/lodash.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/vue-multiselect"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect/dist/vue-multiselect.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<main id="app">
|
||||
<!--<header class="header js-header">
|
||||
<div class="header-inner container">
|
||||
<a href="/" class="header-logo text-dark">
|
||||
<img class="header-logoImage" src="/public/img/helcel.png" alt="Helcel logo" width="40">
|
||||
<span class="hide-small">Helcel</span>
|
||||
</a>
|
||||
<ul class="header-nav">
|
||||
<li class="header-navItem"><a class="text-dark" href="/">OTM</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>-->
|
||||
<div class="container">
|
||||
<section class="mb-big">
|
||||
<div class="text-center">
|
||||
<img class="main-logo mb-medium" src="/public/img/helcel.png" alt="Helcel logo">
|
||||
<div>
|
||||
<h1 class="text-huge text-withSubtitle">Open Tourism Map</h1>
|
||||
<h2 class="text-big text-gray">Collaborative Holiday Planner</h2>
|
||||
</div>
|
||||
<p id="js-header-waypoint" class="m-none">
|
||||
<a class="button button--primary button--mobileFull" href="#go">Get started</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="bg-dark">
|
||||
<div class="container">
|
||||
<div class="row text-center">
|
||||
<div class="col-12 col-sm-3">
|
||||
<div class="section">
|
||||
<img src="/public/img/lightweight.png" alt="Lightweight" width="118"> <br>
|
||||
<h2 class="text-withSubtitle text-big text-white">Lightweight <br>
|
||||
<span class="text-medium text-gray">Powered By<br>Fastify & Sierra</span></h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-4">
|
||||
<div class="section">
|
||||
<img src="/public/img/customizable.png" alt="Customizable" width="118"> <br>
|
||||
<h2 class="text-withSubtitle text-big text-white">Customizable<br>
|
||||
<span class="text-medium text-gray">Many Templates<br>to choose from</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<div class="section">
|
||||
<h2 class="text-withSubtitle text-big text-white">
|
||||
<img src="/public/img/opensource.png" alt="Open Source" width="118"> <br>
|
||||
FOSS<br>
|
||||
|
||||
<span class="text-medium text-gray">:-)</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="go" class="container-medium section">
|
||||
<h2 class="text-big">Your journey</h2>
|
||||
<p>Browse hotels, restaurants and attractions,.... <br> Select and plan the varying elements of your journey</p>
|
||||
<div class="aligner aligner--contentEnd">
|
||||
<div class="input">
|
||||
<input id="journey_id" v-model="journey_id" placeholder="ID" type="text">
|
||||
</div>
|
||||
<button class="button button--primary button--mobileFull" v-on:click="start_journey">Start the journey</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="section bg-dark">
|
||||
<div class="container text-center">
|
||||
<h3 class="text-huge text-white text-withSubtitle">Developed entirely with Sierra library selectors</h3>
|
||||
<h4 class="text-big text-gray">Just define your custom SCSS variables and compile</h4>
|
||||
<p>
|
||||
<p><a href="https://sierra-library.github.io/" class="button button--secondary button--medium button--mobileFull">Documentation</a> <a href="https://github.com/sierra-library/sierra-library.github.io/tree/master/examples/example1" class="button button--outlined button--medium button--mobileFull">View code</a></p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-medium section">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
||||
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
||||
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
<div class="aligner aligner--contentEnd">
|
||||
<a href="https://github.com/sierra-library/sierra" class="button button--primary button--mobileFull">Visit Github Page</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<script src="/public/js/main.js" type="text/javascript" charset="utf-8"></script>
|
||||
<footer class="bg-dark">
|
||||
<div class="container">
|
||||
<div class="section text-center text-small">
|
||||
<p class="text-white">
|
||||
<img src="/public/img/helcel.png" alt="helcel logo" width="100"><br><br>
|
||||
Built with ❤ by Helcel <br><span class="text-small text-gray">v0.0.1</span>
|
||||
</p>
|
||||
|
||||
<p class="text-gray"><a href="https://git.helcel.net">Helcel Git</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
320
template/journey.html
Normal file
320
template/journey.html
Normal file
@ -0,0 +1,320 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Helcel-OTM</title>
|
||||
|
||||
<link rel='shortcut icon' href='/public/img/helcel.ico' type='image/x-icon'/>
|
||||
<meta charset='utf-8'/>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1'/>
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:400,700,300" type="text/css">
|
||||
<link rel='stylesheet' href='/public/css/index.css'/>
|
||||
|
||||
<script src="/public/js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/vue2-datepicker/index.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/vue2-datepicker/index.css">
|
||||
|
||||
<link rel="stylesheet" href="//unpkg.com/leaflet/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/vue2-leaflet"></script>
|
||||
|
||||
<script src="https://unpkg.com/leaflet.awesome-markers/dist/leaflet.awesome-markers.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.awesome-markers/dist/leaflet.awesome-markers.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@fortawesome/fontawesome-free@5.15.3/css/all.min.css">
|
||||
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<script src="https://unpkg.com/lodash/lodash.min.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/vue-multiselect"></script>
|
||||
|
||||
<script src="//cdn.jsdelivr.net/npm/sortablejs/Sortable.min.js"></script>
|
||||
<script src="https://unpkg.com/vuedraggable/dist/vuedraggable.umd.min.js"></script>
|
||||
<script src="https://unpkg.com/vue-textarea-autosize/dist/vue-textarea-autosize.umd.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect/dist/vue-multiselect.min.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<main id="app">
|
||||
<header class="header">
|
||||
<div class="header-inner container">
|
||||
<a href="/" class="header-logo text-dark">
|
||||
<img class="header-logoImage" src="/public/img/helcel.png" alt="Helcel logo" width="40">
|
||||
<span class="hide-small">HOTM</span>
|
||||
</a>
|
||||
<div class="input input-invis"><input class="small" v-model="journey_data.name" type="text" /></div>
|
||||
|
||||
<ul class="header-nav">
|
||||
<div class="col-sm-5"><button class="button button--primary button--mobileFull" v-on:click="first_step">Main</button></div>
|
||||
<div class="col-sm-5"><button class="button button--primary button--mobileFull" v-on:click="next_step">Next</button></div>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
<div v-if="journey_step==-1">
|
||||
<div v-for="(item,idx) in journey_data.main" :class="idx%2===0 ? 'bg-dark text-white' : ''" v-cloak>
|
||||
<div class="container section">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-1"><button class="button button--secondary button--mobileFull" v-on:click="rm_section(idx)">X</button></div>
|
||||
<div class="input col-sm-4"><input class="" v-model="item.title" type="text" /></div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-8">
|
||||
<l-map
|
||||
:zoom.sync="item.map.zoom"
|
||||
:center.sync="item.map.center"
|
||||
style="padding-top: 100%;"
|
||||
|
||||
>
|
||||
<l-tile-layer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||
></l-tile-layer>
|
||||
<l-control-scale position="topright" :imperial="false" :metric="true"></l-control-scale>
|
||||
|
||||
<l-marker v-if="item.hotel && item.hotel.icon" :lat-lng.sync="item.hotel.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(item.hotel,'hotel')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Hotel: {{item.hotel.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
|
||||
<!-- <l-tooltip :options="{ permanent: true, interactive: true }">
|
||||
<div>
|
||||
H
|
||||
</div>
|
||||
</l-tooltip> -->
|
||||
</l-marker>
|
||||
<l-marker v-for="place in item.places.restaurants" :lat-lng.sync="place.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(place,'food')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Food: {{place.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
</l-marker>
|
||||
<l-marker v-for="place in item.places.activities" :lat-lng.sync="place.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(place,'activity')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Activity: {{place.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
</l-marker>
|
||||
</l-map>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<div class="row text-center">
|
||||
<div><label>Date Range</label></div>
|
||||
<div class="input ">
|
||||
<date-picker v-model="item.dateRange" range :placeholder="item.dateRange"></date-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div><label>Hotel</label></div>
|
||||
<multiselect v-model="item.hotel" id="ajax" label="display_name" track-by="place_id" placeholder="Type to search" open-direction="bottom" :options="query.nominatim" :searchable="true" :loading="querying.hotel" :internal-search="false" :clear-on-select="false" :allow-empty="false" :options-limit="300" :limit="3" :max-height="600" @search-change="debounceSearch.hotel"></multiselect>
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div><label>Fight</label></div>
|
||||
|
||||
<multiselect v-model="item.flightA" id="ajax" label="label" track-by="id" placeholder="Type to search" open-direction="bottom" :multiple="true" :options="query.flight" :searchable="true" :loading="querying.flight" :internal-search="false" :clear-on-select="false" :allow-empty="false" :options-limit="300" :limit="3" :max-height="600" @search-change="debounceSearch.flight"></multiselect>
|
||||
<multiselect v-model="item.flightB" id="ajax" label="label" track-by="id" placeholder="Type to search" open-direction="bottom" :multiple="true" :options="query.flight" :searchable="true" :loading="querying.flight" :internal-search="false" :clear-on-select="false" :allow-empty="false" :options-limit="300" :limit="3" :max-height="600" @search-change="debounceSearch.flight"></multiselect>
|
||||
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div><label>Restoration</label></div>
|
||||
|
||||
<multiselect v-model="item.places.restaurants" id="ajax" label="display_name" track-by="place_id" placeholder="Type to search" open-direction="bottom" :multiple="true" :options="query.nominatim" :searchable="true" :loading="querying.food" :internal-search="false" :clear-on-select="false" :allow-empty="false" :options-limit="300" :limit="3" :max-height="600" @search-change="debounceSearch.restaurants"></multiselect>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="row text-center">
|
||||
<div><label>Activities</label></div>
|
||||
|
||||
<multiselect v-model="item.places.activities" id="ajax" label="display_name" track-by="place_id" placeholder="Type to search" open-direction="bottom" :multiple="true" :options="query.nominatim" :searchable="true" :loading="querying.place" :internal-search="false" :clear-on-select="false" :options-limit="300" :limit="3" :max-height="600" @search-change="debounceSearch.places"></multiselect>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="container-medium section">
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-dark">
|
||||
<div id="go" class="container-medium section text-white">
|
||||
<h2 class="text-big">Add new section</h2>
|
||||
<p>Got an other flight and/or hotel ? Add a new section to register it</p>
|
||||
<div class="aligner aligner--contentEnd">
|
||||
<button class="button button--primary button--mobileFull" v-on:click="add_section">Add Section</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="journey_step>=0 && journey_data.main[journey_step_data.section]">
|
||||
<div class="bg-dark text-white" v-cloak>
|
||||
<div class="container section">
|
||||
<div class="row text-center">
|
||||
<div class="input col-sm-4"><input :value="journey_data.main[journey_step_data.section].title + ': Day ' + journey_step_data.day" disabled /></div>
|
||||
<div class="input col-sm-2"><input placeholder="Day title" v-model="journey_data.step_title[journey_step]" /></div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-12">
|
||||
<l-map
|
||||
:zoom=" journey_data.main[journey_step_data.section].map.zoom"
|
||||
:center="journey_data.main[journey_step_data.section].map.center"
|
||||
style="padding-top: 100%;"
|
||||
|
||||
>
|
||||
<l-tile-layer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||
></l-tile-layer>
|
||||
<l-control-scale position="topright" :imperial="false" :metric="true"></l-control-scale>
|
||||
|
||||
<l-marker v-if="journey_data.main[journey_step_data.section].hotel &&
|
||||
journey_data.main[journey_step_data.section].hotel.icon"
|
||||
:lat-lng="journey_data.main[journey_step_data.section].hotel.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(journey_data.main[journey_step_data.section].hotel,'hotel')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Hotel: {{journey_data.main[journey_step_data.section].hotel.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
</l-marker>
|
||||
<!-- <l-marker v-for="place in journey_data.main[journey_step_data.section].places.restaurants" :lat-lng="place.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(place,'food')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Food: {{place.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
</l-marker> -->
|
||||
<l-marker v-for="place in filter_selected(journey_data.main[journey_step_data.section].places.activities, true)" :lat-lng="place.latlon">
|
||||
<l-icon>
|
||||
<div v-html="generate_icon(place,'activity')"></div>
|
||||
</l-icon>
|
||||
<l-popup>
|
||||
<div>
|
||||
Activity: {{place.display_name}}
|
||||
</div>
|
||||
</l-popup>
|
||||
</l-marker>
|
||||
</l-map>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-12">
|
||||
<div class="container text-center" v-if="journey_step_data.start || journey_step_data.end">
|
||||
<b>Flights</b>
|
||||
<div v-if="journey_step_data.start">
|
||||
<div v-for="element in journey_data.main[journey_step_data.section].flightA"><a :href="'https://www.flightradar24.com/data/flights/'+element.id">{{ element.label }}</a></div>
|
||||
</div>
|
||||
<div v-if="journey_step_data.end">
|
||||
<div v-for="element in journey_data.main[journey_step_data.section].flightB"><a :href="'https://www.flightradar24.com/data/flights/'+element.id">{{ element.label }}</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container"></div>
|
||||
<div class="container text-center">
|
||||
<b>Select Daily Activities</b>
|
||||
<div class= "row text-dark container">
|
||||
<div class="col-12 col-sm-6">
|
||||
<l-draggable
|
||||
class="dragArea list-group"
|
||||
:list="filter_unselected(journey_data.main[journey_step_data.section].places.activities)"
|
||||
:group="{ name: 'people', pull: 'clone', put: false }"
|
||||
>
|
||||
<div
|
||||
class="list-group-item"
|
||||
v-for="element in filter_unselected(journey_data.main[journey_step_data.section].places.activities)"
|
||||
:key="element.place_id"
|
||||
>
|
||||
{{ element.display_name.split(",")[0] }}
|
||||
</div>
|
||||
</l-draggable>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6">
|
||||
<l-draggable
|
||||
class="dragArea list-group"
|
||||
:list="activities_tmp"
|
||||
group="people"
|
||||
>
|
||||
<div
|
||||
class="list-group-item"
|
||||
v-for="(element,idx) in filter_selected(activities_tmp,true)"
|
||||
:key="element.place_id"
|
||||
>
|
||||
<div class="row">
|
||||
<i class="fa fa-times close col-12 col-sm-1" @click="remove_item(activities_tmp,idx)"></i>
|
||||
<div class="text">{{ element.display_name.split(",")[0] }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input col-12 col-sm-12">
|
||||
<textarea-autosize
|
||||
class="small"
|
||||
placeholder="Comments..."
|
||||
v-model="element.text"
|
||||
:min-height="16"
|
||||
:max-height="360"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</l-draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="container-medium section">
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-dark">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="container-medium section">
|
||||
<div class="aligner">
|
||||
<div class="input col-sm-4"><input class="" v-model="impexp" type="text" /></div>
|
||||
<div class="col-sm-2"><button class="button button--primary button--mobileFull" v-on:click="import_data">Import</button></div>
|
||||
<div class="col-sm-2"><button class="button button--primary button--mobileFull" v-on:click="export_data">Export</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="/public/js/main.js" type="text/javascript" charset="utf-8"></script>
|
||||
<footer class="bg-dark">
|
||||
<div class="container">
|
||||
<div class="section text-center text-small">
|
||||
<p class="text-white">
|
||||
<img src="/public/img/helcel.png" alt="helcel logo" width="100"><br><br>
|
||||
Built with ❤ by Helcel <br><span class="text-small text-gray">v0.0.1</span>
|
||||
</p>
|
||||
|
||||
<p class="text-gray"><a href="https://git.helcel.net">Helcel Git</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
1446
template/templates.html
Normal file
1446
template/templates.html
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user