Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/src/examples/QTable/ColumnResize.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<div class="q-pa-md">
<q-table
title="Resizable Columns"
:rows="rows"
:columns="columns"
row-key="name"
resizable-columns
@column-resize="onColumnResize"
/>

<div class="q-mt-md">
Column widths: {{ columnWidths }}
</div>
</div>
</template>

<script>
import { ref } from 'vue'

const columns = [
{
name: 'name',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: row => row.name,
sortable: true
},
{ name: 'calories', align: 'center', label: 'Calories', field: 'calories', sortable: true },
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' }
]

const rows = [
{ name: 'Frozen Yogurt', calories: 159, fat: 6.0, carbs: 24, protein: 4.0, sodium: 87 },
{ name: 'Ice cream sandwich', calories: 237, fat: 9.0, carbs: 37, protein: 4.3, sodium: 129 },
{ name: 'Eclair', calories: 262, fat: 16.0, carbs: 23, protein: 6.0, sodium: 337 },
{ name: 'Cupcake', calories: 305, fat: 3.7, carbs: 67, protein: 4.3, sodium: 413 },
{ name: 'Gingerbread', calories: 356, fat: 16.0, carbs: 49, protein: 3.9, sodium: 327 }
]

export default {
setup () {
const columnWidths = ref({})

function onColumnResize (evt) {
columnWidths.value = evt.widths
}

return {
columns,
rows,
columnWidths,
onColumnResize
}
}
}
</script>
7 changes: 7 additions & 0 deletions docs/src/pages/vue-components/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ QTable is a component that allows you to display data in a tabular manner. It's
* Column picker (through QTableColumns component described in one of the sections)
* Custom top and/or bottom Table controls
* Responsive design
* Resize column width manually

::: tip
If you don't need pagination, sorting, filtering, and all other features of QTable, then you may want to check out [QMarkupTable](/vue-components/markup-table) component instead.
Expand Down Expand Up @@ -165,6 +166,12 @@ For all the styling component properties, please check the API card at the top o

<DocExample title="No header/footer" file="NoHeaderFooter" />

## Column resizing

Enable column resizing by using the `resizable-columns` prop. Double-click on the separator to reset the column to its automatic width.

<DocExample title="Resizable columns" file="ColumnResize" />

## Virtual scrolling

Notice that when enabling virtual scroll you will need to specify the `table-style` (with a max-height) prop. In the example below, we are also forcing QTable to display all rows at once (note the use of `pagination` and `rows-per-page-options` props).
Expand Down
141 changes: 141 additions & 0 deletions ui/playground/src/pages/components/data-table-column-resize.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<template>
<div class="q-pa-md">
<h5 class="q-mb-md">Column Resize Demo</h5>
<p class="q-mb-md text-caption">
Drag the edge of column headers to resize. Double-click to reset to auto width.
</p>

<div class="q-gutter-sm q-mb-md">
<q-toggle v-model="dark" label="Dark mode" />
<q-toggle v-model="dense" label="Dense" />
<q-btn
color="primary"
label="Reset all widths"
size="sm"
@click="resetWidths"
/>
<q-btn
color="secondary"
label="Set initial widths"
size="sm"
@click="setInitialWidths"
/>
</div>

<q-table
title="Resizable Columns"
:rows="rows"
:columns="columns"
row-key="id"
resizable-columns
:column-widths="columnWidths"
:dark="dark"
:dense="dense"
bordered
flat
@column-resize="onColumnResize"
/>

<q-card flat bordered class="q-mt-md" :dark="dark">
<q-card-section>
<div class="text-subtitle2">Current Column Widths:</div>
<pre class="q-mt-sm">{{ JSON.stringify(columnWidths, null, 2) }}</pre>
</q-card-section>
</q-card>

