83 lines
2.1 KiB
Vue
83 lines
2.1 KiB
Vue
<script>
|
|
import { GlIcon } from '@gitlab/ui';
|
|
import Sorter from '../../core/sorter';
|
|
import ThResizable from '../common/th_resizable.vue';
|
|
|
|
export default {
|
|
name: 'TablePresenter',
|
|
components: {
|
|
GlIcon,
|
|
ThResizable,
|
|
},
|
|
inject: ['presenter'],
|
|
props: {
|
|
data: {
|
|
required: true,
|
|
type: Object,
|
|
validator: ({ nodes }) => Array.isArray(nodes),
|
|
},
|
|
config: {
|
|
required: true,
|
|
type: Object,
|
|
validator: ({ fields }) => Array.isArray(fields) && fields.length > 0,
|
|
},
|
|
},
|
|
data() {
|
|
const items = this.data.nodes.slice();
|
|
|
|
return {
|
|
items,
|
|
fields: this.config.fields,
|
|
sorter: new Sorter(items),
|
|
|
|
table: null,
|
|
};
|
|
},
|
|
async mounted() {
|
|
await this.$nextTick();
|
|
|
|
this.table = this.$refs.table;
|
|
},
|
|
};
|
|
</script>
|
|
<template>
|
|
<div class="!gl-my-4">
|
|
<table ref="table" class="!gl-mb-2 !gl-mt-0 gl-overflow-y-hidden">
|
|
<thead>
|
|
<tr v-if="table">
|
|
<th-resizable v-for="(field, fieldIndex) in fields" :key="field.key" :table="table">
|
|
<div
|
|
:data-testid="`column-${fieldIndex}`"
|
|
class="gl-cursor-pointer"
|
|
@click="sorter.sortBy(field.key)"
|
|
>
|
|
{{ field.label }}
|
|
<gl-icon
|
|
v-if="sorter.options.fieldName === field.key"
|
|
:name="sorter.options.ascending ? 'arrow-up' : 'arrow-down'"
|
|
/>
|
|
</div>
|
|
</th-resizable>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr
|
|
v-for="(item, itemIndex) in items"
|
|
:key="item.id"
|
|
:data-testid="`table-row-${itemIndex}`"
|
|
>
|
|
<td v-for="field in fields" :key="field.key">
|
|
<component :is="presenter.forField(item, field.key)" />
|
|
</td>
|
|
</tr>
|
|
<tr v-if="!items.length">
|
|
<td :colspan="fields.length" class="gl-text-center">
|
|
<em>{{ __('No data found for this query') }}</em>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<small>{{ __('Generated by GLQL') }}</small>
|
|
</div>
|
|
</template>
|