mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 00:59:06 +00:00
created base layout, reuseable context and logging off
This commit is contained in:
parent
9b19908914
commit
2139f3d9da
16 changed files with 554 additions and 10 deletions
web-frontend
assets/scss
components
config
directives
layouts
pages/app
plugins
store
utils
|
@ -64,3 +64,10 @@ $z-index-layout-col-3: 1;
|
||||||
$z-index-layout-col-3-1: 5;
|
$z-index-layout-col-3-1: 5;
|
||||||
$z-index-layout-col-3-2: 4;
|
$z-index-layout-col-3-2: 4;
|
||||||
$z-index-modal: 6;
|
$z-index-modal: 6;
|
||||||
|
|
||||||
|
// The z-index of the context must always be the highest because they can open in a
|
||||||
|
// modal.
|
||||||
|
$z-index-context: 7;
|
||||||
|
|
||||||
|
// normalize overrides
|
||||||
|
$base-font-family: $text-font-stack;
|
||||||
|
|
|
@ -11,6 +11,10 @@ body {
|
||||||
background-color: $color-neutral-100;
|
background-color: $color-neutral-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visibility-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.align-right {
|
.align-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.context {
|
.context {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: $z-index-context;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
158
web-frontend/components/Context.vue
Normal file
158
web-frontend/components/Context.vue
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-move-to-body
|
||||||
|
v-click-outside="hide"
|
||||||
|
class="context"
|
||||||
|
:class="{ 'visibility-hidden': !open }"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { isElement } from '@/utils/dom'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Context',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
opener: null,
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Because we don't want the parent context to close when a user clicks 'outside' that
|
||||||
|
* element and in the child element we need to register the child with their parent to
|
||||||
|
* prevent this.
|
||||||
|
*/
|
||||||
|
beforeMount() {
|
||||||
|
let $parent = this.$parent
|
||||||
|
while ($parent !== undefined) {
|
||||||
|
if ($parent.registerContextChild) {
|
||||||
|
$parent.registerContextChild(this.$el)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
$parent = $parent.$parent
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Toggles the open state of the context menu.
|
||||||
|
*
|
||||||
|
* @param target The original element element that changed the state of the
|
||||||
|
* context, this will be used to calculate the correct position.
|
||||||
|
* @param vertical Bottom positions the context under the target.
|
||||||
|
* Top positions the context above the target.
|
||||||
|
* @param horizontal Left aligns the context with the left side of the target.
|
||||||
|
* Right aligns the context with the right side of the target.
|
||||||
|
* @param offset The distance between the target element and the context.
|
||||||
|
* @param value True if context must be shown, false if not and undefine
|
||||||
|
* will invert the current state.
|
||||||
|
*/
|
||||||
|
toggle(
|
||||||
|
target,
|
||||||
|
vertical = 'bottom',
|
||||||
|
horizontal = 'left',
|
||||||
|
offset = 10,
|
||||||
|
value
|
||||||
|
) {
|
||||||
|
if (value === undefined) {
|
||||||
|
value = !this.open
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
const css = this.calculatePosition(target, vertical, horizontal, offset)
|
||||||
|
|
||||||
|
// Set the calculated positions of the context.
|
||||||
|
for (const key in css) {
|
||||||
|
const value = css[key] !== null ? Math.ceil(css[key]) + 'px' : 'auto'
|
||||||
|
this.$el.style[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we store the element who opened the context menu we can exclude the element
|
||||||
|
// when clicked outside of this element.
|
||||||
|
this.opener = value ? target : null
|
||||||
|
this.open = value
|
||||||
|
},
|
||||||
|
hide(event) {
|
||||||
|
// Checks if the click is inside one of our children. In that code the context
|
||||||
|
// must stay open.
|
||||||
|
const isChild = this.children.some(element =>
|
||||||
|
isElement(element, event.target)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Checks if the context is already opened, if the click was not on the opener
|
||||||
|
// because he can trigger the toggle method and if the click was not in one of
|
||||||
|
// our child contexts.
|
||||||
|
if (this.open && !isElement(this.opener, event.target) && !isChild) {
|
||||||
|
this.open = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Calculates the absolute position of the context based on the original clicked
|
||||||
|
* element.
|
||||||
|
*/
|
||||||
|
calculatePosition(target, vertical, horizontal, offset) {
|
||||||
|
const targetRect = target.getBoundingClientRect()
|
||||||
|
const contextRect = this.$el.getBoundingClientRect()
|
||||||
|
const positions = { top: null, right: null, bottom: null, left: null }
|
||||||
|
|
||||||
|
// Calculate if top, bottom, left and right positions are possible.
|
||||||
|
const canTop = targetRect.top - contextRect.height - offset > 0
|
||||||
|
const canBottom =
|
||||||
|
window.innerHeight - targetRect.bottom - contextRect.height - offset > 0
|
||||||
|
const canRight = targetRect.right - contextRect.width > 0
|
||||||
|
const canLeft =
|
||||||
|
window.innerWidth - targetRect.left - contextRect.width > 0
|
||||||
|
|
||||||
|
// If bottom, top, left or right doesn't fit, but their opposite does we switch to
|
||||||
|
// that.
|
||||||
|
if (vertical === 'bottom' && !canBottom && canTop) {
|
||||||
|
vertical = 'top'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertical === 'top' && !canTop) {
|
||||||
|
vertical = 'bottom'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontal === 'left' && !canLeft && canRight) {
|
||||||
|
horizontal = 'right'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontal === 'right' && !canRight) {
|
||||||
|
horizontal = 'left'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the correct positions for horizontal and vertical values.
|
||||||
|
if (horizontal === 'left') {
|
||||||
|
positions.left = targetRect.left
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontal === 'right') {
|
||||||
|
positions.right = window.innerWidth - targetRect.right
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertical === 'bottom') {
|
||||||
|
positions.top = targetRect.bottom + offset
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vertical === 'top') {
|
||||||
|
positions.bottom = window.innerHeight - targetRect.top + offset
|
||||||
|
}
|
||||||
|
|
||||||
|
return positions
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* A child context can register itself with the parent to prevent closing of the
|
||||||
|
* parent when clicked inside the child.
|
||||||
|
*
|
||||||
|
* @param element HTMLElement
|
||||||
|
*/
|
||||||
|
registerContextChild(element) {
|
||||||
|
this.children.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -25,7 +25,11 @@ export default {
|
||||||
/*
|
/*
|
||||||
** Plugins to load before mounting the App
|
** Plugins to load before mounting the App
|
||||||
*/
|
*/
|
||||||
plugins: [{ src: '@/plugins/auth.js' }, { src: '@/plugins/vuelidate.js' }],
|
plugins: [
|
||||||
|
{ src: '@/plugins/auth.js' },
|
||||||
|
{ src: '@/plugins/directives.js' },
|
||||||
|
{ src: '@/plugins/vuelidate.js' }
|
||||||
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Nuxt.js modules
|
** Nuxt.js modules
|
||||||
|
|
19
web-frontend/directives/clickOutside.js
Normal file
19
web-frontend/directives/clickOutside.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { isElement } from '@/utils/dom'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This directive calls a custom method if the user clicks outside of the
|
||||||
|
* element.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
bind: (el, binding, vnode) => {
|
||||||
|
el.clickOutsideEvent = event => {
|
||||||
|
if (!isElement(el, event.target)) {
|
||||||
|
vnode.context[binding.expression](event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.body.addEventListener('click', el.clickOutsideEvent)
|
||||||
|
},
|
||||||
|
unbind: el => {
|
||||||
|
document.body.removeEventListener('click', el.clickOutsideEvent)
|
||||||
|
}
|
||||||
|
}
|
18
web-frontend/directives/moveToBody.js
Normal file
18
web-frontend/directives/moveToBody.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* This directive moves the whole element to the document body so that it can be
|
||||||
|
* positioned over another element.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
inserted: el => {
|
||||||
|
const body = document.body
|
||||||
|
|
||||||
|
// The element is added as first child in the body so that child contexts
|
||||||
|
// are being shown on top of their parent.
|
||||||
|
body.insertBefore(el, body.firstChild)
|
||||||
|
},
|
||||||
|
unbind: el => {
|
||||||
|
if (el.parentNode) {
|
||||||
|
el.parentNode.removeChild(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
278
web-frontend/layouts/app.vue
Normal file
278
web-frontend/layouts/app.vue
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
<template>
|
||||||
|
<div :class="{ 'layout-collapsed': isCollapsed }" class="layout">
|
||||||
|
<div class="layout-col-1 menu">
|
||||||
|
<ul class="menu-items">
|
||||||
|
<li class="menu-item">
|
||||||
|
<nuxt-link :to="{ name: 'app' }" class="menu-link">
|
||||||
|
<i class="fas fa-tachometer-alt"></i>
|
||||||
|
<span class="menu-link-text">Dashboard</span>
|
||||||
|
</nuxt-link>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a href="#" class="menu-link" data-context=".select">
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
<span class="menu-link-text">Groups</span>
|
||||||
|
</a>
|
||||||
|
<div class="select hidden">
|
||||||
|
<div class="select-search">
|
||||||
|
<i class="select-search-icon fas fa-search"></i>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="select-search-input"
|
||||||
|
placeholder="Search views"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ul class="select-items">
|
||||||
|
<li class="select-item active">
|
||||||
|
<a href="#" class="select-item-link">Group name 1</a>
|
||||||
|
<a href="#" class="select-item-options" data-context=".context">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
<div class="context hidden">
|
||||||
|
<ul class="context-menu">
|
||||||
|
<li>
|
||||||
|
<a href="#">
|
||||||
|
<i class="context-menu-icon fas fa-fw fa-pen"></i>
|
||||||
|
Rename group
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">
|
||||||
|
<i class="context-menu-icon fas fa-fw fa-trash"></i>
|
||||||
|
Delete group
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="select-item">
|
||||||
|
<a href="#" class="select-item-link">Group name 2</a>
|
||||||
|
<a href="#" class="select-item-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="select-item">
|
||||||
|
<a href="#" class="select-item-link">Group name 3</a>
|
||||||
|
<a href="#" class="select-item-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="select-item">
|
||||||
|
<a href="#" class="select-item-link">Group name 4</a>
|
||||||
|
<a href="#" class="select-item-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="select-footer">
|
||||||
|
<a href="#" class="select-footer-button">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
Do something
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="menu-items">
|
||||||
|
<li class="menu-item layout-uncollapse">
|
||||||
|
<a class="menu-link" @click="toggleCollapsed()">
|
||||||
|
<i class="menu-item-icon fas fa-angle-double-right"></i>
|
||||||
|
<span class="menu-link-text">Uncollapse</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a
|
||||||
|
class="menu-link menu-user-item"
|
||||||
|
@click="$refs.userContext.toggle($event.target)"
|
||||||
|
>
|
||||||
|
{{ nameAbbreviation }}
|
||||||
|
<span class="menu-link-text">{{ name }}</span>
|
||||||
|
</a>
|
||||||
|
<Context ref="userContext">
|
||||||
|
<div class="context-menu-title">{{ name }}</div>
|
||||||
|
<ul class="context-menu">
|
||||||
|
<li>
|
||||||
|
<a @click="logoff()">
|
||||||
|
<i class="context-menu-icon fas fa-fw fa-sign-out-alt"></i>
|
||||||
|
Logoff
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Context>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layout-col-2 sidebar">
|
||||||
|
<div class="sidebar-content-wrapper">
|
||||||
|
<nav class="sidebar-content">
|
||||||
|
<div class="sidebar-title">
|
||||||
|
<img src="@/static/img/logo.svg" alt="" />
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-group-title">Group name 1</div>
|
||||||
|
<ul class="tree">
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Vehicles
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options" data-context=".context">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
<div class="context hidden">
|
||||||
|
<div class="context-menu-title">Vehicles</div>
|
||||||
|
<ul class="context-menu">
|
||||||
|
<li>
|
||||||
|
<a href="#">
|
||||||
|
<i class="context-menu-icon fas fa-fw fa-pen"></i>
|
||||||
|
Rename database
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">
|
||||||
|
<i class="context-menu-icon fas fa-fw fa-trash"></i>
|
||||||
|
Delete table
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-angle-right"></i>
|
||||||
|
Map nummer 1
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item active">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Webshop
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<ul class="tree-subs">
|
||||||
|
<li class="tree-sub active">
|
||||||
|
<a href="#" class="tree-sub-link">Customers</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="tree-sub">
|
||||||
|
<a href="#" class="tree-sub-link">Products very long name</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="tree-sub">
|
||||||
|
<a href="#" class="tree-sub-link">Categories</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-angle-down"></i>
|
||||||
|
Map nummer 1
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<ul class="tree">
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Vehicles
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Something
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Vehicles with very long name and that is not good.
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="tree-item">
|
||||||
|
<div class="tree-action">
|
||||||
|
<a href="#" class="tree-link">
|
||||||
|
<i class="tree-type fas fa-database"></i>
|
||||||
|
Something else
|
||||||
|
</a>
|
||||||
|
<a href="#" class="tree-options">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-footer">
|
||||||
|
<a class="sidebar-collapse" @click="toggleCollapsed()">
|
||||||
|
<i class="fas fa-angle-double-left"></i>
|
||||||
|
Collapse sidebar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-col-3">
|
||||||
|
<nuxt />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapGetters } from 'vuex'
|
||||||
|
|
||||||
|
import Context from '@/components/Context'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
layout: 'default',
|
||||||
|
middleware: 'authenticated',
|
||||||
|
components: {
|
||||||
|
Context
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
isCollapsed: 'sidebar/isCollapsed',
|
||||||
|
name: 'auth/getName',
|
||||||
|
nameAbbreviation: 'auth/getNameAbbreviation'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logoff() {
|
||||||
|
this.$store.dispatch('auth/logoff')
|
||||||
|
this.$nuxt.$router.replace({ name: 'login' })
|
||||||
|
},
|
||||||
|
...mapActions({
|
||||||
|
toggleCollapsed: 'sidebar/toggleCollapsed'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,5 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<nuxt />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -8,7 +8,7 @@
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
middleware: 'authenticated',
|
layout: 'app',
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
user: state => state.auth.user
|
user: state => state.auth.user
|
||||||
|
|
7
web-frontend/plugins/directives.js
Normal file
7
web-frontend/plugins/directives.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
import moveToBody from '@/directives/moveToBody'
|
||||||
|
import clickOutside from '@/directives/clickOutside'
|
||||||
|
|
||||||
|
Vue.directive('move-to-body', moveToBody)
|
||||||
|
Vue.directive('click-outside', clickOutside)
|
|
@ -49,6 +49,14 @@ export const actions = {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Logs off the user by removing the token as a cookie and clearing the user
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
logoff({ commit }) {
|
||||||
|
unsetToken(this.app.$cookies)
|
||||||
|
commit('CLEAR_USER_DATA')
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Refresh the existing token. If successful commit the new token and start a
|
* Refresh the existing token. If successful commit the new token and start a
|
||||||
* new refresh timeout. If unsuccessful the existing cookie and user data is
|
* new refresh timeout. If unsuccessful the existing cookie and user data is
|
||||||
|
@ -100,10 +108,19 @@ export const getters = {
|
||||||
token(state) {
|
token(state) {
|
||||||
return state.token
|
return state.token
|
||||||
},
|
},
|
||||||
|
getName(state) {
|
||||||
|
return state.user ? state.user.first_name : ''
|
||||||
|
},
|
||||||
|
getNameAbbreviation(state) {
|
||||||
|
return state.user ? state.user.first_name.split('')[0] : ''
|
||||||
|
},
|
||||||
|
getEmail(state) {
|
||||||
|
return state.user ? state.user.email : ''
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Returns the amount of seconds it will take before the tokes expires.
|
* Returns the amount of seconds it will take before the tokes expires.
|
||||||
* @TODO figure out what happens if the browser and server time and not very
|
* @TODO figure out what happens if the browser and server time are not in
|
||||||
* much in sync.
|
* sync.
|
||||||
*/
|
*/
|
||||||
tokenExpireSeconds(state) {
|
tokenExpireSeconds(state) {
|
||||||
const now = Math.ceil(new Date().getTime() / 1000)
|
const now = Math.ceil(new Date().getTime() / 1000)
|
||||||
|
|
24
web-frontend/store/sidebar.js
Normal file
24
web-frontend/store/sidebar.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
export const state = () => ({
|
||||||
|
collapsed: false
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mutations = {
|
||||||
|
SET_COLLAPSED(state, collapsed) {
|
||||||
|
state.collapsed = collapsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const actions = {
|
||||||
|
toggleCollapsed({ commit, getters }, value) {
|
||||||
|
if (value === undefined) {
|
||||||
|
value = !getters.isCollapsed
|
||||||
|
}
|
||||||
|
commit('SET_COLLAPSED', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getters = {
|
||||||
|
isCollapsed(state) {
|
||||||
|
return !!state.collapsed
|
||||||
|
}
|
||||||
|
}
|
9
web-frontend/utils/dom.js
Normal file
9
web-frontend/utils/dom.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/**
|
||||||
|
* Checks if the target is the same as the provided element of that the element
|
||||||
|
* contains the target. Returns true is this is the case.
|
||||||
|
*
|
||||||
|
* @returns boolean
|
||||||
|
*/
|
||||||
|
export const isElement = (element, target) => {
|
||||||
|
return element === target || element.contains(target)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue