diff --git a/ui/src/components/table/QTable.js b/ui/src/components/table/QTable.js index c7e3601ca4e..8f40d441745 100644 --- a/ui/src/components/table/QTable.js +++ b/ui/src/components/table/QTable.js @@ -1,4 +1,4 @@ -import { h, ref, computed, watch, getCurrentInstance } from 'vue' +import { h, ref, computed, watch, getCurrentInstance, onMounted, reactive } from 'vue' import QTh from './QTh.js' @@ -43,10 +43,12 @@ export default createComponent({ type: [ String, Function ], default: 'id' }, - + resizableCols: Boolean, columns: Array, loading: Boolean, + + iconFirstPage: String, iconPrevPage: String, iconNextPage: String, @@ -127,6 +129,48 @@ export default createComponent({ const vm = getCurrentInstance() const { proxy: { $q } } = vm + const colWidths = reactive({}) + const resizingCol = ref(null) + const startX = ref(0) + + onMounted(() => { + props.columns.forEach(col => { + colWidths[col.name] = 150 + }) + }) + + function resetColumnWidth (colName) { + colWidths[colName] = 150 // Reset to default width or any desired full size + } + + function startResizing (colName, evt) { + resizingCol.value = colName + startX.value = evt.pageX + document.addEventListener('mousemove', handleResize) + document.addEventListener('mouseup', stopResizing) + } + + function handleResize (evt) { + if (!resizingCol.value) return + const diff = evt.pageX - startX.value + colWidths[resizingCol.value] += diff + startX.value = evt.pageX + } + + function stopResizing () { + document.removeEventListener('mousemove', handleResize) + document.removeEventListener('mouseup', stopResizing) + resizingCol.value = null + } + + return { + colWidths, + resetColumnWidth, + startResizing, + handleResize, + stopResizing, + }; + const isDark = useDark(props, $q) const { inFullscreen, toggleFullscreen } = useFullscreen() @@ -621,62 +665,65 @@ export default createComponent({ return h('thead', child) } - function getTHeadTR () { - const - header = slots.header, - headerCell = slots[ 'header-cell' ] - - if (header !== void 0) { - return header( - getHeaderScope({ header: true }) - ).slice() - } - - const child = computedCols.value.map(col => { - const - headerCellCol = slots[ `header-cell-${ col.name }` ], - slot = headerCellCol !== void 0 ? headerCellCol : headerCell, - props = getHeaderScope({ col }) - - return slot !== void 0 - ? slot(props) - : h(QTh, { - key: col.name, - props - }, () => col.label) - }) - - if (singleSelection.value === true && props.grid !== true) { - child.unshift( - h('th', { class: 'q-table--col-auto-width' }, ' ') - ) - } - else if (multipleSelection.value === true) { - const slot = slots[ 'header-selection' ] - const content = slot !== void 0 - ? slot(getHeaderScope({})) - : [ - h(QCheckbox, { - color: props.color, - modelValue: headerSelectedValue.value, - dark: isDark.value, - dense: props.dense, - 'onUpdate:modelValue': onMultipleSelectionSet - }) - ] - - child.unshift( - h('th', { class: 'q-table--col-auto-width' }, content) - ) - } - - return [ - h('tr', { - class: props.tableHeaderClass, - style: props.tableHeaderStyle - }, child) - ] - } + function getTHeadTR() { + const header = slots.header; + const headerCell = slots['header-cell']; + + if (header !== void 0) { + return header( + getHeaderScope({ header: true }) + ).slice(); + } + + const child = computedCols.value.map(col => { + const headerCellCol = slots[`header-cell-${col.name}`]; + const slot = headerCellCol !== void 0 ? headerCellCol : headerCell; + const props = getHeaderScope({ col }); + + return slot !== void 0 + ? slot(props) + : h(QTh, { + key: col.name, + props + }, () => [ + col.label, + col.resizable ? h('div', { + class: 'q-table__resize-handle', + onMousedown: evt => startResizing(col.name, evt) + }) : null + ]); + }); + + if (singleSelection.value === true && props.grid !== true) { + child.unshift( + h('th', { class: 'q-table--col-auto-width' }, ' ') + ); + } else if (multipleSelection.value === true) { + const slot = slots['header-selection']; + const content = slot !== void 0 + ? slot(getHeaderScope({})) + : [ + h(QCheckbox, { + color: props.color, + modelValue: headerSelectedValue.value, + dark: isDark.value, + dense: props.dense, + 'onUpdate:modelValue': onMultipleSelectionSet + }) + ]; + + child.unshift( + h('th', { class: 'q-table--col-auto-width' }, content) + ); + } + + return [ + h('tr', { + class: props.tableHeaderClass, + style: props.tableHeaderStyle + }, child) + ]; + } function getHeaderScope (data) { Object.assign(data, { diff --git a/ui/src/components/table/QTable.json b/ui/src/components/table/QTable.json index 6e3e6029c4f..e559afcb8e1 100644 --- a/ui/src/components/table/QTable.json +++ b/ui/src/components/table/QTable.json @@ -13,7 +13,11 @@ "examples": [ "# :rows=\"myData\"" ], "category": "general" }, - + "resizable-cols": { + "type": "Boolean", + "desc": "Enabling allows for easy configuration of user resizable column widths by adding a customizable drag handle between each column specified in the 'columns' prop. By default the drag handle is a small vertical line between each column. The drag handle can be customized by using the 'column-resize-handle' slot. Double clicking the grabber sets the columns back to their default width", + "category": "behavior" + }, "row-key": { "type": [ "String", "Function" ], "desc": "Property of each row that defines the unique key of each row (the result must be a primitive, not Object, Array, etc); The value of property must be string or a function taking a row and returning the desired (nested) key in the row; If supplying a function then for best performance, reference it from your scope and do not define it inline", diff --git a/ui/src/components/table/QTable.sass b/ui/src/components/table/QTable.sass index e82234d61aa..eed9f99353d 100644 --- a/ui/src/components/table/QTable.sass +++ b/ui/src/components/table/QTable.sass @@ -291,3 +291,16 @@ body.desktop .q-table > tbody > tr:not(.q-tr--no-hover):hover > td:not(.q-td--no &.q-table--vertical-separator, &.q-table--cell-separator .q-table__top border-color: $table-dark-border-color + + + +.q-table th .q-table__resize-handle { + cursor: col-resize + display: inline-block + width: 6px + height: 100% + background-color: #ccc + position: absolute + right: 0 + top: 0 +}