1
0

New design (#14)

This commit is contained in:
konrad
2018-12-25 15:03:51 +00:00
committed by Gitea
parent de6104dda6
commit e094b654e2
51 changed files with 2828 additions and 551 deletions

View File

@ -1,84 +1,101 @@
<template>
<div id="app">
<nav class="navbar is-dark" role="navigation" aria-label="main navigation" v-if="user.authenticated">
<div class="container">
<div class="navbar-brand">
<router-link :to="{name: 'home'}" class="navbar-item logo">
<img src="images/logo-full-white.svg"/>
</router-link>
</div>
<div class="navbar-menu">
<div class="navbar-end">
<router-link :to="{name: 'listTeams'}" class="navbar-item">
<span class="icon is-small">
<icon icon="users"/>
</span>
Teams
</router-link>
<router-link :to="{name: 'newNamespace'}" class="navbar-item">
<span class="icon is-small">
<icon icon="layer-group"/>
</span>
New Namespace
</router-link>
<span class="navbar-item">{{user.infos.username}}</span>
<span class="navbar-item image">
<img :src="gravatar()" class="is-rounded" alt=""/>
</span>
<a v-on:click="logout()" class="navbar-item is-right logout-icon">
<span class="icon is-medium">
<icon icon="sign-out-alt" size="2x"/>
</span>
</a>
</div>
</div>
<nav class="navbar is-dark is-fixed-top" role="navigation" aria-label="main navigation" v-if="user.authenticated">
<div class="navbar-brand">
<router-link :to="{name: 'home'}" class="navbar-item logo">
<img src="/images/logo-full-white.svg"/>
</router-link>
</div>
</nav>
<div class="column is-centered container">
<div class="box shadow" v-if="user.authenticated">
<div class="columns">
<div class="column is-3">
<aside class="menu namespaces-lists">
<p class="menu-label" v-if="loading">Loading...</p>
<template v-for="n in namespaces">
<div :key="n.id">
<router-link :to="{name: 'editNamespace', params: {id: n.id} }" class="nsettings" v-if="n.id > 0">
<span class="icon">
<icon icon="cog"/>
</span>
</router-link>
<router-link :to="{ name: 'newList', params: { id: n.id} }" class="is-success nsettings" :key="n.id + 'newList'" v-if="n.id > 0">
<span class="icon">
<icon icon="plus"/>
</span>
</router-link>
<div class="menu-label">
{{n.name}}
</div>
<div v-if="user.authenticated">
<a @click="mobileMenuActive = true" class="mobilemenu-show-button" v-if="!mobileMenuActive"><icon icon="bars"></icon></a>
<a @click="mobileMenuActive = false" class="mobilemenu-hide-button" v-if="mobileMenuActive"><icon icon="times"></icon></a>
<div class="app-container">
<div class="namespace-container" :class="{'is-active': mobileMenuActive}">
<div class="menu top-menu">
<ul class="menu-list user">
<li>
<img :src="gravatar()" class="is-rounded" alt=""/>
<span class="username">{{user.infos.username}}</span>
<a @click="logout()" class="logout-icon">
<span class="icon is-medium">
<icon icon="power-off" size="2x"/>
</span>
</a>
</li>
</ul>
</div>
<div class="menu top-menu">
<ul class="menu-list">
<li>
<router-link :to="{ name: 'home'}">
<span class="icon">
<icon icon="calendar"/>
</span>
Overview
</router-link>
</li>
<li>
<router-link :to="{ name: 'listTeams'}">
<span class="icon">
<icon icon="users"/>
</span>
Teams
</router-link>
</li>
<li>
<router-link :to="{ name: 'newNamespace'}">
<span class="icon">
<icon icon="layer-group"/>
</span>
New Namespace
</router-link>
</li>
</ul>
</div>
<aside class="menu namespaces-lists">
<div class="spinner" :class="{ 'is-loading': loading}"></div>
<template v-for="n in namespaces">
<div :key="n.id">
<router-link v-tooltip.right="'Settings'" :to="{name: 'editNamespace', params: {id: n.id} }" class="nsettings" v-if="n.id > 0">
<span class="icon">
<icon icon="cog"/>
</span>
</router-link>
<router-link v-tooltip="'Add a new list in the ' + n.name + ' namespace'" :to="{ name: 'newList', params: { id: n.id} }" class="nsettings" :key="n.id + 'newList'" v-if="n.id > 0">
<span class="icon">
<icon icon="plus"/>
</span>
</router-link>
<div class="menu-label">
{{n.name}}
</div>
<ul class="menu-list" :key="n.id + 'child'">
<li v-for="l in n.lists" :key="l.id">
<router-link :to="{ name: 'showList', params: { id: l.id} }">{{l.title}}</router-link>
</li>
</ul>
</template>
</aside>
</div>
<div class="column is-9">
<router-view/>
</div>
</div>
<ul class="menu-list" :key="n.id + 'child'">
<li v-for="l in n.lists" :key="l.id">
<router-link :to="{ name: 'showList', params: { id: l.id} }">{{l.title}}</router-link>
</li>
</ul>
</template>
</aside>
</div>
</div>
<div v-else>
<div class="container has-text-centered">
<div class="column is-4 is-offset-4">
<img src="images/logo-full.svg"/>
<router-view/>
</div>
<div class="app-content" :class="{'fullpage-overlay': fullpage}">
<a class="mobile-overlay" v-if="mobileMenuActive" @click="mobileMenuActive = false"></a>
<transition name="fade">
<router-view/>
</transition>
</div>
</div>
</div>
<notifications position="bottom left" />
<div v-else>
<div class="container has-text-centered">
<div class="column is-4 is-offset-4">
<img src="/images/logo-full.svg"/>
<router-view/>
</div>
</div>
</div>
<notifications position="bottom left" />
</div>
</template>
@ -96,6 +113,8 @@
user: auth.user,
loading: false,
namespaces: [],
mobileMenuActive: false,
fullpage: false,
}
},
beforeMount() {
@ -119,7 +138,7 @@
},
watch: {
// call the method again if the route changes
'$route': 'loadNamespacesIfNeeded'
'$route': 'doStuffAfterRoute'
},
methods: {
logout() {
@ -129,14 +148,14 @@
return 'https://www.gravatar.com/avatar/' + this.user.infos.avatar + '?s=50'
},
loadNamespaces() {
this.loading = true
this.namespaces = []
const cancel = message.setLoading(this)
HTTP.get(`namespaces`, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => {
this.$set(this, 'namespaces', response.data)
this.loading = false
cancel()
})
.catch(e => {
cancel()
this.handleError(e)
})
},
@ -145,8 +164,15 @@
this.loadNamespaces()
}
},
doStuffAfterRoute(e) {
this.fullpage = false;
this.loadNamespacesIfNeeded(e)
this.mobileMenuActive = false
},
setFullPage() {
this.fullpage = true;
},
handleError(e) {
this.loading = false
message.error(e, this)
}
},

