Permalink
Browse files

added vuejs table component, list users

  • Loading branch information...
aaroniker committed Jan 7, 2019
1 parent 89e65f3 commit de4e5c77ef5643fa814407dc716eea6d3497cade
@@ -19,6 +19,7 @@ Currently heavily WIP.
* [axios](https://github.com/axios/axios)
* [Twig](http://twig.sensiolabs.org/)
* [Medoo](https://github.com/catfan/Medoo)
* [Teible](https://github.com/hiendv/teible)
* ...

_Structure is inspired by [Pagekit](http://pagekit.com/)_
@@ -3,5 +3,8 @@
"Show less": "Weniger anzeigen",
"Remove all": "Alle löschen",
"No messages": "Keine Nachrichten",
"URL <strong>%s</strong> not found": "URL <strong>%s</strong> nicht gefunden"
"URL <strong>%s</strong> not found": "URL <strong>%s</strong> nicht gefunden",
"Showing %s of %s entries": "Zeige %s von %s Einträgen",
"No entries": "Keine Einträge",
"Search": "Suche"
}
@@ -35,7 +35,7 @@ public function getUser($id) {
}
public function getUsers() {
return array_intersect_key($this->app->db->get($this->module->config('table'), '*'), array_flip(['id', 'username', 'email']));
return $this->app->db->select($this->module->config('table'), ['id', 'username', 'email']);
}
public function addUser($username, $email, $hash) {
@@ -1,3 +1,13 @@
<?php
var_dump($app->user->getUsers());
?>
<g-table :items='<?= json_encode($app->user->getUsers()); ?>'>
<g-column class="grow" field="username" label="Name">
<template slot-scope="props">
<a href="" v-text="props.value"></a>
</template>
</g-column>
<g-column class="half" field="email" label="Email"></g-column>
<g-column class="action" label="" :sortable="false">
<template slot-scope="props">
...
</template>
</g-column>
</g-table>
@@ -88,7 +88,7 @@ module.exports = {
toggle() {
this.open = !this.open;
},
documentClick: function(e) {
documentClick(e) {
var el = this.$refs.messageList;
var target = e.target;
if((el !== target) && !el.contains(target)) {
@@ -45,6 +45,7 @@ body {
a {
text-decoration: none;
color: var(--link);
transition: color .3s ease;
&:hover,
&:focus {
text-decoration: none;
@@ -1,6 +1,6 @@
:root {
--input-color: var(--text);
--input-background: var(--dark-2);
--input-background: var(--dark-3);
--input-background-disabled: var(--dark-3);
--input-placeholder: var(--text-muted);
--input-disabled: var(--text-muted);
@@ -1,3 +1,5 @@
import { GTable, GColumn } from './table'

function install(Vue) {

var gear = window.$gear;
@@ -6,8 +8,9 @@ function install(Vue) {
Vue.config.debug = gear.debug;
Vue.config.productionTip = gear.debug;

var axios = require('axios');
var $ = require('jquery');
var axios = require('axios'),
$ = require('jquery'),
vsprintf = require('sprintf-js').vsprintf;

Vue.prototype.$api = axios.create({
baseURL: gear.url + '/api',
@@ -16,8 +19,11 @@ function install(Vue) {
}
});

function getLang(name) {
function getLang(name, array) {
if(name in lang) {
if(array !== undefined) {
return vsprintf(lang[name], array);
}
return lang[name];
}
return name;
@@ -33,18 +39,22 @@ function install(Vue) {
});
};

Vue.filter('lang', function(name) {
return getLang(name);
Vue.filter('lang', function(name, array) {
return getLang(name, array);
});

Vue.prototype.$lang = function(name) {
return getLang(name);
Vue.prototype.$lang = function(name, array) {
return getLang(name, array);
};

Vue.prototype.$visibility = require('visibilityjs');

new Vue({
el: '#gear'
el: '#gear',
components: {
GTable,
GColumn
}
});

}
@@ -0,0 +1,27 @@
<template>
<div class="body">
<div class="row" v-for="(d, index) in items" :key="index">
<g-cell v-for="(column, columnIndex) in columns" v-bind="column.attrs" :item="d" :class="column.staticClass" :column="column" :key="columnIndex" />
</div>
<div class="row" v-if="items.length < 1">{{ 'No entries' | lang }}</div>
</div>
</template>
<script>
import GCell from './GCell'
export default {
name: 'GBody',
components: {
GCell
},
props: {
items: {
type: Array,
required: true
},
columns: {
type: Array,
required: true
}
}
}
</script>
@@ -0,0 +1,31 @@
import { dotGet } from './helpers'

export default {
functional: true,
props: {
item: {
type: Object,
required: true
},
column: {
type: Object,
required: true
}
},
render(h, { props, data, listeners }) {
if(props.column.field) {
let value = dotGet(props.item, props.column.field)
if(typeof value !== 'string') {
value = JSON.stringify(value)
}
if(props.column.scopedSlots && typeof props.column.scopedSlots.default === 'function') {
return h('div', data, props.column.scopedSlots.default({ value, item: props.item, column: props.column }))
}
return h('div', data, value)
}
if(props.column.scopedSlots && typeof props.column.scopedSlots.default === 'function') {
return h('div', data, props.column.scopedSlots.default(props))
}
return h('div', data, props.column.children)
}
}
@@ -0,0 +1,24 @@
export default {
name: 'GColumn',
props: {
label: {
type: String,
required: true
},
field: {
type: String,
default: ''
},
sortable: {
type: Boolean,
default: true
},
filterable: {
type: Boolean,
default: true
},
render: {
type: Function
}
}
}
@@ -0,0 +1,57 @@
<template>
<div class="search">
<input class="form-field" :value="filter" type="text" :placeholder="'Search' | lang" @input="update($event.target.value)">
<a v-if="filter" href="" @click.prevent="clear">
<svg>
<use xlink:href="#crossUI" />
</svg>
</a>
</div>
</template>
<script>
export default {
name: 'GFilter',
props: {
filter: {
type: String,
required: true
}
},
methods: {
update(filter) {
this.$emit('update:filter', filter)
},
clear() {
this.$emit('update:filter', '')
}
}
}
</script>
<style lang="scss">
:root {
--search-cross: var(--text-muted);
--search-cross-hover: var(--text);
}
.search {
position: relative;
a {
position: absolute;
right: 16px;
top: 50%;
margin: -8px 0 0 0;
display: block;
svg {
width: 16px;
height: 16px;
display: block;
color: var(--search-cross);
transition: color .3s ease;
}
&:hover {
svg {
color: var(--search-cross-hover);
}
}
}
}
</style>
@@ -0,0 +1,54 @@
<template>
<div class="head row">
<div v-for="(column, index) in columns" :key="column.field + column.label" :class="[{
'custom': column.scopedSlots && column.scopedSlots.header,
'sortable': column.sortable,
'active': isActive(column)
}, column.staticClass, column.dynamicClass]" v-bind="column.attrs" scope="col" @click.prevent="updateSort(column.field, column.sortable)">
<g-head-content :column="column" :active="isActive(column)" :sort-desc="sortDesc" />
</div>
</div>
</template>
<script>
import GHeadContent from './GHeadContent'
export default {
name: 'GHead',
components: {
GHeadContent
},
props: {
columns: {
type: Array,
required: true
},
sortBy: {
type: String,
default: ''
},
sortDesc: {
type: Boolean,
default: false
}
},
methods: {
isActive(column) {
return !!(column.sortable) && this.isSortedBy(column.field)
},
isSortedBy(field) {
return this.sortBy === field
},
updateSort(field, sortable) {
if(!field || !sortable) {
return
}
if(this.isSortedBy(field)) {
this.$emit('update:sortDesc', !this.sortDesc)
return
}
this.$emit('update:sortBy', field)
}
}
}
</script>
<style lang="scss">
</style>
@@ -0,0 +1,46 @@
const capitalize = str => {
if(!str) {
return
}
return str.charAt(0).toUpperCase() + str.slice(1)
}

export default {
functional: true,
props: {
column: {
type: Object,
required: true
},
active: {
type: Boolean,
required: true
},
sortDesc: {
type: Boolean,
required: true
}
},
render(h, { props, parent }) {
if(props.column.scopedSlots && props.column.scopedSlots.header) {
return h('span', {
on: {
click(e) {
e.stopPropagation()
}
}
}, props.column.scopedSlots.header(props))
}
let children = [h('span', capitalize(parent.$lang(props.column.label || props.column.field)))]
if(props.column.sortable) {
children.push(h('span', {
class: {
active: props.active,
sortDown: props.sortDesc,
sortUp: !props.sortDesc
}
}))
}
return children
}
}
Oops, something went wrong.

0 comments on commit de4e5c7

Please sign in to comment.