UNPKG

@extclp/vexip-ui

Version:

A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good

1 lines 16.6 kB
{"version":3,"file":"table-row.vue2.mjs","sources":["../../../components/table/table-row.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CollapseTransition } from '@/components/collapse-transition'\nimport { Renderer } from '@/components/renderer'\nimport { ResizeObserver } from '@/components/resize-observer'\n\nimport { computed, inject, nextTick, onMounted, reactive, ref, toRef, watchEffect } from 'vue'\n\nimport { useNameHelper } from '@vexip-ui/config'\nimport { useSetTimeout } from '@vexip-ui/hooks'\nimport { isFunction } from '@vexip-ui/utils'\nimport { TABLE_ACTIONS, TABLE_STORE } from './symbol'\n\nimport type { PropType } from 'vue'\nimport type { TableRowState } from './symbol'\n\ndefineOptions({ name: 'TableRow' })\n\nconst props = defineProps({\n row: {\n type: Object as PropType<TableRowState>,\n default: () => ({})\n },\n index: {\n type: Number,\n default: null\n },\n isHead: {\n type: Boolean,\n default: false\n },\n isFoot: {\n type: Boolean,\n default: false\n },\n fixed: {\n type: String as PropType<'left' | 'right' | undefined>,\n default: null\n }\n})\n\nconst { state, getters, mutations } = inject(TABLE_STORE)!\nconst tableAction = inject(TABLE_ACTIONS)!\n\nconst nh = useNameHelper('table')\n\nconst { timer } = useSetTimeout()\nconst dragging = ref(false)\nconst isDragOver = ref(false)\n\nconst wrapper = ref<HTMLElement>()\nconst rowEl = ref<HTMLElement>()\nconst expandEl = ref<HTMLElement>()\n\nconst instance = reactive({\n el: wrapper,\n row: toRef(props, 'row')\n})\n\nconst rowKey = computed(() => props.row.key)\nconst rowType = computed(() => (props.isHead ? 'head' : props.isFoot ? 'foot' : undefined))\nconst className = computed(() => {\n let customClass = null\n\n if (!rowType.value) {\n if (typeof state.rowClass === 'function') {\n customClass = state.rowClass(props.row.data, props.index)\n } else {\n customClass = state.rowClass\n }\n }\n\n return [\n nh.be('row'),\n {\n [nh.bem('row', 'fixed')]: state.rowHeight && state.rowHeight > 0,\n [nh.bem('row', 'hover')]: !rowType.value && state.highlight && props.row.hover,\n [nh.bem('row', 'stripe')]: state.stripe && props.index % 2 === 1,\n [nh.bem('row', 'checked')]: props.row.checked,\n [nh.bem('row', 'dragging')]: dragging.value,\n [nh.bem('row', 'drag-over')]: isDragOver.value\n },\n customClass\n ]\n})\nconst maxHeight = computed(() =>\n Math.max(...Object.values(props.row.cellHeights || {}), state.rowMinHeight)\n)\nconst style = computed(() => {\n let customStyle: any = ''\n\n if (!rowType.value) {\n if (typeof state.rowStyle === 'function') {\n customStyle = state.rowStyle(props.row.data, props.index)\n } else {\n customStyle = state.rowStyle\n }\n }\n\n return [\n customStyle,\n {\n height: !state.rowHeight ? `${maxHeight.value}px` : `${state.rowHeight}px`,\n minHeight: state.rowHeight ? undefined : `${state.rowMinHeight}px`,\n border: '0'\n }\n ]\n})\nconst attrs = computed(() => {\n if (!rowType.value) {\n if (typeof state.rowAttrs === 'function') {\n return state.rowAttrs(props.row.data, props.index)\n } else {\n return state.rowAttrs\n }\n }\n\n return null\n})\nconst groupStyle = computed(() => {\n if (props.isHead || props.isFoot) return undefined\n\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n state.heightTrigger\n\n const offset =\n state.heightBITree && !rowType.value && props.index ? state.heightBITree.sum(props.index) : 0\n\n return {\n transform: offset ? `translate3d(0, ${offset}px, 0)` : undefined\n }\n})\nconst cellDraggable = computed(() => {\n return getters.hasDragColumn && !getters.disableDragRows.has(rowKey.value)\n})\nconst rowDraggable = computed(() => !rowType.value && state.rowDraggable)\nconst draggable = computed(() => !rowType.value && (state.rowDraggable || cellDraggable.value))\nconst expandRenderer = computed(() => state.expandRenderer)\nconst hasExpand = computed(() => {\n if (props.isHead || props.isFoot || !getters.expandColumn) return false\n if (state.rightFixedColumns.length) return props.fixed === 'right'\n if (state.leftFixedColumns.length) return props.fixed === 'left'\n\n return !!state.normalColumns.length && !props.fixed\n})\n\nfunction setExpandHeight() {\n let targetHeight: number\n\n if (props.row.expanded && expandEl.value) {\n targetHeight = expandEl.value.scrollHeight\n } else {\n targetHeight = 0\n }\n\n if (targetHeight !== props.row.expandHeight) {\n mutations.setRowProp(rowKey.value, 'expandHeight', targetHeight)\n updateTotalHeight(true)\n }\n}\n\nfunction updateTotalHeight(force = false) {\n if (state.heightBITree && getters.visibleKeys.has(rowKey.value) && (force || !props.fixed)) {\n const height = props.row.height + props.row.expandHeight\n const tree = state.heightBITree\n const prev = tree.get(props.index)\n\n if (height !== prev) {\n tree.add(props.index, height - prev)\n mutations.updateTotalHeight()\n mutations.triggerHeightChange()\n }\n }\n}\n\nfunction handleResize(entry: ResizeObserverEntry) {\n const height = entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height\n mutations.setRowProp(rowKey.value, 'height', height)\n !rowType.value && updateTotalHeight()\n}\n\nwatchEffect(() => {\n if (props.isHead || props.isFoot) return\n\n mutations.setRowProp(rowKey.value, 'height', state.rowHeight || maxHeight.value)\n !rowType.value && updateTotalHeight()\n nextTick(() => {\n hasExpand.value && setExpandHeight()\n })\n})\n\nonMounted(() => {\n nextTick(() => {\n mutations.setRowProp(rowKey.value, 'height', state.rowHeight || maxHeight.value)\n nextTick(() => {\n hasExpand.value && setExpandHeight()\n })\n })\n})\n\nfunction buildEventPayload(event: Event) {\n return {\n row: props.row.data,\n key: props.row.key,\n index: props.index,\n event\n }\n}\n\nfunction handleMouseEnter(event: MouseEvent) {\n mutations.setRowProp(rowKey.value, 'hover', true)\n\n if (!rowType.value && tableAction) {\n tableAction.emitRowEvent('Enter', buildEventPayload(event))\n }\n}\n\nfunction handleMouseLeave(event: MouseEvent) {\n mutations.setRowProp(rowKey.value, 'hover', false)\n\n if (!rowType.value && tableAction) {\n tableAction.emitRowEvent('Leave', buildEventPayload(event))\n }\n}\n\nfunction handleClick(event: MouseEvent) {\n if (!rowType.value && tableAction) {\n tableAction.emitRowEvent('Click', buildEventPayload(event))\n }\n}\n\nfunction handleDblclick(event: MouseEvent) {\n if (!rowType.value && tableAction) {\n tableAction.emitRowEvent('Dblclick', buildEventPayload(event))\n }\n}\n\nfunction handleContextmenu(event: MouseEvent) {\n if (!rowType.value && tableAction) {\n tableAction.emitRowEvent('Contextmenu', buildEventPayload(event))\n }\n}\n\nfunction shouldProcessDrag() {\n return draggable.value && state.dragging\n}\n\nfunction handleDragStart(event: DragEvent) {\n if (!draggable.value && !cellDraggable.value) return\n\n dragging.value = true\n tableAction.handleRowDragStart(instance, event)\n}\n\nfunction handleDragOver(event: DragEvent) {\n if (!shouldProcessDrag() || (cellDraggable.value && !getters.rowDragging)) return\n\n clearTimeout(timer.drag)\n event.stopPropagation()\n event.preventDefault()\n\n isDragOver.value = true\n\n tableAction.handleRowDragOver(instance, event)\n}\n\nfunction handleDrop(event: DragEvent) {\n if (!shouldProcessDrag()) return\n\n clearTimeout(timer.drag)\n event.stopPropagation()\n event.preventDefault()\n\n isDragOver.value = false\n\n tableAction.handleRowDrop(instance, event)\n nextTick(() => mutations.handleDrag(rowKey.value, false))\n}\n\nfunction handleDragEnd(event: DragEvent) {\n if (!shouldProcessDrag()) return\n\n event.stopPropagation()\n dragging.value = true\n\n tableAction.handleRowDragEnd(event)\n nextTick(() => mutations.handleDrag(rowKey.value, false))\n}\n\nfunction handleDragLeave(event: DragEvent) {\n if (!shouldProcessDrag()) return\n\n clearTimeout(timer.drag)\n event.preventDefault()\n\n timer.drag = setTimeout(() => {\n isDragOver.value = false\n }, 100)\n}\n\nfunction afterExpand() {\n mutations.setRowProp(rowKey.value, 'expandAnimate', false)\n}\n</script>\n\n<template>\n <div\n v-if=\"!row.hidden\"\n ref=\"wrapper\"\n :class=\"{\n [nh.be('group')]: true,\n [nh.bem('group', 'checked')]: row.checked,\n [nh.bem('group', 'last')]: row.last\n }\"\n role=\"row\"\n :draggable=\"rowDraggable || row.dragging\"\n :style=\"groupStyle\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n @click=\"handleClick\"\n @dblclick=\"handleDblclick\"\n @contextmenu=\"handleContextmenu\"\n @dragstart.stop=\"handleDragStart\"\n @dragover=\"handleDragOver\"\n @drop=\"handleDrop\"\n @dragend=\"handleDragEnd\"\n @dragleave=\"handleDragLeave\"\n >\n <ResizeObserver :on-resize=\"handleResize\">\n <div\n v-bind=\"attrs\"\n ref=\"rowEl\"\n :class=\"className\"\n :style=\"style\"\n >\n <slot></slot>\n </div>\n </ResizeObserver>\n <CollapseTransition\n v-if=\"hasExpand\"\n :disabled=\"!row.expandAnimate\"\n @enter=\"setExpandHeight\"\n @leave=\"setExpandHeight\"\n @after-enter=\"afterExpand\"\n @after-leave=\"afterExpand\"\n >\n <div\n v-if=\"row.expanded\"\n ref=\"expandEl\"\n :class=\"[nh.be('expanded'), fixed === 'right' && nh.bem('expanded', 'fixed')]\"\n >\n <ResizeObserver :disabled=\"row.expandAnimate\" :on-resize=\"setExpandHeight\">\n <div :class=\"nh.be('expanded-wrapper')\">\n <Renderer\n v-if=\"isFunction(getters.expandColumn!.renderer)\"\n :renderer=\"getters.expandColumn!.renderer\"\n :data=\"{ leftFixed: 0, rightFixed: 0, row: row.data, rowIndex: index }\"\n ></Renderer>\n <Renderer\n v-else-if=\"isFunction(expandRenderer)\"\n :renderer=\"expandRenderer\"\n :data=\"{ leftFixed: 0, rightFixed: 0, row: row.data, rowIndex: index }\"\n ></Renderer>\n </div>\n </ResizeObserver>\n </div>\n </CollapseTransition>\n </div>\n</template>\n"],"names":["props","__props","state","getters","mutations","inject","TABLE_STORE","tableAction","TABLE_ACTIONS","nh","useNameHelper","timer","useSetTimeout","dragging","ref","isDragOver","wrapper","rowEl","expandEl","instance","reactive","toRef","rowKey","computed","rowType","className","customClass","maxHeight","style","customStyle","attrs","groupStyle","offset","cellDraggable","rowDraggable","draggable","expandRenderer","hasExpand","setExpandHeight","targetHeight","updateTotalHeight","force","height","tree","prev","handleResize","entry","_b","_a","watchEffect","nextTick","onMounted","buildEventPayload","event","handleMouseEnter","handleMouseLeave","handleClick","handleDblclick","handleContextmenu","shouldProcessDrag","handleDragStart","handleDragOver","handleDrop","handleDragEnd","handleDragLeave","afterExpand"],"mappings":"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"}