View File

@ -4,11 +4,11 @@
<p>Click on a list or namespace on the left to get started.</p>
<p v-if="loading">Loading tasks...</p>
<h3 v-if="tasks && tasks.length > 0">Current tasks</h3>
<div class="box tasks" v-if="tasks && tasks.length > 0">
<div class="tasks" v-if="tasks && tasks.length > 0">
<div @click="gotoList(l.listID)" class="task" v-for="l in tasks" v-bind:key="l.id" v-if="!l.done">
<label v-bind:for="l.id">
<div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" v-bind:id="l.id" v-bind:checked="l.done" style="display: none;" disabled>
<input type="checkbox" v-bind:id="l.id" v-bind:checked="l.done" style="display: none;" disabled>
<label v-bind:for="l.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>

View File

@ -1,5 +1,5 @@
<template>
<div class="loader-container" v-bind:class="{ 'is-loading': loading}">
<div class="loader-container" :class="{ 'is-loading': loading}">
<div class="card">
<header class="card-header">
<p class="card-header-title">
@ -12,7 +12,7 @@
<div class="field">
<label class="label" for="listtext">List Name</label>
<div class="control">
<input :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="listtext" placeholder="The list title goes here..." v-model="list.title">
<input v-focus :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="listtext" placeholder="The list title goes here..." v-model="list.title">
</div>
</div>
<div class="field">
@ -25,7 +25,7 @@
<div class="columns bigbuttons">
<div class="column">
<button @click="submit()" class="button is-success is-fullwidth" :class="{ 'is-loading': loading}">
<button @click="submit()" class="button is-primary is-fullwidth" :class="{ 'is-loading': loading}">
Save
</button>
</div>
@ -48,7 +48,7 @@
<modal
v-if="showDeleteModal"
@close="showDeleteModal = false"
v-on:submit="deleteList()">
@submit="deleteList()">
<span slot="header">Delete the list</span>
<p slot="text">Are you sure you want to delete this list and all of its contents?
<br/>This includes all tasks and <b>CANNOT BE UNDONE!</b></p>

View File

