-
{
- console.log('event', Object.keys(evt)[0])
+ // console.log('event', Object.keys(evt)[0])
emit('update:draggedAttribute', {
cellType: props.cellType,
- attributes: modelItems.value
+ attributes: Object.values(modelItems.value)
})
}
diff --git a/src/components/pivottable-ui/VPivottableUi.vue b/src/components/pivottable-ui/VPivottableUi.vue
index f6bebbb..ac936e3 100644
--- a/src/components/pivottable-ui/VPivottableUi.vue
+++ b/src/components/pivottable-ui/VPivottableUi.vue
@@ -4,19 +4,21 @@
@@ -27,28 +29,30 @@
@@ -57,26 +61,37 @@
-
|
-
+
|
+
+
+ State
+ rows: {{ state.rows }}
+ cols: {{ state.cols }}
+ aggregatorName: {{ state.aggregatorName }}
+ rendererName: {{ state.rendererName }}
+ vals: {{ state.vals }}
+ |
+
@@ -85,64 +100,52 @@
import {
defaultProps,
PivotData,
- aggregators,
- sortAs,
- getSort
-} from '../../helper'
+ sortAs
+} from '@/helper'
import VRendererCell from './VRendererCell.vue'
import VAggregatorCell from './VAggregatorCell.vue'
import VDragAndDropCell from './VDragAndDropCell.vue'
-import { VPivottable } from '../'
-import { computed, ref, watch } from 'vue'
-import { usePropsData } from '../../composables'
+import { VPivottable } from '@/'
+import { computed, ref, toRefs, watch } from 'vue'
+import { usePropsState } from '@/composables'
+import TableRenderer from '../pivottable/renderer/index'
+
const props = defineProps({
...defaultProps,
- async: {
- type: Boolean,
- default: false
- },
hiddenAttributes: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
hiddenFromAggregators: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
hiddenFromDragDrop: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
- sortonlyFromDragDrop: {
+ restrictedFromDragDrop: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
disabledFromDragDrop: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
menuLimit: {
type: Number,
default: 500
},
- config: {
+ pivotModel: {
type: Object,
- default: function () {
- return {}
- }
+ default: () => ({})
+ },
+ hideFilterBoxOfUnusedAttrs: {
+ type: Boolean,
+ default: false
}
})
-const state = ref({
+const pivotUiState = ref({
unusedOrder: props.unusedAttrs,
zIndices: {},
maxZIndex: 1000,
@@ -151,40 +154,37 @@ const state = ref({
attrValues: {},
materializedInput: []
})
-const { newProps, setProps, propUpdater } = usePropsData(props)
+const propsRefs = toRefs(props)
+
+const { state, updateState, updateMultiple } = usePropsState(propsRefs)
+const rendererItems = computed(() => Object.keys(state.value.renderers).length ? state.value.renderers : TableRenderer)
+const aggregatorItems = computed(() => state.value.aggregators)
const rowAttrs = computed(() => {
- return newProps.value.rows.filter(
+ return state.value.rows.filter(
e =>
- !newProps.value.hiddenAttributes.includes(e) &&
- !newProps.value.hiddenFromDragDrop.includes(e)
+ !state.value.hiddenAttributes.includes(e) &&
+ !state.value.hiddenFromDragDrop.includes(e)
)
})
-
const colAttrs = computed(() => {
- return newProps.value.cols.filter(
+ return state.value.cols.filter(
e =>
- !newProps.value.hiddenAttributes.includes(e) &&
- !newProps.value.hiddenFromDragDrop.includes(e)
+ !state.value.hiddenAttributes.includes(e) &&
+ !state.value.hiddenFromDragDrop.includes(e)
)
})
const unusedAttrs = computed(() => {
- return newProps.value.attributes
+ return state.value.attributes
.filter(
e =>
- !newProps.rows.includes(e) &&
- !newProps.value.cols.includes(e) &&
- !newProps.value.hiddenAttributes.includes(e) &&
- !newProps.value.hiddenFromDragDrop.includes(e)
+ !state.value.rows.includes(e) &&
+ !state.value.cols.includes(e) &&
+ !state.value.hiddenAttributes.includes(e) &&
+ !state.value.hiddenFromDragDrop.includes(e)
)
- .sort(sortAs(newProps.value.unusedOrder))
+ .sort(sortAs(state.value.unusedOrder))
})
-const aggregatorItems = computed(
- () => newProps.value.aggregators || aggregators
-)
-const numValsAllowed = computed(
- () =>
- aggregatorItems.value[newProps.value.aggregatorName]([])().numInputs || 0
-)
+
const materializeInput = nextData => {
if (props.data === nextData) {
return
@@ -219,39 +219,57 @@ const materializeInput = nextData => {
recordsProcessed++
}
)
- state.value = newState
- setProps({
- ...newProps.value,
- ...state.value
+
+ updateMultiple({
+ ...state.value,
+ ...newState,
+ ...pivotUiState.value
+ })
+}
+
+const onMoveFilterBoxToTop = ({ attribute }) => {
+ updateState('maxZIndex', state.value.maxZIndex++)
+ updateState('zIndices', {
+ ...state.value.zIndices,
+ [attribute]: state.value.maxZIndex
})
}
const onUpdateValueFilter = ({ attribute, valueFilter }) => {
- newProps.value.valueFilter[attribute] = valueFilter
+ updateState('valueFilter', {
+ ...state.value.valueFilter,
+ [attribute]: valueFilter
+ })
}
-const onMoveFilterBoxToTop = ({ attribute }) => {
- newProps.value.maxZIndex += 1
- newProps.value.zIndices[attribute] = newProps.value.maxZIndex + 1
+const onUpdateRendererName = (rendererName) => {
+ updateState('rendererName', rendererName)
}
-const onNoFilterBox = () => this.$emit('no:filterbox')
-const onValSlice = (e, i) => newProps.value.vals.splice(i, 1, e.target.value)
-
-const pivotData = new PivotData(newProps.value)
-
-watch(
- () => props.data,
- value => {
- state.value.unusedOrder = props.unusedAttrs
- materializeInput(value)
- },
- {
- immediate: false
- }
-)
-
-const updateFilters = ({ cellType, filters }) => {
- console.log('updated cell type', cellType)
- console.log('updated filter items', filters)
+const onUpdateAggregatorName = (aggregatorName) => {
+ updateState('aggregatorName', aggregatorName)
+}
+const onUpdateRowOrder = (rowOrder) => {
+ updateState('rowOrder', rowOrder)
+}
+const onUpdateColOrder = (colOrder) => {
+ updateState('colOrder', colOrder)
+}
+const onUpdateVals = (vals) => {
+ updateState('vals', vals)
}
+const onDraggedAttribute = ({ cellType, attributes }) => {
+ updateState(cellType, attributes)
+}
+const onUpdateOpenStatus = ({ attribute, status }) => {
+ updateState('openStatus', {
+ ...state.value.openStatus,
+ [attribute]: status
+ })
+}
+const pivotData = computed(() => new PivotData(state.value))
+
+watch(() => props.data, value => {
+ updateState('unusedOrder', props.unusedAttrs)
+ materializeInput(value)
+})
diff --git a/src/components/pivottable/renderer/index.js b/src/components/pivottable/renderer/index.js
new file mode 100644
index 0000000..e489f5b
--- /dev/null
+++ b/src/components/pivottable/renderer/index.js
@@ -0,0 +1,45 @@
+import { defineComponent, h } from 'vue'
+import TableRenderer from './TableRenderer.vue'
+import { defaultProps } from '@/helper'
+
+export default {
+ Table: defineComponent({
+ name: 'vue-table',
+ setup (props) {
+ return () => h(TableRenderer, {
+ ...defaultProps,
+ ...props
+ })
+ }
+ }),
+ 'Table Heatmap': defineComponent({
+ name: 'vue-table-heatmap',
+ setup (props) {
+ return () => h(TableRenderer, {
+ ...defaultProps,
+ ...props,
+ heatmapMode: 'full'
+ })
+ }
+ }),
+ 'Table Col Heatmap': defineComponent({
+ name: 'vue-table-col-heatmap',
+ setup (props) {
+ return () => h(TableRenderer, {
+ ...defaultProps,
+ ...props,
+ heatmapMode: 'col'
+ })
+ }
+ }),
+ 'Table Row Heatmap': defineComponent({
+ name: 'vue-table-row-heatmap',
+ setup (props) {
+ return () => h(TableRenderer, {
+ ...defaultProps,
+ ...props,
+ heatmapMode: 'row'
+ })
+ }
+ })
+}
diff --git a/src/composables/index.js b/src/composables/index.js
index 6cf3cad..3c5f459 100644
--- a/src/composables/index.js
+++ b/src/composables/index.js
@@ -1,2 +1,2 @@
export { usePivotData, providePivotData } from './pivotData'
-export { usePropsData } from './propData'
+export { usePropsState } from './usePropsState'
diff --git a/src/composables/propData.js b/src/composables/propData.js
deleted file mode 100644
index 1c07c63..0000000
--- a/src/composables/propData.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ref } from 'vue'
-
-const newProps = ref({})
-
-export function usePropsData ($props) {
- newProps.value = $props
- const propUpdater = ({ key, value }) => {
- newProps[key] = value
- }
- const setProps = ($props) => (newProps.value = $props)
- return {
- newProps,
- setProps,
- propUpdater
- }
-}
diff --git a/src/composables/useAggregators.js b/src/composables/useAggregators.js
new file mode 100644
index 0000000..8f3e4bc
--- /dev/null
+++ b/src/composables/useAggregators.js
@@ -0,0 +1,11 @@
+// 집계 함수들을 Vue3 반응형으로 래핑
+import { ref, computed } from 'vue'
+import { aggregators, aggregatorTemplates } from '../helper/utilities.js'
+
+export function useAggregators (options) {
+ // 집계 함수 선택 및 관리
+
+ return {
+ // 집계 함수와 관련 메서드
+ }
+}
diff --git a/src/composables/usePivotData.js b/src/composables/usePivotData.js
new file mode 100644
index 0000000..b8f8655
--- /dev/null
+++ b/src/composables/usePivotData.js
@@ -0,0 +1,41 @@
+import { inject, provide, ref, computed } from 'vue'
+import { PivotData } from '@/helper/utilities.js'
+
+const pivotDataKey = Symbol('pivotData')
+
+export function providePivotData (props) {
+ const error = ref(null)
+
+ const pivotData = computed(() => {
+ try {
+ return new PivotData(props)
+ } catch (err) {
+ console.error(err.stack)
+ error.value = 'An error occurred computing the PivotTable results.'
+ return null
+ }
+ })
+
+ const rowKeys = computed(() => pivotData.value?.getRowKeys() || [])
+ const colKeys = computed(() => pivotData.value?.getColKeys() || [])
+ 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([], [])
+
+ provide(pivotDataKey, {
+ pivotData,
+ rowKeys,
+ colKeys,
+ colAttrs,
+ rowAttrs,
+ getAggregator,
+ grandTotalAggregator,
+ error
+ })
+}
+
+export function usePivotData () {
+ return inject(pivotDataKey)
+}
diff --git a/src/composables/usePropsState.js b/src/composables/usePropsState.js
new file mode 100644
index 0000000..4021d6e
--- /dev/null
+++ b/src/composables/usePropsState.js
@@ -0,0 +1,73 @@
+import { ref, watch } from 'vue'
+
+export function usePropsState (initialProps) {
+ const state = ref({ ...initialProps })
+
+ watch(() => initialProps, (newProps) => {
+ // console.log('props 변경 감지:', newProps)
+ state.value = { ...newProps }
+ }, {
+ deep: true,
+ immediate: true
+ })
+
+ const logStateChange = (key, oldVal, newVal) => {
+ // console.log(`상태 변경: ${key}`, { 이전: oldVal, 새값: newVal })
+ }
+
+ const updateState = (key, value) => {
+ if (key in state.value) {
+ const oldVal = state.value[key]
+
+ if (typeof value === 'object' && value !== null) {
+ if (Array.isArray(value)) {
+ state.value[key] = [...value]
+ } else {
+ state.value[key] = { ...value }
+ }
+ } else {
+ state.value[key] = value
+ }
+
+ logStateChange(key, oldVal, state.value[key])
+ state.value = { ...state.value }
+ }
+ }
+
+ const updateMultiple = (updates) => {
+ const oldState = { ...state.value }
+ let changed = false
+
+ Object.entries(updates).forEach(([key, value]) => {
+ if (key in state.value) {
+ if (typeof value === 'object' && value !== null) {
+ if (Array.isArray(value)) {
+ state.value[key] = [...value]
+ } else {
+ state.value[key] = { ...value }
+ }
+ } else {
+ state.value[key] = value
+ }
+ changed = true
+ }
+ })
+
+ if (changed) {
+ // console.log('여러 상태 동시 업데이트:', { 이전: oldState, 새값: state.value })
+ state.value = { ...state.value }
+ }
+ }
+
+ const resetState = () => {
+ // console.log('상태 초기화')
+ state.value = { ...initialProps }
+ }
+
+ return {
+ state,
+ updateState,
+ updateMultiple,
+ resetState
+ }
+}
diff --git a/src/composables/useSorting.js b/src/composables/useSorting.js
new file mode 100644
index 0000000..a6bc0d2
--- /dev/null
+++ b/src/composables/useSorting.js
@@ -0,0 +1,11 @@
+// 정렬 관련 유틸리티를 Vue3 반응형으로 래핑
+import { ref, computed } from 'vue'
+import { naturalSort, getSort, sortAs } from '../helper/utilities.js'
+
+export function useSorting (options) {
+ // 정렬 방향, 정렬 필드 등 관리
+
+ return {
+ // 노출할 상태와 메서드
+ }
+}
diff --git a/src/composables/useTableRenderers.js b/src/composables/useTableRenderers.js
new file mode 100644
index 0000000..e0eb881
--- /dev/null
+++ b/src/composables/useTableRenderers.js
@@ -0,0 +1,19 @@
+// composables/useTableRenderer.js
+import { ref, computed } from 'vue'
+import { redColorScaleGenerator } from '../helper/utilities.js'
+
+export function useTableRenderer (options) {
+ // 기존 redColorScaleGenerator 및 관련 함수 래핑
+
+ // 히트맵 로직
+ const getColorScaleGenerator = (values) => {
+ return options.tableColorScaleGenerator || redColorScaleGenerator(values)
+ }
+
+ // 기타 렌더러 관련 함수
+
+ return {
+ getColorScaleGenerator
+ // 기타 필요한 함수 및 상태
+ }
+}
diff --git a/src/composables/useValueFormatting.js b/src/composables/useValueFormatting.js
new file mode 100644
index 0000000..7d110cf
--- /dev/null
+++ b/src/composables/useValueFormatting.js
@@ -0,0 +1,11 @@
+// 값 포맷팅 기능을 Vue3 반응형으로 래핑
+import { ref, computed } from 'vue'
+import { numberFormat } from '../helper/utilities.js'
+
+export function useValueFormatting (options) {
+ // 포맷팅 옵션 관리
+
+ return {
+ // 포맷팅 함수와 옵션
+ }
+}
diff --git a/src/helper/defaultProps.js b/src/helper/defaultProps.js
index 23f6f14..9f3f855 100644
--- a/src/helper/defaultProps.js
+++ b/src/helper/defaultProps.js
@@ -6,9 +6,7 @@ export default {
},
aggregators: {
type: Object,
- default: function () {
- return aggregators
- }
+ default: () => aggregators
},
aggregatorName: {
type: String,
@@ -20,11 +18,12 @@ export default {
},
tableOptions: {
type: Object,
- default: function () {
- return {}
- }
+ default: () => ({})
+ },
+ renderers: {
+ type: Object,
+ default: () => ({})
},
- renderers: Object,
rendererName: {
type: String,
default: 'Table'
@@ -33,89 +32,59 @@ export default {
type: String,
default: 'en'
},
- locales: {
+ languagePack: {
type: Object,
- default: function () {
- return locales
- }
+ default: () => locales
},
- rowTotal: {
+ showRowTotal: {
type: Boolean,
default: true
},
- colTotal: {
+ showColTotal: {
type: Boolean,
default: true
},
cols: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
rows: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
vals: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
attributes: {
type: Array,
- default: function () {
- return []
- }
+ default: () => []
},
valueFilter: {
type: Object,
- default: function () {
- return {}
- }
+ default: () => ({})
},
sorters: {
type: [Function, Object],
- default: function () {
- return {}
- }
+ default: () => ({})
},
derivedAttributes: {
type: [Function, Object],
- default: function () {
- return {}
- }
+ default: () => ({})
},
rowOrder: {
type: String,
default: 'key_a_to_z',
- validator: function (value) {
- return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1
- }
+ validator: (value) => ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1
},
colOrder: {
type: String,
default: 'key_a_to_z',
- validator: function (value) {
- return ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1
- }
+ validator: value => ['key_a_to_z', 'value_a_to_z', 'value_z_to_a'].indexOf(value) !== -1
},
tableMaxWidth: {
type: Number,
default: 0,
- validator: function (value) {
- return value >= 0
- }
- },
- colLimit: {
- type: Number,
- default: 100
- },
- rowLimit: {
- type: Number,
- default: 100
+ validator: value => value >= 0
}
}
diff --git a/vite.config.js b/vite.config.js
index 85ce67a..9965a0b 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,7 +1,22 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
-
+import { fileURLToPath, URL } from 'node:url'
// https://vite.dev/config/
export default defineConfig({
- plugins: [vue()]
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ },
+ extensions: [
+ '.mjs',
+ '.js',
+ '.json',
+ '.jsx',
+ '.ts',
+ '.tsx',
+ '.vue',
+ '.mts'
+ ]
+ }
})