<q-card flat bordered class="q-mt-md" :dark="dark">
<q-card-section>
<div class="row items-center q-mb-sm">
<div class="text-subtitle2">Resize Events Log:</div>
<q-space />
<q-btn
flat
dense
size="sm"
label="Clear"
@click="resizeEvents = []"
/>
</div>
<div v-if="resizeEvents.length === 0" class="text-grey">
No resize events yet. Drag a column edge to resize.
</div>
<div
v-for="(event, index) in resizeEvents"
:key="index"
class="text-caption q-mb-xs"
>
<q-badge :color="event.width === 'auto' ? 'orange' : 'primary'" class="q-mr-sm">
{{ event.width === 'auto' ? 'reset' : 'resize' }}
</q-badge>
Column "{{ event.col.label }}" {{ event.width === 'auto' ? 'reset to auto' : `set to ${ event.width }px` }}
</div>
</q-card-section>
</q-card>
</div>
</template>

<script>
import { ref } from 'vue'

export default {
setup () {
const dark = ref(false)
const dense = ref(false)
const columnWidths = ref({})
const resizeEvents = ref([])

const columns = [
{ name: 'id', label: 'ID', field: 'id', align: 'left', sortable: true },
{ name: 'name', label: 'Name', field: 'name', align: 'left', sortable: true },
{ name: 'email', label: 'Email', field: 'email', align: 'left' },
{ name: 'amount', label: 'Amount', field: 'amount', align: 'right', format: val => `$${ val.toFixed(2) }` },
{ name: 'status', label: 'Status', field: 'status', align: 'center' },
{ name: 'date', label: 'Date', field: 'date', align: 'left' }
]

const rows = [
{ id: 1, name: 'John Doe', email: '[email protected]', amount: 1234.56, status: 'Active', date: '2024-01-15' },
{ id: 2, name: 'Jane Smith', email: '[email protected]', amount: 2345.67, status: 'Pending', date: '2024-01-16' },
{ id: 3, name: 'Bob Johnson', email: '[email protected]', amount: 345.89, status: 'Active', date: '2024-01-17' },
{ id: 4, name: 'Alice Williams', email: '[email protected]', amount: 4567.12, status: 'Inactive', date: '2024-01-18' },
{ id: 5, name: 'Charlie Brown', email: '[email protected]', amount: 678.90, status: 'Active', date: '2024-01-19' }
]

function onColumnResize (event) {
console.log('Column resize event:', event)
columnWidths.value = event.widths
resizeEvents.value.unshift(event)
if (resizeEvents.value.length > 10) {
resizeEvents.value.pop()
}
}

function resetWidths () {
columnWidths.value = {}
}

function setInitialWidths () {
columnWidths.value = {
id: 60,
name: 200,
email: 250,
amount: 120,
status: 100,
date: 120
}
}

return {
dark,
dense,
columns,
rows,
columnWidths,
resizeEvents,
onColumnResize,
resetWidths,
setInitialWidths
}
}
}
</script>
34 changes: 25 additions & 9 deletions ui/src/components/table/QTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useTablePaginationState, useTablePagination, useTablePaginationProps }
import { useTableRowSelection, useTableRowSelectionProps, useTableRowSelectionEmits } from './table-row-selection.js'
import { useTableRowExpand, useTableRowExpandProps, useTableRowExpandEmits } from './table-row-expand.js'
import { useTableColumnSelection, useTableColumnSelectionProps } from './table-column-selection.js'
import { useTableColumnResize, useTableColumnResizeProps, useTableColumnResizeEmits } from './table-column-resize.js'