@ -1,16 +1,17 @@
<template>
<div class="content">
<div class="fullpage">
<a class="close" @click="back()">
<icon :icon="['far', 'times-circle']">
</icon>
</a>
<h3>Create a new list</h3>
<form @submit.prevent="newList">
<form @submit.prevent="newList" @keyup.esc="back()">
<div class="field is-grouped">
<p class="control has-icons-left is-expanded" v-bind:class="{ 'is-loading': loading}">
<input class="input" v-bind:class="{ 'disabled': loading}" v-model="list.title" type="text" placeholder="The list's name goes here...">
<span class="icon is-small is-left">
<icon icon="list-ol"/>
</span>
<p class="control is-expanded" :class="{ 'is-loading': loading}">
<input v-focus class="input" :class="{ 'disabled': loading}" v-model="list.title" type="text" placeholder="The list's name goes here...">
</p>
<p class="control">
<button type="submit" class="button is-success">
<button type="submit" class="button is-success noshadow">
<span class="icon is-small">
<icon icon="plus"/>
</span>
@ -43,21 +44,28 @@
router.push({name: 'home'})
}
},
created() {
this.$parent.setFullPage();
},
methods: {
newList() {
const cancel = message.setLoading(this)
HTTP.put(`namespaces/` + this.$route.params.id + `/lists`, this.list, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => {
.then(response => {
this.$parent.loadNamespaces()
this.handleSuccess({message: 'The list was successfully created.'})
cancel()
router.push({name: 'showList', params: {id: response.data.id}})
})
.catch(e => {
cancel()
this.handleError(e)
})
},
back() {
router.go(-1)
},
handleError(e) {
message.error(e, this)
},
@ -66,8 +74,4 @@
}
}
}
</script>
<style scoped>
</style>
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="loader-container" v-bind:class="{ 'is-loading': loading}">
<div class="loader-container" :class="{ 'is-loading': loading}">
<div class="content">
<router-link :to="{ name: 'editList', params: { id: list.id } }" class="icon settings is-medium">
<icon icon="cog" size="2x"/>
@ -8,8 +8,8 @@
</div>
<form @submit.prevent="addTask()">
<div class="field is-grouped">
<p class="control has-icons-left is-expanded" v-bind:class="{ 'is-loading': loading}">
<input class="input" v-bind:class="{ 'disabled': loading}" v-model="newTask.text" type="text" placeholder="Add a new task...">
<p class="control has-icons-left is-expanded" :class="{ 'is-loading': loading}">
<input v-focus class="input" :class="{ 'disabled': loading}" v-model="newTask.text" type="text" placeholder="Add a new task...">
<span class="icon is-small is-left">
<icon icon="tasks"/>
</span>
@ -27,19 +27,19 @@
<div class="columns">
<div class="column">
<div class="box tasks" v-if="this.list.tasks && this.list.tasks.length > 0">
<div class="task" v-for="l in list.tasks" v-bind:key="l.id">
<label v-bind:for="l.id">
<div class="tasks" v-if="this.list.tasks && this.list.tasks.length > 0" :class="{'short': isTaskEdit}">
<div class="task" v-for="l in list.tasks" :key="l.id">
<label :for="l.id">
<div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" v-bind:id="l.id" v-bind:checked="l.done" style="display: none;">
<label v-bind:for="l.id" class="check">
<input @change="markAsDone" type="checkbox" :id="l.id" :checked="l.done" style="display: none;">
<label :for="l.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
</label>
</div>
<span class="tasktext">
<span class="tasktext" :class="{ 'done': l.done}">
{{l.text}}
</span>
</label>
@ -67,7 +67,7 @@
<div class="field">
<label class="label" for="tasktext">Task Text</label>
<div class="control">
<input :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="tasktext" placeholder="The task text is here..." v-model="taskEditTask.text">
<input v-focus :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="tasktext" placeholder="The task text is here..." v-model="taskEditTask.text">
</div>
</div>
<div class="field">
@ -78,7 +78,7 @@
</div>
<b>Reminder Dates</b>
<div class="reminder-input" :class="{ 'overdue': (r < nowUnix && index !== (taskEditTask.reminderDates.length - 1))}" v-for="(r, index) in taskEditTask.reminderDates" v-bind:key="index">
<div class="reminder-input" :class="{ 'overdue': (r < nowUnix && index !== (taskEditTask.reminderDates.length - 1))}" v-for="(r, index) in taskEditTask.reminderDates" :key="index">
<flat-pickr
:class="{ 'disabled': loading}"
:disabled="loading"
@ -129,7 +129,7 @@
v-model="taskEditTask.endDate"
:config="flatPickerConfig"
id="taskduedate"
placeholder="Start date">
placeholder="End date">
</flat-pickr>
</div>
</div>
@ -141,7 +141,7 @@
<div class="column">
<input class="input" placeholder="Specify an amount..." v-model="repeatAfter.amount"/>
</div>
<div class="column">
<div class="column is-3">
<div class="select">
<select v-model="repeatAfter.type">
<option value="hours">Hours</option>
@ -157,30 +157,31 @@
<div class="field">
<label class="label" for="subtasks">Subtasks</label>
<div class="control subtasks">
<div class="tasks noborder" v-if="taskEditTask.subtasks && taskEditTask.subtasks.length > 0">
<div class="task" v-for="s in taskEditTask.subtasks" v-bind:key="s.id">
<label v-bind:for="s.id">
<div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" v-bind:id="s.id" v-bind:checked="s.done" style="display: none;">
<label v-bind:for="s.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
</label>
</div>
<span class="tasktext">
{{s.text}}
</span>
</label>
</div>
<div class="tasks noborder" v-if="taskEditTask.subtasks && taskEditTask.subtasks.length > 0">
<div class="task" v-for="s in taskEditTask.subtasks" :key="s.id">
<label :for="s.id">
<div class="fancycheckbox">
<input @change="markAsDone" type="checkbox" :id="s.id" :checked="s.done" style="display: none;">
<label :for="s.id" class="check">
<svg width="18px" height="18px" viewBox="0 0 18 18">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
</label>
</div>
<span class="tasktext" :class="{ 'done': s.done}">
{{s.text}}
</span>
</label>
</div>
<input :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="tasktext" placeholder="New subtask" v-model="newTask.text"/>
</div>
</div>
<div class="field has-addons">
<div class="control is-expanded">
<input @keyup.enter="addSubtask()" :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="tasktext" placeholder="New subtask" v-model="newTask.text"/>
</div>
<div class="control">
<a class="button" @click="addSubtask()"><icon icon="plus"></icon></a>
</div>
</div>
@ -258,6 +259,7 @@
}
// This adds a new elemednt "list" to our object which contains all lists
response.data.tasks = this.sortTasks(response.data.tasks)
this.$set(this, 'list', response.data)
if (this.list.tasks === null) {
this.list.tasks = []
@ -302,21 +304,30 @@
if (task.ParentTask === this.taskEditTask.id) {
this.taskEditTask.subtasks.push(task)
}
this.list.tasks = this.sortTasks(this.list.tasks)
},
markAsDone(e) {
const cancel = message.setLoading(this)
let context = this
if (e.target.checked) {
setTimeout(doTheDone, 300); // Delay it to show the animation when marking a task as done
} else {
doTheDone() // Don't delay it when un-marking it as it doesn't have an animation the other way around
}
HTTP.post(`tasks/` + e.target.id, {done: e.target.checked}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => {
this.updateTaskByID(parseInt(e.target.id), response.data)
this.handleSuccess({message: 'The task was successfully ' + (e.target.checked ? 'un-' :'') + 'marked as done.'})
cancel() // To not set the spinner to loading when the request is made in less than 100ms, would lead to loading infinitly.
})
.catch(e => {
cancel()
this.handleError(e)
})
function doTheDone() {
const cancel = message.setLoading(context)
HTTP.post(`tasks/` + e.target.id, {done: e.target.checked}, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => {
context.updateTaskByID(parseInt(e.target.id), response.data)
context.handleSuccess({message: 'The task was successfully ' + (e.target.checked ? '' : 'un-') + 'marked as done.'})
cancel() // To not set the spinner to loading when the request is made in less than 100ms, would lead to loading infinitly.
})
.catch(e => {
cancel()
context.handleError(e)
})
}
},
editTask(id) {
// Find the selected task and set it to the current object
@ -371,7 +382,6 @@
this.taskEditTask.startDate = (+ new Date(this.taskEditTask.startDate)) / 1000
this.taskEditTask.endDate = (+ new Date(this.taskEditTask.endDate)) / 1000
// remove all nulls
this.taskEditTask.reminderDates = this.removeNullsFromArray(this.taskEditTask.reminderDates)
// Make normal timestamps from js timestamps
@ -404,14 +414,12 @@
HTTP.post(`tasks/` + this.taskEditTask.id, this.taskEditTask, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(response => {
response.data.dueDate = new Date(response.data.dueDate * 1000)
response.data.reminderDates = this.makeJSReminderDatesAfterUpdate(response.data.reminderDates)
// Update the task in the list
// Update the task in the list
this.updateTaskByID(this.taskEditTask.id, response.data)
// Also update the current taskedit object so the ui changes
this.$set(this, 'taskEditTask', this.fixStuffComingFromAPI(response.data))
response.data.reminderDates.push(null) // To be able to add a new one
this.$set(this, 'taskEditTask', response.data)
this.handleSuccess({message: 'The task was successfully updated.'})
cancel() // cancel the timers
})
@ -440,6 +448,7 @@
}
}
}
this.list.tasks = this.sortTasks(this.list.tasks)
},
fixStuffComingFromAPI(task) {
// Make date objects from timestamps
@ -455,8 +464,21 @@
if (task.subtasks === null) {
task.subtasks = []
}
return task
},
sortTasks(tasks) {
if (tasks === null) {
return tasks
}
return tasks.sort(function(a,b) {
if (a.done < b.done)
return -1
if (a.done > b.done)
return 1
return 0
})
},
parseDateIfNessecary(dateUnix) {
let dateobj = (+new Date(dateUnix * 1000))
if (dateobj === 0 || dateUnix === 0) {
@ -499,18 +521,6 @@
}
return array
},
makeJSReminderDatesAfterUpdate(dates) {
// Make js timestamps from normal timestamps
for (const rd in dates) {
dates[rd] = +new Date(dates[rd] * 1000)
}
if (dates == null) {
dates = []
}
dates.push(null)
return dates
},
handleError(e) {
message.error(e, this)
},

