From c9f786ae31d8147bf117844a6816647cdc697d9a Mon Sep 17 00:00:00 2001 From: Seungwoo321 Date: Mon, 14 Apr 2025 10:46:28 +0900 Subject: [PATCH 1/3] feat: add table component --- .../pivottable-ui/VPivottableUi.vue | 2 +- src/components/pivottable/VPivottable.vue | 6 +- src/components/pivottable/VPivottableBody.vue | 54 +++++-- .../pivottable/VPivottableBodyRows.vue | 111 ++++++++++---- .../VPivottableBodyRowsTotalRow.vue | 90 +++++++++++ .../pivottable/VPivottableHeader.vue | 59 +++++--- .../pivottable/VPivottableHeaderColumns.vue | 37 +++++ .../pivottable/VPivottableHeaderRows.vue | 55 +++---- .../pivottable/VPivottableHeaderRowsTotal.vue | 25 +++ src/composables/index.js | 2 +- src/composables/pivotData.js | 27 ---- src/composables/usePivotData.js | 143 +++++++++++++++++- src/helper/defaultProps.js | 4 +- src/helper/index.js | 2 +- src/helper/redColorScaleGenerator.js | 8 + src/helper/spanSize.js | 39 ----- 16 files changed, 487 insertions(+), 177 deletions(-) create mode 100644 src/components/pivottable/VPivottableHeaderColumns.vue delete mode 100644 src/composables/pivotData.js create mode 100644 src/helper/redColorScaleGenerator.js delete mode 100644 src/helper/spanSize.js diff --git a/src/components/pivottable-ui/VPivottableUi.vue b/src/components/pivottable-ui/VPivottableUi.vue index 3a8ec75..0df8568 100644 --- a/src/components/pivottable-ui/VPivottableUi.vue +++ b/src/components/pivottable-ui/VPivottableUi.vue @@ -105,7 +105,7 @@ import VDragAndDropCell from './VDragAndDropCell.vue' import { VPivottable } from '@/' import { computed, ref, toRefs, watch } from 'vue' import { usePropsState, usePivotDataProcessing } from '@/composables' -import TableRenderer from '../pivottable/renderer/index' +import TableRenderer from '../pivottable/renderer' const props = defineProps({ ...defaultProps, diff --git a/src/components/pivottable/VPivottable.vue b/src/components/pivottable/VPivottable.vue index 8893faf..f6e1357 100644 --- a/src/components/pivottable/VPivottable.vue +++ b/src/components/pivottable/VPivottable.vue @@ -2,7 +2,7 @@ + /> - - diff --git a/src/components/pivottable/VPivottableBody.vue b/src/components/pivottable/VPivottableBody.vue index 17aade4..91a99b3 100644 --- a/src/components/pivottable/VPivottableBody.vue +++ b/src/components/pivottable/VPivottableBody.vue @@ -1,29 +1,57 @@ +defineProps({ + rowTotal: { + type: Boolean, + default: true + }, + colTotal: { + type: Boolean, + default: true + }, + localeStrings: { + type: Object, + default: () => ({ + totals: 'Totals' + }) + }, + tableOptions: { + type: Object, + default: () => ({ + clickCallback: null + }) + } +}) - + diff --git a/src/components/pivottable/VPivottableBodyRows.vue b/src/components/pivottable/VPivottableBodyRows.vue index 56966f7..300dc00 100644 --- a/src/components/pivottable/VPivottableBodyRows.vue +++ b/src/components/pivottable/VPivottableBodyRows.vue @@ -1,49 +1,96 @@ +const handleCellClick = (value, rowValues, colValues) => { + if (props.tableOptions?.clickCallback) { + const filters = {} - + // Add row filters + rowAttrs.value.forEach((attr, i) => { + if (rowValues[i] !== undefined && rowValues[i] !== null) { + filters[attr] = rowValues[i] + } + }) + + props.tableOptions.clickCallback(event, value, filters, pivotData.value) + } +} + diff --git a/src/components/pivottable/VPivottableBodyRowsTotalRow.vue b/src/components/pivottable/VPivottableBodyRowsTotalRow.vue index e69de29..db599ea 100644 --- a/src/components/pivottable/VPivottableBodyRowsTotalRow.vue +++ b/src/components/pivottable/VPivottableBodyRowsTotalRow.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/components/pivottable/VPivottableHeader.vue b/src/components/pivottable/VPivottableHeader.vue index ad662dd..2b236de 100644 --- a/src/components/pivottable/VPivottableHeader.vue +++ b/src/components/pivottable/VPivottableHeader.vue @@ -1,27 +1,52 @@ +defineProps({ + rowTotal: { + type: Boolean, + default: true + }, + localeStrings: { + type: Object, + default: () => ({ + totals: 'Totals' + }) + } +}) - + diff --git a/src/components/pivottable/VPivottableHeaderColumns.vue b/src/components/pivottable/VPivottableHeaderColumns.vue new file mode 100644 index 0000000..beaf709 --- /dev/null +++ b/src/components/pivottable/VPivottableHeaderColumns.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/pivottable/VPivottableHeaderRows.vue b/src/components/pivottable/VPivottableHeaderRows.vue index caae377..3a75650 100644 --- a/src/components/pivottable/VPivottableHeaderRows.vue +++ b/src/components/pivottable/VPivottableHeaderRows.vue @@ -1,47 +1,38 @@ - - diff --git a/src/components/pivottable/VPivottableHeaderRowsTotal.vue b/src/components/pivottable/VPivottableHeaderRowsTotal.vue index e69de29..cf11427 100644 --- a/src/components/pivottable/VPivottableHeaderRowsTotal.vue +++ b/src/components/pivottable/VPivottableHeaderRowsTotal.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/composables/index.js b/src/composables/index.js index a1b8a5f..6d1167a 100644 --- a/src/composables/index.js +++ b/src/composables/index.js @@ -1,3 +1,3 @@ -export { usePivotData, providePivotData } from './pivotData' +export { usePivotData, providePivotData } from './usePivotData' export { usePropsState } from './usePropsState' export { usePivotDataProcessing } from './usePivotDataProcessing' diff --git a/src/composables/pivotData.js b/src/composables/pivotData.js deleted file mode 100644 index c6ef40f..0000000 --- a/src/composables/pivotData.js +++ /dev/null @@ -1,27 +0,0 @@ -import { inject, provide } from 'vue' -import { PivotData } from '../helper' - -const pivotDataKey = Symbol('pivotData') - -export function providePivotData (props) { - const pivotData = new PivotData(props) - const colAttrs = pivotData.props.cols - const rowAttrs = pivotData.props.rows - const rowKeys = pivotData.getRowKeys() - const colKeys = pivotData.getColKeys() - const grandTotalAggregator = pivotData.getAggregator([], []) - const getAggregator = pivotData.getAggregator - provide(pivotDataKey, { - colAttrs, - rowAttrs, - rowKeys, - colKeys, - grandTotalAggregator, - getAggregator, - pivotData - }) -} - -export function usePivotData () { - return inject(pivotDataKey) -} diff --git a/src/composables/usePivotData.js b/src/composables/usePivotData.js index b8f8655..7e6d8d2 100644 --- a/src/composables/usePivotData.js +++ b/src/composables/usePivotData.js @@ -1,7 +1,7 @@ -import { inject, provide, ref, computed } from 'vue' -import { PivotData } from '@/helper/utilities.js' +import { ref, provide, inject, computed } from 'vue' +import { PivotData } from '@/helper' -const pivotDataKey = Symbol('pivotData') +const PIVOT_DATA_KEY = Symbol('pivotData') export function providePivotData (props) { const error = ref(null) @@ -21,10 +21,128 @@ export function providePivotData (props) { const colAttrs = computed(() => pivotData.value?.props.cols || []) const rowAttrs = computed(() => pivotData.value?.props.rows || []) - const getAggregator = (rowKey, colKey) => pivotData.value?.getAggregator(rowKey, colKey) - const grandTotalAggregator = () => pivotData.value.getAggregator([], []) + const getAggregator = (rowKey, colKey) => pivotData.value?.getAggregator(rowKey, colKey) || { + value: () => null, + format: () => '' + } - provide(pivotDataKey, { + const grandTotalAggregator = computed(() => { + return pivotData.value + ? pivotData.value.getAggregator([], []) + : { + value: () => null, + format: () => '' + } + }) + + const allValues = computed(() => { + + }) + + // Colors for heatmap + const valueCellColors = (rowKey, colKey, value) => { + if (props.heatmapMode === 'full') { + + } else if (props.heatmapMode === 'row') { + + } else if (props.heatmapMode === 'col') { + + } + return {} + } + + const rowTotalColors = (value) => { + if (!props.heatmapMode) return {} + // Implementation for row total coloring + return {} + } + + const colTotalColors = (value) => { + if (!props.heatmapMode) return {} + // Implementation for column total coloring + return {} + } + + /** + * + let valueCellColors = () => { } + // eslint-disable-next-line no-unused-vars + let rowTotalColors = () => { } + // eslint-disable-next-line no-unused-vars + let colTotalColors = () => { } + if (opts.heatmapMode) { + const colorScaleGenerator = this.tableColorScaleGenerator + const rowTotalValues = colKeys.map(x => + pivotData.getAggregator([], x).value() + ) + rowTotalColors = colorScaleGenerator(rowTotalValues) + const colTotalValues = rowKeys.map(x => + pivotData.getAggregator(x, []).value() + ) + colTotalColors = colorScaleGenerator(colTotalValues) + + if (opts.heatmapMode === 'full') { + const allValues = [] + rowKeys.map(r => + colKeys.map(c => + allValues.push(pivotData.getAggregator(r, c).value()) + ) + ) + const colorScale = colorScaleGenerator(allValues) + valueCellColors = (r, c, v) => colorScale(v) + } else if (opts.heatmapMode === 'row') { + const rowColorScales = {} + rowKeys.map(r => { + const rowValues = colKeys.map(x => + pivotData.getAggregator(r, x).value() + ) + rowColorScales[r] = colorScaleGenerator(rowValues) + }) + valueCellColors = (r, c, v) => rowColorScales[r](v) + } else if (opts.heatmapMode === 'col') { + const colColorScales = {} + colKeys.map(c => { + const colValues = rowKeys.map(x => + pivotData.getAggregator(x, c).value() + ) + colColorScales[c] = colorScaleGenerator(colValues) + }) + valueCellColors = (r, c, v) => colColorScales[c](v) + } + } + */ + // Helper function for determining spans in the table + const spanSize = (arr, i, j) => { + let x + if (i !== 0) { + let noDraw = true + for (x = 0; x <= j; x++) { + if (arr[i - 1][x] !== arr[i][x]) { + noDraw = false + } + } + if (noDraw) { + return -1 + } + } + + let len = 0 + while (i + len < arr.length) { + let stop = false + for (x = 0; x <= j; x++) { + if (arr[i][x] !== arr[i + len][x]) { + stop = true + } + } + if (stop) { + break + } + len++ + } + return len + } + + const pivotDataContext = { pivotData, rowKeys, colKeys, @@ -32,10 +150,19 @@ export function providePivotData (props) { rowAttrs, getAggregator, grandTotalAggregator, + spanSize, + valueCellColors, + rowTotalColors, + colTotalColors, error - }) + } + + provide(PIVOT_DATA_KEY, pivotDataContext) + return pivotDataContext } export function usePivotData () { - return inject(pivotDataKey) + return inject(PIVOT_DATA_KEY) } + +https://github.com/vue-pivottable/vue3-pivottable/pull/12#discussion_r2041281526 \ No newline at end of file diff --git a/src/helper/defaultProps.js b/src/helper/defaultProps.js index 9f3f855..e5786c7 100644 --- a/src/helper/defaultProps.js +++ b/src/helper/defaultProps.js @@ -1,4 +1,5 @@ import { aggregators, locales } from './utilities' +import { redColorScaleGenerator } from './redColorScaleGenerator' export default { data: { type: [Array, Object, Function], @@ -14,7 +15,8 @@ export default { }, heatmapMode: String, tableColorScaleGenerator: { - type: Function + type: Function, + default: redColorScaleGenerator }, tableOptions: { type: Object, diff --git a/src/helper/index.js b/src/helper/index.js index 82f985d..295a076 100644 --- a/src/helper/index.js +++ b/src/helper/index.js @@ -1,3 +1,3 @@ export { default as defaultProps } from './defaultProps.js' -export { spanSize } from './spanSize.js' +export { redColorScaleGenerator } from './redColorScaleGenerator.js' export * from './utilities.js' diff --git a/src/helper/redColorScaleGenerator.js b/src/helper/redColorScaleGenerator.js new file mode 100644 index 0000000..4f62db2 --- /dev/null +++ b/src/helper/redColorScaleGenerator.js @@ -0,0 +1,8 @@ +export function redColorScaleGenerator (values) { + const min = Math.min.apply(Math, values) + const max = Math.max.apply(Math, values) + return x => { + const nonRed = 255 - Math.round(255 * (x - min) / (max - min)) + return { backgroundColor: `rgb(255,${nonRed},${nonRed})` } + } +} diff --git a/src/helper/spanSize.js b/src/helper/spanSize.js deleted file mode 100644 index 241db60..0000000 --- a/src/helper/spanSize.js +++ /dev/null @@ -1,39 +0,0 @@ -export const spanSize = (arr, i, j) => { - // helper function for setting row/col-span in pivotTableRenderer - let x - if (i !== 0) { - let asc, end - let noDraw = true - for ( - x = 0, end = j, asc = end >= 0; - asc ? x <= end : x >= end; - asc ? x++ : x-- - ) { - if (arr[i - 1][x] !== arr[i][x]) { - noDraw = false - } - } - if (noDraw) { - return -1 - } - } - let len = 0 - while (i + len < arr.length) { - let asc1, end1 - let stop = false - for ( - x = 0, end1 = j, asc1 = end1 >= 0; - asc1 ? x <= end1 : x >= end1; - asc1 ? x++ : x-- - ) { - if (arr[i][x] !== arr[i + len][x]) { - stop = true - } - } - if (stop) { - break - } - len++ - } - return len -} From 0124353a54d9973cfb8cbac76bae0a7ed55b4dcb Mon Sep 17 00:00:00 2001 From: Seungwoo321 Date: Tue, 15 Apr 2025 09:03:07 +0900 Subject: [PATCH 2/3] feat: close #14 and #24 --- .../pivottable-ui/VPivottableUi.vue | 7 ++ .../pivottable/VPivottableBodyRows.vue | 17 ++-- src/composables/usePivotData.js | 95 +++++++------------ 3 files changed, 49 insertions(+), 70 deletions(-) diff --git a/src/components/pivottable-ui/VPivottableUi.vue b/src/components/pivottable-ui/VPivottableUi.vue index e5c1478..1a9c952 100644 --- a/src/components/pivottable-ui/VPivottableUi.vue +++ b/src/components/pivottable-ui/VPivottableUi.vue @@ -214,6 +214,13 @@ const onUpdateValueFilter = ({ attribute, valueFilter }) => { } const onUpdateRendererName = (rendererName) => { updateState('rendererName', rendererName) + if (rendererName === 'Table Heatmap') { + updateState('heatmapMode', 'full') + } else if (rendererName === 'Table Row Heatmap') { + updateState('heatmapMode', 'row') + } else if (rendererName === 'Table Col Heatmap') { + updateState('heatmapMode', 'col') + } } const onUpdateAggregatorName = (aggregatorName) => { updateState('aggregatorName', aggregatorName) diff --git a/src/components/pivottable/VPivottableBodyRows.vue b/src/components/pivottable/VPivottableBodyRows.vue index 300dc00..b529357 100644 --- a/src/components/pivottable/VPivottableBodyRows.vue +++ b/src/components/pivottable/VPivottableBodyRows.vue @@ -53,14 +53,15 @@ const props = defineProps({ } }) -const { pivotData, spanSize, valueCellColors, colTotalColors, rowAttrs, colAttrs } = usePivotData() - -const getAggregator = (rowKey, colKey) => { - return pivotData.value?.getAggregator(rowKey, colKey) || { - value: () => null, - format: () => '' - } -} +const { + pivotData, + spanSize, + valueCellColors, + colTotalColors, + rowAttrs, + colAttrs, + getAggregator +} = usePivotData() const getValueCellStyle = (rowKey, colKey) => { const value = getAggregator(rowKey, colKey).value() diff --git a/src/composables/usePivotData.js b/src/composables/usePivotData.js index 7e6d8d2..bc24dde 100644 --- a/src/composables/usePivotData.js +++ b/src/composables/usePivotData.js @@ -20,7 +20,7 @@ export function providePivotData (props) { const colKeys = computed(() => pivotData.value?.getColKeys() || []) const colAttrs = computed(() => pivotData.value?.props.cols || []) const rowAttrs = computed(() => pivotData.value?.props.rows || []) - + const colorScaleGenerator = props.tableColorScaleGenerator const getAggregator = (rowKey, colKey) => pivotData.value?.getAggregator(rowKey, colKey) || { value: () => null, format: () => '' @@ -28,7 +28,7 @@ export function providePivotData (props) { const grandTotalAggregator = computed(() => { return pivotData.value - ? pivotData.value.getAggregator([], []) + ? getAggregator([], []) : { value: () => null, format: () => '' @@ -36,82 +36,55 @@ export function providePivotData (props) { }) const allValues = computed(() => { - + const values = [] + rowKeys.value.forEach(r => + colKeys.value.forEach(c => + values.push(getAggregator(r, c).value()) + ) + ) + return values }) // Colors for heatmap const valueCellColors = (rowKey, colKey, value) => { if (props.heatmapMode === 'full') { - + const colorScale = colorScaleGenerator(allValues.value) + return colorScale(value) } else if (props.heatmapMode === 'row') { - + const rowColorScales = rowKeys.value.reduce((scales, r) => { + scales[r] = colorScaleGenerator(colKeys.value.map(x => + getAggregator(r, x).value() + )) + return scales + }, {}) + return rowColorScales[rowKey](value) } else if (props.heatmapMode === 'col') { - + const colColorScales = colKeys.value.reduce((scales, c) => { + scales[c] = colorScaleGenerator(rowKeys.value.map(x => + getAggregator(x, c).value() + )) + return scales + }, {}) + return colColorScales[colKey](value) } return {} } - const rowTotalColors = (value) => { if (!props.heatmapMode) return {} - // Implementation for row total coloring - return {} + const rowTotalValues = colKeys.value.map(x => + getAggregator([], x).value() + ) + return colorScaleGenerator(rowTotalValues)(value) } const colTotalColors = (value) => { if (!props.heatmapMode) return {} - // Implementation for column total coloring - return {} + const colTotalValues = rowKeys.value.map(x => + getAggregator(x, []).value() + ) + return colorScaleGenerator(colTotalValues)(value) } - /** - * - let valueCellColors = () => { } - // eslint-disable-next-line no-unused-vars - let rowTotalColors = () => { } - // eslint-disable-next-line no-unused-vars - let colTotalColors = () => { } - if (opts.heatmapMode) { - const colorScaleGenerator = this.tableColorScaleGenerator - const rowTotalValues = colKeys.map(x => - pivotData.getAggregator([], x).value() - ) - rowTotalColors = colorScaleGenerator(rowTotalValues) - const colTotalValues = rowKeys.map(x => - pivotData.getAggregator(x, []).value() - ) - colTotalColors = colorScaleGenerator(colTotalValues) - - if (opts.heatmapMode === 'full') { - const allValues = [] - rowKeys.map(r => - colKeys.map(c => - allValues.push(pivotData.getAggregator(r, c).value()) - ) - ) - const colorScale = colorScaleGenerator(allValues) - valueCellColors = (r, c, v) => colorScale(v) - } else if (opts.heatmapMode === 'row') { - const rowColorScales = {} - rowKeys.map(r => { - const rowValues = colKeys.map(x => - pivotData.getAggregator(r, x).value() - ) - rowColorScales[r] = colorScaleGenerator(rowValues) - }) - valueCellColors = (r, c, v) => rowColorScales[r](v) - } else if (opts.heatmapMode === 'col') { - const colColorScales = {} - colKeys.map(c => { - const colValues = rowKeys.map(x => - pivotData.getAggregator(x, c).value() - ) - colColorScales[c] = colorScaleGenerator(colValues) - }) - valueCellColors = (r, c, v) => colColorScales[c](v) - } - } - */ - // Helper function for determining spans in the table const spanSize = (arr, i, j) => { let x if (i !== 0) { @@ -164,5 +137,3 @@ export function providePivotData (props) { export function usePivotData () { return inject(PIVOT_DATA_KEY) } - -https://github.com/vue-pivottable/vue3-pivottable/pull/12#discussion_r2041281526 \ No newline at end of file From 8776809a1cc0a433387063652e5870e9a095d50d Mon Sep 17 00:00:00 2001 From: Seungwoo321 Date: Tue, 15 Apr 2025 09:33:01 +0900 Subject: [PATCH 3/3] chore: rename usePivotData to useProvidePivotData --- src/components/pivottable/VPivottableBody.vue | 4 ++-- src/components/pivottable/VPivottableBodyRows.vue | 4 ++-- src/components/pivottable/VPivottableBodyRowsTotalRow.vue | 4 ++-- src/components/pivottable/VPivottableHeader.vue | 4 ++-- src/components/pivottable/VPivottableHeaderColumns.vue | 4 ++-- src/components/pivottable/renderer/TableRenderer.vue | 2 +- src/composables/index.js | 2 +- src/composables/{usePivotData.js => useProvidePivotData.js} | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) rename src/composables/{usePivotData.js => useProvidePivotData.js} (98%) diff --git a/src/components/pivottable/VPivottableBody.vue b/src/components/pivottable/VPivottableBody.vue index 91a99b3..e5d93bb 100644 --- a/src/components/pivottable/VPivottableBody.vue +++ b/src/components/pivottable/VPivottableBody.vue @@ -19,7 +19,7 @@ diff --git a/src/components/pivottable/VPivottableBodyRows.vue b/src/components/pivottable/VPivottableBodyRows.vue index b529357..2304174 100644 --- a/src/components/pivottable/VPivottableBodyRows.vue +++ b/src/components/pivottable/VPivottableBodyRows.vue @@ -32,7 +32,7 @@ diff --git a/src/components/pivottable/VPivottableHeaderColumns.vue b/src/components/pivottable/VPivottableHeaderColumns.vue index beaf709..124aa1a 100644 --- a/src/components/pivottable/VPivottableHeaderColumns.vue +++ b/src/components/pivottable/VPivottableHeaderColumns.vue @@ -12,7 +12,7 @@ diff --git a/src/components/pivottable/renderer/TableRenderer.vue b/src/components/pivottable/renderer/TableRenderer.vue index 7e2c38d..26a6629 100644 --- a/src/components/pivottable/renderer/TableRenderer.vue +++ b/src/components/pivottable/renderer/TableRenderer.vue @@ -13,7 +13,7 @@