import { injectProp, injectMultipleProps } from '../../utils/private.inject-obj-prop/inject-obj-prop.js'
import { createComponent } from '../../utils/private.create/create.js'
Expand Down Expand Up @@ -117,14 +118,16 @@ export default createComponent({
...useTablePaginationProps,
...useTableRowExpandProps,
...useTableRowSelectionProps,
...useTableSortProps
...useTableSortProps,
...useTableColumnResizeProps
},

emits: [
'request', 'virtualScroll',
...useFullscreenEmits,
...useTableRowExpandEmits,
...useTableRowSelectionEmits
...useTableRowSelectionEmits,
...useTableColumnResizeEmits
],

setup (props, { slots, emit }) {
Expand Down Expand Up @@ -246,6 +249,8 @@ export default createComponent({

const { colList, computedCols, computedColsMap, computedColspan } = useTableColumnSelection(props, computedPagination, hasSelectionMode)

const { columnWidths, resizing, colsWithWidths, colsMapWithWidths, startResize, onDoubleClick } = useTableColumnResize(props, computedCols, emit)

const { columnToSort, computedSortMethod, sort } = useTableSort(props, computedPagination, colList, setPagination)

const {
Expand Down Expand Up @@ -411,7 +416,8 @@ export default createComponent({

const
bodyCell = slots[ 'body-cell' ],
child = computedCols.value.map(col => {
cols = props.resizableColumns ? colsWithWidths.value : computedCols.value,
child = cols.map(col => {
const
bodyCellCol = slots[ `body-cell-${ col.name }` ],
slot = bodyCellCol !== void 0 ? bodyCellCol : bodyCell
Expand Down Expand Up @@ -660,17 +666,23 @@ export default createComponent({
).slice()
}

const child = computedCols.value.map(col => {
const cols = props.resizableColumns ? colsWithWidths.value : computedCols.value

const child = cols.map(col => {
const
headerCellCol = slots[ `header-cell-${ col.name }` ],
slot = headerCellCol !== void 0 ? headerCellCol : headerCell,
props = getHeaderScope({ col })
scopeProps = getHeaderScope({ col })

return slot !== void 0
? slot(props)
? slot(scopeProps)
: h(QTh, {
key: col.name,
props
props: scopeProps,
resizableColumns: props.resizableColumns,
resizing: resizing.value,
onStartResize: startResize,
onAutoResize: onDoubleClick
}, () => col.label)
})

Expand Down Expand Up @@ -700,17 +712,21 @@ export default createComponent({

return [
h('tr', {
key: props.resizableColumns ? JSON.stringify(columnWidths.value) : undefined,
class: props.tableHeaderClass,
style: props.tableHeaderStyle
}, child)
]
}

function getHeaderScope (data) {
const cols = props.resizableColumns ? colsWithWidths.value : computedCols.value
const colsMap = props.resizableColumns ? colsMapWithWidths.value : computedColsMap.value

Object.assign(data, {
cols: computedCols.value,
cols,
sort,
colsMap: computedColsMap.value,
colsMap,
color: props.color,
dark: isDark.value,
dense: props.dense
Expand Down
37 changes: 37 additions & 0 deletions ui/src/components/table/QTable.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,19 @@
"category": "behavior|content"
},

"resizable-columns": {
"type": "Boolean",
"desc": "Enable column resizing by dragging the column separator",
"category": "behavior"
},

"column-widths": {
"type": "Object",
"desc": "Object with column names as keys and their widths (in pixels) as values",
"examples": [ "{ name: 150, calories: 100 }" ],
"category": "model"
},

"title": {
"type": "String",
"desc": "Table title",
Expand Down Expand Up @@ -1942,6 +1955,30 @@
},

"events": {
"column-resize": {
"desc": "Emitted when a column is resized",
"params": {
"details": {
"type": "Object",
"desc": "Resize event details",
"definition": {
"col": {
"type": "Object",
"desc": "Column definition object"
},
"width": {
"type": [ "Number", "String" ],
"desc": "New width of the column in pixels, or 'auto'"
},
"widths": {
"type": "Object",
"desc": "Object with all current column widths"
}
}
}
}
},

"row-click": {
"desc": "Emitted when user clicks/taps on a row; Is not emitted when using body/row/item scoped slots",
"params": {
Expand Down
Loading