View File

@ -10,8 +10,8 @@
<slot name="text"></slot>
</div>
<div class="actions">
<button class="button is-danger is-inverted" @click="$emit('close')">Cancel</button>
<button class="button is-success is-inverted" @click="$emit('submit')">Do it!</button>
<button class="button is-danger is-inverted noshadow" @click="$emit('close')">Cancel</button>
<button class="button is-success noshadow" @click="$emit('submit')">Do it!</button>
</div>
</div>
</div>
@ -22,9 +22,6 @@
<script>
export default {
name: 'modal',
props: {
header: ''
},
mounted: function () {
document.addEventListener('keydown', (e) => {
// Close the model when escape is pressed

View File

@ -12,7 +12,7 @@
<div class="field">
<label class="label" for="namespacetext">Namespace Name</label>
<div class="control">
<input :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="namespacetext" placeholder="The namespace text is here..." v-model="namespace.name">
<input v-focus :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="namespacetext" placeholder="The namespace text is here..." v-model="namespace.name">
</div>
</div>
<div class="field">
@ -25,7 +25,7 @@
<div class="columns bigbuttons">
<div class="column">
<button @click="submit()" class="button is-success is-fullwidth" :class="{ 'is-loading': loading}">
<button @click="submit()" class="button is-primary is-fullwidth" :class="{ 'is-loading': loading}">
Save
</button>
</div>

View File

@ -1,16 +1,17 @@
<template>
<div class="content">
<div class="fullpage">
<a class="close" @click="back()">
<icon :icon="['far', 'times-circle']">
</icon>
</a>
<h3>Create a new namespace</h3>
<form @submit.prevent="newNamespace">
<form @submit.prevent="newNamespace" @keyup.esc="back()">
<div class="field is-grouped">
<p class="control has-icons-left is-expanded" v-bind:class="{ 'is-loading': loading}">
<input class="input" v-bind:class="{ 'disabled': loading}" v-model="namespace.name" type="text" placeholder="The namespace's name goes here...">
<span class="icon is-small is-left">
<icon icon="layer-group"/>
</span>
<p class="control is-expanded" v-bind:class="{ 'is-loading': loading}">
<input v-focus class="input" v-bind:class="{ 'disabled': loading}" v-model="namespace.name" type="text" placeholder="The namespace's name goes here...">
</p>
<p class="control">
<button type="submit" class="button is-success">
<button type="submit" class="button is-success noshadow">
<span class="icon is-small">
<icon icon="plus"/>
</span>
@ -19,55 +20,59 @@
</p>
</div>
</form>
<p class="small" v-tooltip.bottom="'A namespace is a collection of lists you can share and use to organize your lists with.<br/>In fact, every list belongs to a namepace.'">What's a namespace?</p>
</div>
</template>
<script>
import auth from '../../auth'
import router from '../../router'
import {HTTP} from '../../http-common'
import message from '../../message'
import auth from '../../auth'
import router from '../../router'
import {HTTP} from '../../http-common'
import message from '../../message'
export default {
name: "NewNamespace",
data() {
return {
namespace: {title: ''},
error: '',
loading: false
}
},
beforeMount() {
// Check if the user is already logged in, if so, redirect him to the homepage
if (!auth.user.authenticated) {
router.push({name: 'home'})
}
},
methods: {
newNamespace() {
export default {
name: "NewNamespace",
data() {
return {
namespace: {title: ''},
error: '',
loading: false
}
},
beforeMount() {
// Check if the user is already logged in, if so, redirect him to the homepage
if (!auth.user.authenticated) {
router.push({name: 'home'})
}
},
created() {
this.$parent.setFullPage();
},
methods: {
newNamespace() {
const cancel = message.setLoading(this)
HTTP.put(`namespaces`, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => {
this.$parent.loadNamespaces()
this.handleSuccess({message: 'The namespace was successfully created.'})
HTTP.put(`namespaces`, this.namespace, {headers: {'Authorization': 'Bearer ' + localStorage.getItem('token')}})
.then(() => {
this.$parent.loadNamespaces()
this.handleSuccess({message: 'The namespace was successfully created.'})
cancel()
router.push({name: 'home'})
})
.catch(e => {
cancel()
})
.catch(e => {
cancel()
this.handleError(e)
})
},
handleError(e) {
message.error(e, this)
},
handleSuccess(e) {
message.success(e, this)
}
}
}
})
},
back() {
router.go(-1)
},
handleError(e) {
message.error(e, this)
},
handleSuccess(e) {
message.success(e, this)
}
}
}
</script>
<style scoped>
</style>

View File

@ -12,7 +12,7 @@
<div class="field">
<label class="label" for="teamtext">Team Name</label>
<div class="control">
<input :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="teamtext" placeholder="The team text is here..." v-model="team.name">
<input v-focus :class="{ 'disabled': loading}" :disabled="loading" class="input" type="text" id="teamtext" placeholder="The team text is here..." v-model="team.name">
</div>
</div>
<div class="field">
@ -276,6 +276,8 @@
.table{
border-top: 1px solid darken(#fff, 15%);
border-radius: 4px;
overflow: hidden;
td{
vertical-align: middle;

View File

@ -1,6 +1,9 @@
<template>
<div class="content loader-container" v-bind:class="{ 'is-loading': loading}">
<router-link :to="{name:'newTeam'}" class="button is-success button-right" >
<span class="icon is-small">
<icon icon="plus"/>
</span>
New Team
</router-link>
<h1>Teams</h1>
@ -57,35 +60,3 @@
}
}
</script>
<style lang="scss" scoped>
.button-right{
float: right;
}
ul.teams{
padding: 0;
margin-left: 0;
li{
list-style: none;
margin: 0;
border-bottom: 1px solid darken(#fff, 25%);
a{
color: #363636;
display: block;
padding: 0.5rem 1rem;
&:hover{
background: darken(#fff, 2%);
}
}
}
li:last-child{
border-bottom: none;
}
}
</style>

View File

@ -1,16 +1,17 @@
<template>
<div class="content">
<div class="fullpage">
<a class="close" @click="back()">
<icon :icon="['far', 'times-circle']">
</icon>
</a>
<h3>Create a new team</h3>
<form @submit.prevent="newTeam">
<form @submit.prevent="newTeam" @keyup.esc="back()">
<div class="field is-grouped">
<p class="control has-icons-left is-expanded" v-bind:class="{ 'is-loading': loading}">
<input class="input" v-bind:class="{ 'disabled': loading}" v-model="team.name" type="text" placeholder="The team's name goes here...">
<span class="icon is-small is-left">
<icon icon="users"/>
</span>
<p class="control is-expanded" v-bind:class="{ 'is-loading': loading}">
<input v-focus class="input" v-bind:class="{ 'disabled': loading}" v-model="team.name" type="text" placeholder="The team's name goes here...">
</p>
<p class="control">
<button type="submit" class="button is-success">
<button type="submit" class="button is-success noshadow">
<span class="icon is-small">
<icon icon="plus"/>
</span>
@ -43,6 +44,9 @@
router.push({name: 'home'})
}
},
created() {
this.$parent.setFullPage();
},
methods: {
newTeam() {
const cancel = message.setLoading(this)
@ -58,6 +62,9 @@
this.handleError(e)
})
},
back() {
router.go(-1)
},
handleError(e) {
message.error(e, this)
},
@ -67,7 +74,3 @@
}
}
</script>
<style scoped>
</style>

View File

@ -8,7 +8,7 @@
<form id="loginform" @submit.prevent="submit">
<div class="field">
<div class="control">
<input type="text" class="input" name="username" placeholder="Username" v-model="credentials.username" required>
<input v-focus type="text" class="input" name="username" placeholder="Username" v-model="credentials.username" required>
</div>
</div>
<div class="field">

View File

@ -5,7 +5,7 @@
<form id="form" @submit.prevent="submit" v-if="!successMessage">
<div class="field">
<div class="control">
<input type="password" class="input" name="password1" placeholder="Password" v-model="credentials.password" required>
<input v-focus type="password" class="input" name="password1" placeholder="Password" v-model="credentials.password" required>
</div>
</div>
<div class="field">

View File

@ -5,7 +5,7 @@
<form id="registerform" @submit.prevent="submit">
<div class="field">
<div class="control">
<input type="text" class="input" name="username" placeholder="Username" v-model="credentials.username" required>
<input v-focus type="text" class="input" name="username" placeholder="Username" v-model="credentials.username" required>
</div>
</div>
<div class="field">

View File

@ -5,7 +5,7 @@
<form id="loginform" @submit.prevent="submit" v-if="!isSuccess">
<div class="field">
<div class="control">
<input type="text" class="input" name="email" placeholder="Email-Adress" v-model="email" required>
<input v-focus type="text" class="input" name="email" placeholder="Email-Adress" v-model="email" required>
</div>
</div>

View File

@ -30,6 +30,11 @@ import { faUser } from '@fortawesome/free-solid-svg-icons'
import { faLock } from '@fortawesome/free-solid-svg-icons'
import { faPen } from '@fortawesome/free-solid-svg-icons'
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import { faTachometerAlt } from '@fortawesome/free-solid-svg-icons'
import { faCalendar } from '@fortawesome/free-solid-svg-icons'
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { faPowerOff } from '@fortawesome/free-solid-svg-icons'
import { faTimesCircle } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
library.add(faSignOutAlt)
@ -45,9 +50,28 @@ library.add(faUser)
library.add(faLock)
library.add(faPen)
library.add(faTimes)
library.add(faTachometerAlt)
library.add(faCalendar)
library.add(faTimesCircle)
library.add(faBars)
library.add(faPowerOff)
Vue.component('icon', FontAwesomeIcon)
// Tooltip
import VTooltip from 'v-tooltip'
Vue.use(VTooltip)
// Set focus
Vue.directive('focus', {
// When the bound element is inserted into the DOM...
inserted: function (el) {
// Focus the element
el.focus()
}
})
// Check the user's auth status when the app starts
auth.checkAuth()

109
src/styles/_tooltip.scss Normal file
View File

@ -0,0 +1,109 @@
.tooltip {
display: block !important;
z-index: 10000;
font-size: 0.8em;
text-align: center;
.tooltip-inner {
background: $dark;
color: white;
border-radius: 5px;
padding: 5px 10px 5px;
}
.tooltip-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: $dark;
z-index: 1;
}
&[x-placement^="top"] {
margin-bottom: 5px;
.tooltip-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="bottom"] {
margin-top: 5px;
.tooltip-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="right"] {
margin-left: 5px;
.tooltip-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[x-placement^="left"] {
margin-right: 5px;
.tooltip-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&.popover {
.popover-inner {
background: $light;
color: $dark;
padding: 24px;
border-radius: 5px;
box-shadow: 0 5px 30px rgba(black, .1);
}
.popover-arrow {
border-color: $light;
}
}
&[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}

View File

@ -0,0 +1,26 @@
$black: hsl(0, 0%, 4%) !default
$orange: #ff851b;
$green: #00CE6E;
$blue: #5974d9;
$red: #ff4136;
$primary: #198CFF !default;
$dark: lighten($black, 8);
$info-invert: #fff;
$family-sans-serif: 'Open Sans', Helvetica, Arial, sans-serif;
$thickness: 1px;
$pagination-current-border: darken($primary, 5);
$navbar-item-active-color: $primary;
$dropdown-content-shadow: none;
$navbar-dropdown-boxed-shadow: $dropdown-content-shadow;
$bulmaswatch-import-font: false !default;
$vikunja-font: 'Quicksand', sans-serif;
$vikunja-light-text: darken(#fff, 10%);
$vikunja-blue: #7F23FF;// #7F23FF; // #5974d9
$vikunja-green: #4DB788;
$navbar-padding: 1.5em;
$transition: 150ms ease;

View File

@ -0,0 +1,58 @@
// Fancy Checkboxes
.fancycheckbox {
display: inline-block;
padding-right: 5px;
padding-top: 3px;
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
&:hover svg {
stroke: $primary;
}
svg {
position: relative;
z-index: 1;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
path {
stroke-dasharray: 60;
stroke-dashoffset: 0;
}
polyline {
stroke-dasharray: 22;
stroke-dashoffset: 66;
}
}
}
input[type=checkbox]:checked + .check svg {
stroke: $primary;
}
input[type=checkbox]:checked + .check svg path {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
input[type=checkbox]:checked + .check svg polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
}

57
src/styles/fullpage.scss Normal file
View File

@ -0,0 +1,57 @@
.fullpage{
position: fixed;
text-align: center;
top: 0;
z-index: 50;
left: 0;
right: 0;
bottom: 0;
padding: 15vh 20vh;
background: $vikunja-blue;
color: $vikunja-light-text;
font-size: 2em;
@media screen and (max-width: $tablet) {
padding: 20vw 10vw;
}
input{
background: transparent;
border: none;
border-bottom: 3px solid darken($vikunja-blue, 10);
color: $vikunja-light-text;
padding: 1em 0.5em;
font-size: 1.5em;
border-radius: 0;
&::placeholder{
color: $vikunja-light-text;
}
}
.button {
font-size: 1em;
margin-top: 4px;
}
h3{
font-size: 1.5em;
}
.close {
position: fixed;
top: 5vh;
right: 6vh;
color: $vikunja-light-text;
}
.small{
margin-top: 1em;
font-size: 0.5em;
}
}
.fullpage-overlay{
z-index: 6 !important;
}

312
src/styles/general.scss Normal file
View File

@ -0,0 +1,312 @@
/* Logo */
.logo {
padding-left: 2rem !important;
img {
max-height: 3rem !important;
margin-right: 1rem;
}
}
/* Buttons icons */
.button .icon.is-small {
margin-right: 0.05rem !important;
}
/* menu buttons */
.button-bottom {
margin-bottom: 1rem;
}
.navbar-menu .navbar-item .icon{
margin: 0 0.5em;
}
.navbar{
z-index: 4 !important;
}
.app-container{
min-height: calc(100vh - 65px);
@media screen and (max-width: $tablet) {
padding-top: $navbar-height + 0.75rem;
}
.namespace-container{
background: $vikunja-blue;
box-shadow: 0 0 1em darken(#fff, 30%);
z-index: 3;
color: $vikunja-light-text;
padding: 0;
transition: all $transition;
position: fixed;
bottom: 0;
top: $navbar-height + 0.75rem;
overflow-x: auto;
width: 17vw;
@media screen and (max-width: $tablet) {
padding: 0 0 1em;
left: -147vw;
top: 0;
bottom: 0;
width: 70vw;
z-index: 5;
&.is-active {
left: 0;
}
}
.namespaces-lists{
border-top: 1px solid darken($vikunja-blue, 6);
}
.menu{
.menu-label {
font-size: 1em;
font-weight: 400;
min-height: 2.5em;
padding-top: $navbar-padding * 0.3;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.nsettings{
vertical-align: middle;
float: right;
margin-left: 0.5rem;
min-width: 2.648em;
padding-top: 5px;
}
.menu-label {
font-weight: bold;
font-family: $vikunja-font;
}
.menu-label,.nsettings,.menu-list a{
color: $vikunja-light-text;
}
.menu-label{
padding-left: $navbar-padding;
}
.menu-list {
li {
height: 36px;
}
a {
padding-left: $navbar-padding * 2;
transition: all 0.2s ease;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inline-block;
width: 100%;
.icon {
height: 1rem;
vertical-align: middle;
padding-bottom: 4px;
padding-right: 0.5em;
}
&.router-link-exact-active {
background: darken($vikunja-blue, 5%);
}
&:hover {
background: darken($vikunja-blue, 3%);
}
}
&.user{
padding: 0 0.5em 1em 1em;
border-bottom: 1px solid #6d04ff;
li{
height: auto;
}
img.is-rounded{
border-radius: 100%;
vertical-align: middle;
margin-top: -20px;
display: inline-block;
}
.username{
font-family: $vikunja-font;
font-weight: bold;
font-size: 1.5em;
padding-left: 0.5em;
margin-top: 20px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: calc(100% - 103px - 1em);
display: inline-block;
}
a.logout-icon{
float: right;
width: 53px;
margin-top: 15px;
&:hover, &:focus{
color: lighten($light, 80);
background: transparent;
}
}
}
}
}
.top-menu {
margin: $navbar-padding 0;
.menu-list a {
padding-left: $navbar-padding;
}
}
}
.app-content{
padding: $navbar-height + 1.5rem 1.5em 0 1.5em;
z-index: 2;
background: url('../public/images/llama-upside-down.svg') no-repeat top right darken(#fff, 4);
margin-left: 17vw;
@media screen and (max-width: $tablet) {
margin-left: 0;
padding-top: 1.5em;
min-height: calc(100vh - 4rem);
}
.card{
background: #fff;
}
}
}
.mobilemenu-hide-button,.mobilemenu-show-button{
display: none;
position: fixed;
z-index: 5;
font-weight: bold;
font-size: 2em;
color: $light;
&:hover, &:focus{
color: $light;
}
}
.mobilemenu-hide-button{
color: $dark;
&:hover, &:focus{
color: $dark;
}
}
.mobile-overlay{
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(250,250,250,0.8);
z-index: 4;
opacity: 0;
transition: all $transition;
}
@media screen and (max-width: $tablet) {
.mobilemenu-hide-button{
display: block;
top: 1vh;
right: 4vh;
}
.mobilemenu-show-button{
display: block;
top: 1vh;
left: 4vh;
}
.mobile-overlay{
display: block;
opacity: 1;
}
.navbar.is-dark .navbar-brand > .navbar-item{
margin: 0 auto;
}
}
/* Logout-icon */
.logout-icon {
margin-right: 0.85em !important;
}
/* Loading spinner */
.loader-container {
&.is-loading {
position: relative;
pointer-events: none;
opacity: 0.5;
&:after {
@include loader;
position: absolute;
top: calc(50% - 2.5em);
left: calc(50% - 2.5em);
width: 5em;
height: 5em;
border-width: 0.25em;
}
}
}
.spinner{
&.is-loading {
pointer-events: none;
&:after {
@include loader;
width: 2em;
height: 2em;
margin-left: calc(50% - 1em);
position: absolute;
margin-top: 1em;
z-index: 999;
border-width: 0.25em;
}
}
}
.notifications{
left: 0.5em !important;
bottom: 1em !important;
.notification-wrapper .notification{
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
border-top-width: 0;
border-right-width: 0;
border-bottom-width: 0;
border-left-width: 0.4em;
}
}
.button-right{
float: right;
}

101
src/styles/tasks.scss Normal file
View File

@ -0,0 +1,101 @@
.settings{
float: right;
color: rgb(74, 74, 74);
}
.tasks {
margin-top: 1rem;
padding: 0;
text-align: left;
max-width: 80vw;
@media screen and (min-width: $tablet) {
&.short {
max-width: 53vw;
}
}
@media screen and (max-width: $tablet) {
max-width: 100%;
}
&.noborder{
margin: 1rem -0.5rem;
}
.task {
display: block;
padding: 0.5rem 1rem;
border-bottom: 1px solid darken(#fff, 10%);
label{
width: 96%;
display: inline-block;
cursor: pointer;
.tasktext {
vertical-align: top;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: inline-block;
width: 94%;
@media screen and (max-width: $tablet) {
width: 89%;
}
&.done{
text-decoration: line-through;
color: $grey;
}
}
}
input[type="checkbox"] {
vertical-align: middle;
}
.settings{
float: right;
width: 4%;
cursor: pointer;
}
}
.task:last-child {
border-bottom: none;
}
}
.taskedit{
min-height: calc(100% - 1rem);
margin-top: 1rem;
.reminder-input{
margin: 0;
&.overdue input{
color: $red;
}
&:last-child {
margin-bottom: 0.75rem;
}
a {
color: $red;
vertical-align: sub;
}
input {
width: 90%;
border: none;
&:focus {
border: none;
box-shadow: none;
}
}
}
}

25
src/styles/teams.scss Normal file
View File

@ -0,0 +1,25 @@
ul.teams{
padding: 0;
margin-left: 0;
overflow: hidden;
li{
list-style: none;
margin: 0;
border-bottom: 1px solid $border;
a{
color: #363636;
display: block;
padding: 0.5rem 1rem;
&:hover{
background: darken(#fff, 2%);
}
}
}
li:last-child{
border-bottom: none;
}
}

230
src/styles/theme.scss Normal file
View File

@ -0,0 +1,230 @@
@import url('/fonts/fonts.css');
@import 'variables';
@import "../../node_modules/bulma/bulma";
*, *:hover, *:active, *:focus{
outline: none;
}
.button {
transition: all $transition;
border: 0;
text-transform: uppercase;
font-size: 0.85rem;
font-weight: bold;
height: 2.648em;
box-shadow: 0.3em 0.3em 1em lighten($dark, 75);
&.is-hovered,
&:hover {
box-shadow: 0.6em 0.6em 1em lighten($dark, 75);
}
&.is-active,
&.is-focused,
&:active,
&:focus,
&:focus:not(:active) {
box-shadow: 0.1em 0.1em 0.7em lighten($dark, 75) !important;
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
&.is-#{$name} {
box-shadow: 0.3em 0.3em 1em lighten($color, 30);
&.is-hovered,
&:hover {
box-shadow: 0.6em 0.6em 1em lighten($color, 30);
}
&.is-active,
&.is-focused,
&:active,
&:focus,
&:focus:not(:active) {
box-shadow: 0.1em 0.1em 0.7em lighten($color, 30) !important;
}
}
}
&.noshadow{
&,
&.is-hovered,
&:hover,
&.is-active,
&.is-focused,
&:active,
&:focus,
&:focus:not(:active) {
box-shadow: none;
}
}
}
.input,
.textarea {
transition: all $transition;
box-shadow: none;
&.is-active,
&.is-focused,
&:active,
&:focus {
box-shadow: none;
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
&.is-active,
&.is-focused,
&:active,
&:focus,
&:focus:not(:active) {
border-color: darken($color, 10);
}
}
}
}
.select:after {
margin-top: -0.575em;
}
.select select {
border-width: $thickness;
&:not([multiple]) {
height: calc(2.25em + #{$thickness});
}
&.is-active,
&.is-focused,
&:active,
&:focus,
&:focus:not(:active) {
box-shadow: none;
}
}
.field.has-addons {
.control .select select {
height: 2.25em;
}
}
.notification {
border: $thickness solid $border;
@each $name, $pair in $colors {
$color: nth($pair, 1);
&.is-#{$name} {
border-color: darken($color, 5);
}
}
}
.progress {
border-radius: $radius-large;
}
.card {
background-color: rgba($grey-lighter, 0.075);
border-radius: $radius;
margin-bottom: 1rem;
.card-image {
img {
border-radius: $radius $radius 0 0;
}
}
.card-header {
box-shadow: none;
border-bottom: 1px solid $grey-lighter;
border-radius: $radius $radius 0 0;
}
}
.box,.card{
border: $thickness solid $border;
box-shadow: 0.3em 0.3em 0.8em darken($light, 6);
}
.message {
.message-body {
border: $thickness solid;
}
}
.hero {
.navbar {
border: none;
box-shadow: none;
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
.navbar {
box-shadow: none;
}
}
}
@include touch {
.navbar-menu {
box-shadow: none;
}
}
}
.navbar {
z-index: 2;
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
&.is-#{$name} {
border-color: darken($color, 5);
}
}
.navbar-dropdown {
box-shadow: $navbar-dropdown-boxed-shadow;
top: 101%;
}
}
.pagination-link,
.pagination-next,
.pagination-previous {
border-width: $thickness;
}
body {
background: url('../public/images/llama.svg') no-repeat bottom left fixed darken(#fff, 4);
min-height: 100vh;
}
h1,h2,h3,h4,h5,h6{
font-family: $vikunja-font;
font-weight: 400 !important;
}
.bigbuttons{
margin-top: 0.5rem;
}
.control.has-icons-left .icon, .control.has-icons-right .icon {
z-index: 0;
}
.buttonright {
margin-right: 0.5rem;
}

View File

@ -1,263 +1,9 @@
*, *:hover, *:active, *:focus{
outline: none;
}
@import 'styles/theme';
@import 'styles/general';
@import '../node_modules/bulmaswatch/lumen/variables';
@import "../node_modules/bulma/bulma";
@import '../node_modules/bulmaswatch/lumen/overrides';
@import 'styles/tasks';
@import 'styles/teams';
@import 'styles/fullpage';
@import url('/fonts/fonts.css');
*, *:focus, *:active{
outline: none;
}
body {
background: url('../public/images/llama.svg') no-repeat bottom right fixed darken(#fff, 7%);
min-height: 100vh;
}
h1,h2,h3,h4,h5,h6{
font-family: 'Quicksand', sans-serif;
font-weight: 400 !important;
}
/* Logout-icon */
.logout-icon {
margin-right: 0.85em !important;
}
/* Logo */
.logo {
padding-left: 2rem !important;
img {
max-height: 3rem !important;
margin-right: 1rem;
}
}
/* Buttons icons */
.button .icon.is-small {
margin-right: 0.05rem !important;
}
/* List active link */
.menu-list a.router-link-active{
background: darken(#fff, 5%);
}
/* menu buttons */
.button-bottom {
margin-bottom: 1rem;
}
.navbar-menu .navbar-item .icon{
margin: 0 0.5em;
}
/* Namespaces list */
.namespaces-lists{
.menu-label {
font-size: 1em;
font-weight: 400;
min-height: 2.5em;
padding-top: 0.3em;
}
/* Namespace settings */
.nsettings{
vertical-align: middle;
float: right;
margin-left: 0.5rem;
min-width: 2.648em;
color: rgb(74, 74, 74);
}
}
.bigbuttons{
margin-top: 0.5rem;
}
.card{
margin-bottom: 1rem;
}
.buttonright {
margin-right: 0.5rem;
}
.tasks.noborder{
margin: 1rem -0.5rem;
}
.tasks {
margin-top: 1rem;
padding: 0;
text-align: left;
.task {
display: block;
padding: 0.5rem 1rem;
border-bottom: 1px solid darken(#fff, 10%);
label{
width: 96%;
display: inline-block;
cursor: pointer;
.tasktext {
vertical-align: top;
}
}
input[type="checkbox"] {
vertical-align: middle;
}
.settings{
float: right;
width: 4%;
cursor: pointer;
}
}
.task:last-child {
border-bottom: none;
}
}
.taskedit{
min-height: calc(100% - 1rem);
margin-top: 1rem;
.subtasks {
input {
width: auto;
}
.button {
float:right;
}
}
.reminder-input{
margin: 0;
&.overdue input{
color: $red;
}
&:last-child {
margin-bottom: 0.75rem;
}
a {
color: $red;
vertical-align: sub;
}
input {
width: 90%;
border: none;
&:focus {
border: none;
box-shadow: none;
}
}
}
}
.settings{
float: right;
color: rgb(74, 74, 74);
}
.column.container {
padding:0;
.box.shadow {
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
box-shadow: 2px 2px 5px lighten(#000, 85%);
}
}
/* Loading spinner */
.loader-container {
&.is-loading {
position: relative;
pointer-events: none;
opacity: 0.5;
&:after {
@include loader;
position: absolute;
top: calc(50% - 2.5em);
left: calc(50% - 2.5em);
width: 5em;
height: 5em;
border-width: 0.25em;
}
}
}
// Fancy Checkboxes
.fancycheckbox {
display: inline-block;
padding-right: 5px;
padding-top: 3px;
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
&:hover svg {
stroke: $primary;
}
svg {
position: relative;
z-index: 1;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
path {
stroke-dasharray: 60;
stroke-dashoffset: 0;
}
polyline {
stroke-dasharray: 22;
stroke-dashoffset: 66;
}
}
}
input[type=checkbox]:checked + .check svg {
stroke: $primary;
}
input[type=checkbox]:checked + .check svg path {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
input[type=checkbox]:checked + .check svg polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
}
@import 'styles/fancycheckbox';
@import 'styles/tooltip';