diff --git a/src/components/pivottable-ui/VDragAndDropCell.vue b/src/components/pivottable-ui/VDragAndDropCell.vue index 32a96d7..9c0396d 100644 --- a/src/components/pivottable-ui/VDragAndDropCell.vue +++ b/src/components/pivottable-ui/VDragAndDropCell.vue @@ -1,33 +1,28 @@ <template> <td> - <h3>VDragAndDropCell ({{ attrs }})</h3> + <h3>VDragAndDropCell ({{ cellType }})</h3> <Draggable - v-model="modelItems" - :class="classNames" - :group="{ name: 'sharted', pull: true, put: true }" - ghost-class="pvtPlaceholder" - @change="onChange" tag="ul" + :list="modelItems" + :group="{ name: 'sharted', pull: true, put: true }" + ghost-class=".pvtFilterBox" :preventOnfFilter="false" + :class="classes" + @sort="onDrag" > - <!-- :modelValue="items" --> - <!-- @update:modelValue="onChange" --> - <!-- @sort="onChange"--> <VDraggableAttribute v-for="item in modelItems" :key="item" - :name="item" - :sortable="true" - :draggable="true" - :attrValues="{}" - :sorter="getSort(() => { }, item)" - :menuLimit="500" - :zIndex="1000" - :valueFilter="{}" - :open="false" - :async="false" - :unused="false" - :localeStrings="{}" + :disabled="disabledFromDragDrop.includes(item)" + :sortOnly="restrictedFromDragDrop.includes(item)" + :open="openStatus?.[item]" + :unSelectedFilterValues="valueFilter?.[item]" + :attributeName="item" + :zIndex="zIndices[item]" + :hideDropDown="hideDropDown" + @update:zIndexOfFilterBox="$emit('update:zIndexOfFilterBox')" + @update:unselectedFilterValues="$emit('update:unselectedFilterValues')" + @update:openStatusOfFilterBox="$emit('update:unselectedFilterValues')" > <template #pvtAttr="{ attrName }"> {{ attrName }} @@ -38,40 +33,77 @@ </template> <script setup> -import VDraggableAttribute from './VDraggableAttribute.vue' +import { ref, onMounted, computed } from 'vue' import { VueDraggableNext as Draggable } from 'vue-draggable-next' -import { ref, onMounted } from 'vue' -import { PivotData, getSort, sortAs, aggregators } from '../../helper' +import VDraggableAttribute from './VDraggableAttribute.vue' -const emit = defineEmits(['update:filters']) +const emit = defineEmits([ + 'update:draggedAttribute', + 'update:zIndexOfFilterBox', + 'update:unselectedFilterValues', + 'update:openStatusOfFilterBox' +]) const props = defineProps({ - attrs: { + cellType: { + type: String, + required: true + }, + // 삭제할 수 있으면 삭제 + classes: { type: String, default: '' }, - items: { + attributeNames: { + type: Array, + default: () => [] + }, + valueFilter: { + type: Object, + default: () => ({}) + }, + // 같은 셀 내 이동만 가능(정렬) + restrictedFromDragDrop: { type: Array, default: () => [] }, - classNames: { + // 삭제 보류 + disabledFromDragDrop: { type: Array, default: () => [] + }, + hideFilterBoxOfUnusedAttributes: { + type: Boolean, + default: false + }, + zIndices: { + type: Object, + default: () => ({}) + }, + openStatus: { + type: Object, + default: () => ({}) } }) const modelItems = ref([]) -const onChange = evt => { +const onDrag = evt => { console.log('event', Object.keys(evt)[0]) - emit('update:filters', { cellType: props.attrs, filters: modelItems.value }) + emit('update:draggedAttribute', { + cellType: props.cellType, + attributes: modelItems.value + }) } onMounted(() => { - modelItems.value = [...props.items] + modelItems.value = [...props.attributeNames] }) +// 이름 변경해야할 것 같음 hideDropDownInUnusedCell +const hideDropDown = computed(() => { + return props.cellType === 'unused' && props.hideFilterBoxOfUnusedAttrs +}) </script> -<style scoped> -</style> +<style scoped></style> diff --git a/src/components/pivottable-ui/VDraggableAttribute.vue b/src/components/pivottable-ui/VDraggableAttribute.vue index 4e4647d..e9df68f 100644 --- a/src/components/pivottable-ui/VDraggableAttribute.vue +++ b/src/components/pivottable-ui/VDraggableAttribute.vue @@ -1,22 +1,23 @@ <template> - <li :data-id="!disabled ? name : undefined"> - <span class="pvtAttr" :class="[filtered, { sortonly, disabled }]"> - <slot name="pvtAttr" :attrName="name">{{ name }}</slot> + <li> + <span class="pvtAttr" :class="[filtered, { sortOnly, disabled }]"> + <slot name="pvtAttr" :attrName="attributeName">{{ attributeName }}</slot> <span - v-if="showDropdown" + v-if="!hideDropDownButton" + @click="toggleFilterBox" class="pvtTriangle" - > ▾</span> + > + ▾ + </span> <VFilterBox v-if="open" - :valueFilter="valueFilter" - :name="name" - :attrValues="attrValues" - :sorter="sorter" - :menuLimit="menuLimit" + :unselectedFilterValues="unselectedFilterValues" + :filterBoxKey="attributeName" + :zIndex="zIndex" + @update:zIndexOfFilterBox="$emit('update:zIndexOfFilterBox')" + @update:unselectedFilterValues="$emit('update:unselectedFilterValues')" > </VFilterBox> - <!-- <VFilterBox v-if="open" ></VFilterBox> --> - <!-- <slot v-if="open" name="filterbox"></slot> --> </span> </li> </template> @@ -25,50 +26,62 @@ import VFilterBox from './VFilterBox.vue' import { computed } from 'vue' +const emit = defineEmits([ + 'update:zIndexOfFilterBox', + 'update:unselectedFilterValues', + 'update:openStatusOfFilterBox' +]) + const props = defineProps({ - draggable: { - type: Boolean, - default: true + attributeName: { + type: String, + required: true }, - sortable: { + disabled: { type: Boolean, - default: true + default: false }, - name: { - type: String, - required: true + sortOnly: { + type: Boolean, + default: false }, open: { type: Boolean, default: false }, - async: Boolean, - unused: Boolean, - valueFilter: { + unselectedFilterValues: { type: Object, - default: () => { - return {} - } + default: () => ({}) }, - attrValues: { - type: Object, - required: false + zIndex: { + type: Number }, - sorter: { - type: Function, - required: true + hideDropDown: { + type: Boolean, + default: false }, - menuLimit: Number + // 임시 데이터; 필터 목록은 어떻게 할 것인지? + attributeValues: { + type: Array, + default: () => [] + } }) -const disabled = computed(() => !props.sortable && !props.draggable) -const sortonly = computed(() => props.sortable && !props.draggable) +const toggleFilterBox = () => { + emit('update:openStatusOfFilterBox', props.attributeName) +} -const filtered = computed(() => Object.keys(props.valueFilter).length !== 0 ? 'pvtFilteredAttribute' : '') -const showDropdown = computed(() => !disabled.value && (props.async ? !props.unused : true)) +const hideDropDownButton = computed( + () => props.hideDropDown || !props.attributeValues.length || props.disabled +) +const filtered = computed(() => { + return Object.keys(props.unselectedFilterValues).length !== 0 + ? 'pvtFilteredAttribute' + : null +}) </script> <style scoped> - +/* css sortonly를 sortOnly로 변경해야함 */ </style> diff --git a/src/components/pivottable-ui/VPivottableUi.vue b/src/components/pivottable-ui/VPivottableUi.vue index 58a2db0..f6bebbb 100644 --- a/src/components/pivottable-ui/VPivottableUi.vue +++ b/src/components/pivottable-ui/VPivottableUi.vue @@ -8,27 +8,19 @@ @update:propUpdater="propUpdater" /> <VDragAndDropCell - attrs="unused" - :items="unusedAttrs" - :unusedOrder="newProps.unusedOrder" - :unusedItems="unusedAttrs" - :sortonlyFromDragDrop="newProps.sortonlyFromDragDrop" - :fields="newProps.unusedOrder" - :attrValues="newProps.attrValues" - :menuLimit="newProps.menuLimit" - :maxZIndex="newProps.maxZIndex" - :valueFilter="newProps.valueFilter" - :async="newProps.async" - :localeStrings="newProps.locales[newProps.locale].localeStrings" - :getSort="getSort" - @update:filter="onUpdateValueFilter" - @moveToTop:filterbox="onMoveFilterBoxToTop" - @no:filterbox="onNoFilterBox" + cellType="unused" + :attributeNames="unusedAttrs" + classes="pvtAxisContainer pvtUnused pvtHorizList" + :restrictedFromDragDrop="[]" + :disabledFromDragDrop="[]" + :zIndices="{}" + :openStatus="{}" + :hideFilterBoxOfUnusedAttributes="false" + :valueFilter="{}" > <template v-slot:pvtAttr="props"> - <slot name="pvtAttr" v-bind="props"/> + <slot name="pvtAttr" v-bind="props" /> </template> - </VDragAndDropCell> </tr> <tr> @@ -49,56 +41,35 @@ </slot> <VDragAndDropCell - attrs="col" - :items="colAttrs" - :unusedOrder="newProps.unusedOrder" - :unusedItems="unusedAttrs" - :sortonlyFromDragDrop="newProps.sortonlyFromDragDrop" - :fields="newProps.cols" - :attrValues="newProps.attrValues" - :menuLimit="newProps.menuLimit" - :maxZIndex="newProps.maxZIndex" - :valueFilter="newProps.valueFilter" - :async="newProps.async" - :localeStrings="newProps.locales[newProps.locale].localeStrings" - :getSort="getSort" - @update:filter="onUpdateValueFilter" - @moveToTop:filterbox="onMoveFilterBoxToTop" - @no:filterbox="onNoFilterBox" + cellType="col" + :attributeNames="colAttrs" + classes="pvtAxisContainer pvtHorizList pvtCols" + :restrictedFromDragDrop="[]" + :disabledFromDragDrop="[]" + :zIndices="{}" + :openStatus="{}" + :valueFilter="{}" > <template v-slot:pvtAttr="props"> - <slot name="pvtAttr" v-bind="props"/> + <slot name="pvtAttr" v-bind="props" /> </template> </VDragAndDropCell> - </tr> <tr> <VDragAndDropCell - attrs="row" - :items="rowAttrs" - :unusedOrder="newProps.unusedOrder" - :unusedItems="unusedAttrs" - :sortonlyFromDragDrop="newProps.sortonlyFromDragDrop" - :fields="newProps.rows" - :attrValues="newProps.attrValues" - :menuLimit="newProps.menuLimit" - :maxZIndex="newProps.maxZIndex" - :valueFilter="newProps.valueFilter" - :async="newProps.async" - :localeStrings="newProps.locales[newProps.locale].localeStrings" - :getSort="getSort" - @update:filter="onUpdateValueFilter" - @moveToTop:filterbox="onMoveFilterBoxToTop" - @no:filterbox="onNoFilterBox" + cellType="row" + :attributeNames="rowAttrs" + classes="pvtAxisContainer pvtVertList pvtRows" + :restrictedFromDragDrop="[]" + :disabledFromDragDrop="[]" + :zIndices="{}" + :openStatus="{}" + :valueFilter="{}" > <template v-slot:pvtAttr="props"> - <slot - v-bind="props" - name="pvtAttr" - /> + <slot v-bind="props" name="pvtAttr" /> </template> <!-- <slot name="pvtAttr" v-bind="$props"></slot> --> - </VDragAndDropCell> <td class="pvtOutput"> <slot name="outputSlot" :outputSlot="{ pivotData }"> @@ -111,7 +82,13 @@ </template> <script setup> -import { defaultProps, PivotData, aggregators, sortAs, getSort } from '../../helper' +import { + defaultProps, + PivotData, + aggregators, + sortAs, + getSort +} from '../../helper' import VRendererCell from './VRendererCell.vue' import VAggregatorCell from './VAggregatorCell.vue' import VDragAndDropCell from './VDragAndDropCell.vue' @@ -179,7 +156,7 @@ const rowAttrs = computed(() => { return newProps.value.rows.filter( e => !newProps.value.hiddenAttributes.includes(e) && - !newProps.value.hiddenFromDragDrop.includes(e) + !newProps.value.hiddenFromDragDrop.includes(e) ) }) @@ -187,21 +164,28 @@ const colAttrs = computed(() => { return newProps.value.cols.filter( e => !newProps.value.hiddenAttributes.includes(e) && - !newProps.value.hiddenFromDragDrop.includes(e) + !newProps.value.hiddenFromDragDrop.includes(e) ) }) const unusedAttrs = computed(() => { - return newProps.value.attributes.filter( - e => - !newProps.rows.includes(e) && - !newProps.value.cols.includes(e) && - !newProps.value.hiddenAttributes.includes(e) && - !newProps.value.hiddenFromDragDrop.includes(e) - ).sort(sortAs(newProps.value.unusedOrder)) + return newProps.value.attributes + .filter( + e => + !newProps.rows.includes(e) && + !newProps.value.cols.includes(e) && + !newProps.value.hiddenAttributes.includes(e) && + !newProps.value.hiddenFromDragDrop.includes(e) + ) + .sort(sortAs(newProps.value.unusedOrder)) }) -const aggregatorItems = computed(() => (newProps.value.aggregators) || aggregators) -const numValsAllowed = computed(() => aggregatorItems.value[newProps.value.aggregatorName]([])().numInputs || 0) -const materializeInput = (nextData) => { +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 } @@ -212,25 +196,29 @@ const materializeInput = (nextData) => { } let recordsProcessed = 0 - PivotData.forEachRecord(newState.data, props.derivedAttributes, function (record) { - newState.materializedInput.push(record) - for (const attr of Object.keys(record)) { - if (!(attr in newState.attrValues)) { - newState.attrValues[attr] = {} - if (recordsProcessed > 0) { - newState.attrValues[attr].null = recordsProcessed + PivotData.forEachRecord( + newState.data, + props.derivedAttributes, + function (record) { + newState.materializedInput.push(record) + for (const attr of Object.keys(record)) { + if (!(attr in newState.attrValues)) { + newState.attrValues[attr] = {} + if (recordsProcessed > 0) { + newState.attrValues[attr].null = recordsProcessed + } } } - } - for (const attr in newState.attrValues) { - const value = attr in record ? record[attr] : 'null' - if (!(value in newState.attrValues[attr])) { - newState.attrValues[attr][value] = 0 + for (const attr in newState.attrValues) { + const value = attr in record ? record[attr] : 'null' + if (!(value in newState.attrValues[attr])) { + newState.attrValues[attr][value] = 0 + } + newState.attrValues[attr][value]++ } - newState.attrValues[attr][value]++ + recordsProcessed++ } - recordsProcessed++ - }) + ) state.value = newState setProps({ ...newProps.value, @@ -249,18 +237,21 @@ 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 -}) +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) } - </script> <style> @@ -269,7 +260,8 @@ const updateFilters = ({ cellType, filters }) => { width: 100%; } -.pvtUi td, .pvtUi th { +.pvtUi td, +.pvtUi th { border: 1px solid black; padding: 8px; }