Skip to content

Commit

Permalink
feat(Table): add click event for the entire row (#353)
Browse files Browse the repository at this point in the history
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
  • Loading branch information
9uenther and benjamincanac authored Jul 17, 2023
1 parent 31d571a commit d292706
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 7 deletions.
54 changes: 54 additions & 0 deletions docs/components/content/examples/TableExampleClickable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script setup>
const people = [{
id: 1,
name: 'Lindsay Walton',
title: 'Front-end Developer',
email: 'lindsay.walton@example.com',
role: 'Member'
}, {
id: 2,
name: 'Courtney Henry',
title: 'Designer',
email: 'courtney.henry@example.com',
role: 'Admin'
}, {
id: 3,
name: 'Tom Cook',
title: 'Director of Product',
email: 'tom.cook@example.com',
role: 'Member'
}, {
id: 4,
name: 'Whitney Francis',
title: 'Copywriter',
email: 'whitney.francis@example.com',
role: 'Admin'
}, {
id: 5,
name: 'Leonard Krasner',
title: 'Senior Designer',
email: 'leonard.krasner@example.com',
role: 'Owner'
}, {
id: 6,
name: 'Floyd Miles',
title: 'Principal Designer',
email: 'floyd.miles@example.com',
role: 'Member'
}]
function select (row) {
const index = selected.value.findIndex((item) => item.id === row.id)
if (index === -1) {
selected.value.push(row)
} else {
selected.value.splice(index, 1)
}
}
const selected = ref([people[1]])
</script>

<template>
<UTable v-model="selected" :rows="people" @select="select" />
</template>
38 changes: 38 additions & 0 deletions docs/content/4.data/1.table.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,44 @@ const selected = ref([people[1]])
You can use the `by` prop to compare objects by a field instead of comparing object instances. We've replicated the behavior of Headless UI [Combobox](https://headlessui.com/vue/combobox#binding-objects-as-values).
::

### Clickable

Add a `select` listener on your Table to make the rows clickable. The function will receive the row as the first argument.

You can use this to navigate to a page, open a modal or even to select the row manually.

::component-example{class="grid"}
---
padding: false
overflowClass: 'overflow-x-auto'
---

#default
:table-example-clickable{class="flex-1"}

#code
```vue
<script setup>
const people = [...]
function select (row) {
const index = selected.value.findIndex((item) => item.id === row.id)
if (index === -1) {
selected.value.push(row)
} else {
selected.value.splice(index, 1)
}
}
const selected = ref([people[1]])
</script>
<template>
<UTable v-model="selected" :rows="people" @select="select" />
</template>
```
::

### Searchable

You can easily use the [Input](/forms/input) component to filter the rows.
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const table = {
tbody: 'divide-y divide-gray-200 dark:divide-gray-800',
tr: {
base: '',
selected: 'bg-gray-50 dark:bg-gray-800/50'
selected: 'bg-gray-50 dark:bg-gray-800/50',
active: 'hover:bg-gray-50 dark:hover:bg-gray-800/50 cursor-pointer'
},
th: {
base: 'text-left rtl:text-right',
Expand Down
23 changes: 17 additions & 6 deletions src/runtime/components/data/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
</tr>
</thead>
<tbody :class="ui.tbody">
<tr v-for="(row, index) in rows" :key="index" :class="[ui.tr.base, isSelected(row) && ui.tr.selected]">
<tr v-for="(row, index) in rows" :key="index" :class="[ui.tr.base, isSelected(row) && ui.tr.selected, $attrs.onSelect && ui.tr.active]" @click="() => onSelect(row)">
<td v-if="modelValue" class="ps-4">
<UCheckbox v-model="selected" :value="row" />
<UCheckbox v-model="selected" :value="row" @click.stop />
</td>

<td v-for="(column, subIndex) in columns" :key="subIndex" :class="[ui.td.base, ui.td.padding, ui.td.color, ui.td.font, ui.td.size]">
Expand Down Expand Up @@ -69,6 +69,7 @@ import { ref, computed, defineComponent, toRaw } from 'vue'
import type { PropType } from 'vue'
import { capitalize, orderBy } from 'lodash-es'
import { defu } from 'defu'
import { omit } from 'lodash-es'
import type { Button } from '../../types/button'
import { useAppConfig } from '#imports'
// TODO: Remove
Expand All @@ -92,7 +93,7 @@ export default defineComponent({
default: () => defaultComparator
},
rows: {
type: Array as PropType<{ [key: string]: any }[]>,
type: Array as PropType<{ [key: string]: any, click?: Function }[]>,
default: () => []
},
columns: {
Expand Down Expand Up @@ -137,13 +138,13 @@ export default defineComponent({
}
},
emits: ['update:modelValue'],
setup (props, { emit }) {
setup (props, { emit, attrs }) {
// TODO: Remove
const appConfig = useAppConfig()
const ui = computed<Partial<typeof appConfig.ui.table>>(() => defu({}, props.ui, appConfig.ui.table))
const columns = computed(() => props.columns ?? Object.keys(props.rows[0] ?? {}).map((key) => ({ key, label: capitalize(key), sortable: false })))
const columns = computed(() => props.columns ?? Object.keys(omit(props.rows[0] ?? {}, ['click'])).map((key) => ({ key, label: capitalize(key), sortable: false })))
const sort = ref(defu({}, props.sort, { column: null, direction: 'asc' }))
Expand Down Expand Up @@ -200,6 +201,15 @@ export default defineComponent({
}
}
function onSelect (row) {
if (!attrs.onSelect) {
return
}
// @ts-ignore
attrs.onSelect(row)
}
return {
// eslint-disable-next-line vue/no-dupe-keys
ui,
Expand All @@ -214,7 +224,8 @@ export default defineComponent({
// eslint-disable-next-line vue/no-dupe-keys
emptyState,
isSelected,
onSort
onSort,
onSelect
}
}
})
Expand Down

1 comment on commit d292706

@vercel
Copy link

@vercel vercel bot commented on d292706 Jul 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

ui – ./

ui-git-dev-nuxtlabs.vercel.app
ui-nuxtlabs.vercel.app
ui.nuxtlabs.com

Please sign in to comment.