Forráskód Böngészése

完成基础表格元素

pipipi-pikachu 5 éve
szülő
commit
a1515e1994

+ 2 - 20
src/hooks/useCreateElement.ts

@@ -3,7 +3,7 @@ import { MutationTypes } from '@/store'
 import { createRandomCode } from '@/utils/common'
 import { getImageSize } from '@/utils/image'
 import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
-import { ChartType, PPTElement, TableCell } from '@/types/slides'
+import { ChartType, PPTElement } from '@/types/slides'
 import { ShapePoolItem } from '@/configs/shapes'
 import { LinePoolItem } from '@/configs/lines'
 import {
@@ -12,7 +12,6 @@ import {
   DEFAULT_SHAPE,
   DEFAULT_LINE,
   DEFAULT_CHART,
-  DEFAULT_TABLE,
 } from '@/configs/element'
 import useHistorySnapshot from '@/hooks/useHistorySnapshot'
 
@@ -77,24 +76,7 @@ export default () => {
   }
   
   const createTableElement = (rowCount: number, colCount: number) => {
-    const row: TableCell[] = new Array(colCount).fill({ colspan: 1, rowspan: 1, content: '' })
-    const data: TableCell[][] = new Array(rowCount).fill(row)
-  
-    const DEFAULT_CELL_WIDTH = 80
-    const DEFAULT_CELL_HEIGHT = 35
-    const DEFAULT_BORDER_WIDTH = 2
-  
-    const colWidths: number[] = new Array(colCount).fill(DEFAULT_CELL_WIDTH)
-  
-    createElement({
-      ...DEFAULT_TABLE,
-      type: 'table',
-      id: createRandomCode(),
-      width: colCount * DEFAULT_CELL_WIDTH + DEFAULT_BORDER_WIDTH,
-      height: rowCount * DEFAULT_CELL_HEIGHT + DEFAULT_BORDER_WIDTH,
-      colWidths,
-      data,
-    })
+    console.log(rowCount, colCount)
   }
   
   const createTextElement = (position: CommonElementPosition) => {

+ 6 - 1
src/mocks/index.ts

@@ -25,8 +25,13 @@ export const slides: Slide[] = [
         left: 20,
         top: 20,
         width: 400,
-        height: 90,
+        height: 108,
         colWidths: [0.25, 0.25, 0.25, 0.25],
+        outline: {
+          width: 1,
+          style: 'solid',
+          color: '#999',
+        },
         data: [
           [
             { id: '1', colspan: 1, rowspan: 1, text: '' },

+ 1 - 0
src/types/slides.ts

@@ -160,6 +160,7 @@ export interface PPTTableElement {
   groupId?: string;
   width: number;
   height: number;
+  outline: PPTElementOutline;
   colWidths: number[];
   data: TableCell[][];
 }

+ 3 - 1
src/views/Editor/Canvas/Operate/TableElementOperate.vue

@@ -61,7 +61,9 @@ export default defineComponent({
     const store = useStore<State>()
     const canvasScale = computed(() => store.state.canvasScale)
 
-    const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
+    const outlineWidth = computed(() => props.elementInfo.outline.width || 1)
+
+    const scaleWidth = computed(() => (props.elementInfo.width + outlineWidth.value) * canvasScale.value)
     const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
 
     const { textElementResizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)

+ 2 - 0
src/views/Screen/ScreenElement.vue

@@ -24,6 +24,7 @@ import BaseTextElement from '@/views/components/element/TextElement/BaseTextElem
 import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
 import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
 import ScreenChartElement from '@/views/components/element/ChartElement/ScreenChartElement.vue'
+import BaseTableElement from '@/views/components/element/TableElement/BaseTableElement.vue'
 
 export default defineComponent({
   name: 'screen-element',
@@ -49,6 +50,7 @@ export default defineComponent({
         [ElementTypes.SHAPE]: BaseShapeElement,
         [ElementTypes.LINE]: BaseLineElement,
         [ElementTypes.CHART]: ScreenChartElement,
+        [ElementTypes.TABLE]: BaseTableElement,
       }
       return elementTypeMap[props.elementInfo.type] || null
     })

+ 2 - 0
src/views/components/ThumbnailSlide/ThumbnailElement.vue

@@ -20,6 +20,7 @@ import BaseTextElement from '@/views/components/element/TextElement/BaseTextElem
 import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
 import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
 import BaseChartElement from '@/views/components/element/ChartElement/BaseChartElement.vue'
+import BaseTableElement from '@/views/components/element/TableElement/BaseTableElement.vue'
 
 export default defineComponent({
   name: 'base-element',
@@ -41,6 +42,7 @@ export default defineComponent({
         [ElementTypes.SHAPE]: BaseShapeElement,
         [ElementTypes.LINE]: BaseLineElement,
         [ElementTypes.CHART]: BaseChartElement,
+        [ElementTypes.TABLE]: BaseTableElement,
       }
       return elementTypeMap[props.elementInfo.type] || null
     })

+ 2 - 1
src/views/components/element/ShapeElement/BaseShapeElement.vue

@@ -1,5 +1,6 @@
 <template>
-  <div class="base-element-shape"
+  <div 
+    class="base-element-shape"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',

+ 2 - 1
src/views/components/element/ShapeElement/index.vue

@@ -1,5 +1,6 @@
 <template>
-  <div class="editable-element-shape"
+  <div 
+    class="editable-element-shape"
     :class="{ 'lock': elementInfo.lock }"
     :style="{
       top: elementInfo.top + 'px',

+ 51 - 0
src/views/components/element/TableElement/BaseTableElement.vue

@@ -0,0 +1,51 @@
+<template>
+  <div 
+    class="base-element-table"
+    :style="{
+      top: elementInfo.top + 'px',
+      left: elementInfo.left + 'px',
+      width: elementInfo.width + 'px',
+    }"
+  >
+    <div class="element-content">
+      <StaticTable
+        :data="elementInfo.data"
+        :width="elementInfo.width"
+        :colWidths="elementInfo.colWidths"
+        :outline="elementInfo.outline"
+      />
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType } from 'vue'
+import { PPTTableElement } from '@/types/slides'
+
+import StaticTable from './StaticTable.vue'
+
+export default defineComponent({
+  name: 'base-element-table',
+  components: {
+    StaticTable,
+  },
+  props: {
+    elementInfo: {
+      type: Object as PropType<PPTTableElement>,
+      required: true,
+    },
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.base-element-table {
+  position: absolute;
+}
+
+.element-content {
+  width: 100%;
+  height: 100%;
+  position: relative;
+}
+</style>

+ 12 - 12
src/views/components/element/TableElement/EditableDiv.vue

@@ -1,7 +1,7 @@
 <template>
   <div 
-    class="editable-div"
-    ref="editableDivRef"
+    class="custom-textarea"
+    ref="textareaRef"
     :contenteditable="contenteditable"
     @focus="handleFocus"
     @blur="handleBlur"
@@ -14,7 +14,7 @@
 import { defineComponent, onUnmounted, ref, watch } from 'vue'
 
 export default defineComponent({
-  name: 'editable-div',
+  name: 'custom-textarea',
   props: {
     modelValue: {
       type: String,
@@ -26,27 +26,27 @@ export default defineComponent({
     },
   },
   setup(props, { emit }) {
-    const editableDivRef = ref<HTMLElement>()
+    const textareaRef = ref<HTMLElement>()
     const text = ref('')
     const isFocus = ref(false)
 
     watch(() => props.modelValue, () => {
       if(isFocus.value) return
       text.value = props.modelValue
-      if(editableDivRef.value) editableDivRef.value.innerHTML = props.modelValue
+      if(textareaRef.value) textareaRef.value.innerHTML = props.modelValue
     }, { immediate: true })
 
     const handleInput = () => {
-      if(!editableDivRef.value) return
-      const text = editableDivRef.value.innerHTML
+      if(!textareaRef.value) return
+      const text = textareaRef.value.innerHTML
       emit('update:modelValue', text)
     }
 
     const handleFocus = () => {
       isFocus.value = true
 
-      if(!editableDivRef.value) return
-      editableDivRef.value.onpaste = (e: ClipboardEvent) => {
+      if(!textareaRef.value) return
+      textareaRef.value.onpaste = (e: ClipboardEvent) => {
         e.preventDefault()
         if(!e.clipboardData) return
 
@@ -60,15 +60,15 @@ export default defineComponent({
 
     const handleBlur = () => {
       isFocus.value = false
-      if(editableDivRef.value) editableDivRef.value.onpaste = null
+      if(textareaRef.value) textareaRef.value.onpaste = null
     }
 
     onUnmounted(() => {
-      if(editableDivRef.value) editableDivRef.value.onpaste = null
+      if(textareaRef.value) textareaRef.value.onpaste = null
     })
 
     return {
-      editableDivRef,
+      textareaRef,
       handleFocus,
       handleInput,
       handleBlur,

+ 80 - 50
src/views/components/element/TableElement/EditableTable.vue

@@ -1,9 +1,9 @@
 <template>
   <div 
     class="editable-table"
-    :style="{ width: width + 'px' }"
+    :style="{ width: totalWidth + 'px' }"
   >
-    <div class="handler">
+    <div class="handler" v-if="editable">
       <div 
         class="drag-line" 
         v-for="(pos, index) in dragLinePosition" 
@@ -16,7 +16,7 @@
     </div>
     <table>
       <colgroup>
-        <col span="1" v-for="(width, index) in colWidths" :key="index" :width="width">
+        <col span="1" v-for="(width, index) in colSizeList" :key="index" :width="width">
       </colgroup>
       <tbody>
         <tr
@@ -29,6 +29,11 @@
               'selected': selectedCells.includes(`${rowIndex}_${colIndex}`) && selectedCells.length > 1,
               'active': activedCell === `${rowIndex}_${colIndex}`,
             }"
+            :style="{
+              borderStyle: outline.style,
+              borderColor: outline.color,
+              borderWidth: outline.width + 'px',
+            }"
             v-for="(cell, colIndex) in rowCells"
             :key="cell.id"
             :rowspan="cell.rowspan"
@@ -39,7 +44,7 @@
             @mouseenter="handleCellMouseenter(rowIndex, colIndex)"
             v-contextmenu="el => contextmenus(el)"
           >
-            <EditableDiv 
+            <CustomTextarea 
               class="cell-text" 
               :class="{ 'active': activedCell === `${rowIndex}_${colIndex}` }"
               :contenteditable="activedCell === `${rowIndex}_${colIndex}` ? 'plaintext-only' : false"
@@ -54,39 +59,48 @@
 </template>
 
 <script lang="ts">
-import { computed, defineComponent, nextTick, onMounted, onUnmounted, PropType, ref } from 'vue'
+import { computed, defineComponent, nextTick, onMounted, onUnmounted, PropType, ref, watch } from 'vue'
 import debounce from 'lodash/debounce'
-import { TableCell } from '@/types/slides'
+import { PPTElementOutline, TableCell } from '@/types/slides'
 import { ContextmenuItem } from '@/components/Contextmenu/types'
 import { KEYS } from '@/configs/hotkey'
 import { createRandomCode } from '@/utils/common'
 
-import EditableDiv from './EditableDiv.vue'
+import CustomTextarea from './CustomTextarea.vue'
+import { useStore } from 'vuex'
+import { State } from '@/store'
 
 export default defineComponent({
   name: 'editable-table',
   components: {
-    EditableDiv,
+    CustomTextarea,
   },
   props: {
     data: {
       type: Array as PropType<TableCell[][]>,
       required: true,
     },
-    outlineWidth: {
+    width: {
       type: Number,
-      default: 1,
+      required: true,
     },
-    outlineColor: {
-      type: String,
-      default: '#41464b',
+    colWidths: {
+      type: Array as PropType<number[]>,
+      required: true,
     },
-    outlineStyle: {
-      type: String,
-      default: 'solid',
+    outline: {
+      type: Object as PropType<PPTElementOutline>,
+      required: true,
+    },
+    editable: {
+      type: Boolean,
+      default: true,
     },
   },
   setup(props, { emit }) {
+    const store = useStore<State>()
+    const canvasScale = computed(() => store.state.canvasScale)
+
     const tableCells = computed<TableCell[][]>({
       get() {
         return props.data
@@ -95,18 +109,33 @@ export default defineComponent({
         emit('change', newData)
       },
     })
+
+    const colSizeList = ref<number[]>([])
+    const totalWidth = computed(() => colSizeList.value.reduce((a, b) => a + b))
+    watch([
+      () => props.colWidths,
+      () => props.width,
+    ], () => {
+      colSizeList.value = props.colWidths.map(item => item * props.width)
+    }, { immediate: true })
     
-    const colWidths = ref([160, 160, 160, 160, 160])
     const isStartSelect = ref(false)
     const startCell = ref<number[]>([])
     const endCell = ref<number[]>([])
+    
+    const removeSelectedCells = () => {
+      startCell.value = []
+      endCell.value = []
+    }
 
-    const width = computed(() => colWidths.value.reduce((a, b) => (a + b)))
+    watch(() => props.editable, () => {
+      if(!props.editable) removeSelectedCells()
+    })
 
     const dragLinePosition = computed(() => {
       const dragLinePosition: number[] = []
-      for(let i = 1; i < colWidths.value.length + 1; i++) {
-        const pos = colWidths.value.slice(0, i).reduce((a, b) => (a + b))
+      for(let i = 1; i < colSizeList.value.length + 1; i++) {
+        const pos = colSizeList.value.slice(0, i).reduce((a, b) => (a + b))
         dragLinePosition.push(pos)
       }
       return dragLinePosition
@@ -207,11 +236,6 @@ export default defineComponent({
 
     const isHideCell = (rowIndex: number, colIndex: number) => hideCells.value.includes(`${rowIndex}_${colIndex}`)
 
-    const removeSelectedCells = () => {
-      startCell.value = []
-      endCell.value = []
-    }
-
     const selectCol = (index: number) => {
       const maxRow = tableCells.value.length - 1
       startCell.value = [0, index]
@@ -274,12 +298,15 @@ export default defineComponent({
         item.splice(colIndex, 1)
         return item
       })
-      colWidths.value.splice(colIndex, 1)
+      colSizeList.value.splice(colIndex, 1)
+      emit('changeColWidths', colSizeList.value)
     }
     
-    const insertRow = (selectedIndex: number, rowIndex: number) => {
+    const insertRow = (rowIndex: number) => {
+      const _tableCells: TableCell[][] = JSON.parse(JSON.stringify(tableCells.value))
+
       const rowCells: TableCell[] = []
-      for(let i = 0; i < tableCells.value[0].length; i++) {
+      for(let i = 0; i < _tableCells[0].length; i++) {
         rowCells.push({
           colspan: 1,
           rowspan: 1,
@@ -288,10 +315,11 @@ export default defineComponent({
         })
       }
 
-      tableCells.value.splice(rowIndex, 0, rowCells)
+      _tableCells.splice(rowIndex, 0, rowCells)
+      tableCells.value = _tableCells
     }
 
-    const insertCol = (selectedIndex: number, colIndex: number) => {
+    const insertCol = (colIndex: number) => {
       tableCells.value = tableCells.value.map(item => {
         const cell = {
           colspan: 1,
@@ -302,7 +330,8 @@ export default defineComponent({
         item.splice(colIndex, 0, cell)
         return item
       })
-      colWidths.value.splice(colIndex, 0, 160)
+      colSizeList.value.splice(colIndex, 0, 100)
+      emit('changeColWidths', colSizeList.value)
     }
     
     const mergeCells = () => {
@@ -336,7 +365,7 @@ export default defineComponent({
       removeSelectedCells()
       let isMouseDown = true
 
-      const originWidth = colWidths.value[colIndex]
+      const originWidth = colSizeList.value[colIndex]
       const startPageX = e.pageX
 
       const minWidth = 50
@@ -344,15 +373,17 @@ export default defineComponent({
       document.onmousemove = e => {
         if(!isMouseDown) return
         
-        const moveX = e.pageX - startPageX
+        const moveX = (e.pageX - startPageX) / canvasScale.value
         const width = originWidth + moveX < minWidth ? minWidth : Math.round(originWidth + moveX)
 
-        colWidths.value[colIndex] = width
+        colSizeList.value[colIndex] = width
       }
       document.onmouseup = () => {
         isMouseDown = false
         document.onmousemove = null
         document.onmouseup = null
+
+        emit('changeColWidths', colSizeList.value)
       }
     }
 
@@ -384,14 +415,14 @@ export default defineComponent({
 
       const nextCell = getNextCell(nextRow, nextCol)
       if(!nextCell) {
-        insertRow(nextRow, nextRow + 1)
+        insertRow(nextRow + 1)
         startCell.value = [nextRow + 1, 0]
       }
       else startCell.value = nextCell
 
       nextTick(() => {
         const textRef = document.querySelector('.cell-text.active') as HTMLInputElement
-        textRef.focus()
+        if(textRef) textRef.focus()
       })
     }
 
@@ -453,15 +484,15 @@ export default defineComponent({
         {
           text: '插入列',
           children: [
-            { text: '到左侧', handler: () => insertCol(colIndex, colIndex) },
-            { text: '到右侧', handler: () => insertCol(colIndex, colIndex + 1) },
+            { text: '到左侧', handler: () => insertCol(colIndex) },
+            { text: '到右侧', handler: () => insertCol(colIndex + 1) },
           ],
         },
         {
           text: '插入行',
           children: [
-            { text: '到上方', handler: () => insertRow(rowIndex, rowIndex) },
-            { text: '到下方', handler: () => insertRow(rowIndex, rowIndex + 1) },
+            { text: '到上方', handler: () => insertRow(rowIndex) },
+            { text: '到下方', handler: () => insertRow(rowIndex + 1) },
           ],
         },
         {
@@ -506,10 +537,10 @@ export default defineComponent({
     }, 300, { trailing: true })
 
     return {
-      width,
       dragLinePosition,
       tableCells,
-      colWidths,
+      colSizeList,
+      totalWidth,
       hideCells,
       selectedCells,
       activedCell,
@@ -537,16 +568,19 @@ table {
   table-layout: fixed;
   border-collapse: collapse;
   border-spacing: 0;
+  border: 0;
   word-wrap: break-word;
   user-select: none;
 
+  tr {
+    height: 36px;
+  }
+
   .cell {
-    padding: 5px;
     position: relative;
     white-space: normal;
     word-wrap: break-word;
     vertical-align: middle;
-    border: 1px solid #d9d9d9;
     cursor: default;
 
     &.selected::after {
@@ -561,7 +595,8 @@ table {
   }
 
   .cell-text {
-    min-height: 22px;
+    min-height: 32px;
+    padding: 5px;
     border: 0;
     outline: 0;
     line-height: 1.5;
@@ -572,11 +607,6 @@ table {
     &.active {
       user-select: text;
     }
-
-    ::selection {
-      background-color: rgba(27, 110, 232, 0.3);
-      color: inherit;
-    }
   }
 }
 

+ 143 - 0
src/views/components/element/TableElement/StaticTable.vue

@@ -0,0 +1,143 @@
+<template>
+  <div 
+    class="static-table"
+    :style="{ width: totalWidth + 'px' }"
+  >
+    <table>
+      <colgroup>
+        <col span="1" v-for="(width, index) in colSizeList" :key="index" :width="width">
+      </colgroup>
+      <tbody>
+        <tr
+          v-for="(rowCells, rowIndex) in data" 
+          :key="rowIndex"
+        >
+          <td 
+            class="cell"
+            :style="{
+              borderStyle: outline.style,
+              borderColor: outline.color,
+              borderWidth: outline.width + 'px',
+            }"
+            v-for="(cell, colIndex) in rowCells"
+            :key="cell.id"
+            :rowspan="cell.rowspan"
+            :colspan="cell.colspan"
+            v-show="!hideCells.includes(`${rowIndex}_${colIndex}`)"
+          >
+            <div class="cell-text" v-html="cell.content" />
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, PropType, ref, watch } from 'vue'
+import { PPTElementOutline, TableCell } from '@/types/slides'
+
+export default defineComponent({
+  name: 'static-table',
+  props: {
+    data: {
+      type: Array as PropType<TableCell[][]>,
+      required: true,
+    },
+    width: {
+      type: Number,
+      required: true,
+    },
+    colWidths: {
+      type: Array as PropType<number[]>,
+      required: true,
+    },
+    outline: {
+      type: Object as PropType<PPTElementOutline>,
+      required: true,
+    },
+    editable: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  setup(props) {
+    const colSizeList = ref<number[]>([])
+    const totalWidth = computed(() => colSizeList.value.reduce((a, b) => a + b))
+
+    watch([
+      () => props.colWidths,
+      () => props.width,
+    ], () => {
+      colSizeList.value = props.colWidths.map(item => item * props.width)
+    }, { immediate: true })
+
+    const hideCells = computed(() => {
+      const hideCells = []
+      
+      for(let i = 0; i < props.data.length; i++) {
+        const rowCells = props.data[i]
+
+        for(let j = 0; j < rowCells.length; j++) {
+          const cell = rowCells[j]
+          
+          if(cell.colspan > 1 || cell.rowspan > 1) {
+            for(let row = i; row < i + cell.rowspan; row++) {
+              for(let col = row === i ? j + 1 : j; col < j + cell.colspan; col++) {
+                hideCells.push(`${row}_${col}`)
+              }
+            }
+          }
+        }
+      }
+      return hideCells
+    })
+
+    return {
+      colSizeList,
+      totalWidth,
+      hideCells,
+    }
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+.static-table {
+  position: relative;
+  user-select: none;
+}
+table {
+  width: 100%;
+  position: relative;
+  table-layout: fixed;
+  border-collapse: collapse;
+  border-spacing: 0;
+  border: 0;
+  word-wrap: break-word;
+  user-select: none;
+
+  tr {
+    height: 36px;
+  }
+
+  .cell {
+    position: relative;
+    white-space: normal;
+    word-wrap: break-word;
+    vertical-align: middle;
+    cursor: default;
+  }
+
+  .cell-text {
+    min-height: 32px;
+    padding: 5px;
+    border: 0;
+    outline: 0;
+    line-height: 1.5;
+    font-size: 14px;
+    user-select: none;
+    cursor: text;
+  }
+}
+</style>

+ 113 - 9
src/views/components/element/TableElement/index.vue

@@ -1,45 +1,61 @@
 <template>
-  <div class="editable-element-shape"
+  <div 
+    class="editable-element-table"
+    ref="elementRef"
     :class="{ 'lock': elementInfo.lock }"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',
       width: elementInfo.width + 'px',
     }"
-    @mousedown="$event => handleSelectElement($event)"
   >
     <div 
       class="element-content" 
       v-contextmenu="contextmenus"
     >
+      <div 
+        class="table-mask" 
+        v-if="!editable"
+        @dblclick="editable = true"
+        @mousedown="$event => handleSelectElement($event)"
+      ></div>
       <EditableTable 
+        @mousedown.stop
         :data="elementInfo.data"
+        :width="elementInfo.width"
         :colWidths="elementInfo.colWidths"
+        :outline="elementInfo.outline"
+        :editable="editable"
         @change="data => updateTableCells(data)"
+        @changeColWidths="widths => updateColWidths(widths)"
       />
     </div>
   </div>
 </template>
 
 <script lang="ts">
-import { defineComponent, PropType } from 'vue'
-import { PPTShapeElement, TableCell } from '@/types/slides'
+import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, watch } from 'vue'
+import { useStore } from 'vuex'
+import { MutationTypes, State } from '@/store'
+import { PPTTableElement, TableCell } from '@/types/slides'
+import emitter, { EmitterEvents } from '@/utils/emitter'
 import { ContextmenuItem } from '@/components/Contextmenu/types'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
 
 import EditableTable from './EditableTable.vue'
 
 export default defineComponent({
-  name: 'editable-element-shape',
+  name: 'editable-element-table',
   components: {
     EditableTable,
   },
   props: {
     elementInfo: {
-      type: Object as PropType<PPTShapeElement>,
+      type: Object as PropType<PPTTableElement>,
       required: true,
     },
     selectElement: {
-      type: Function as PropType<(e: MouseEvent, element: PPTShapeElement, canMove?: boolean) => void>,
+      type: Function as PropType<(e: MouseEvent, element: PPTTableElement, canMove?: boolean) => void>,
       required: true,
     },
     contextmenus: {
@@ -47,27 +63,107 @@ export default defineComponent({
     },
   },
   setup(props) {
+    const store = useStore<State>()
+    const { addHistorySnapshot } = useHistorySnapshot()
+
     const handleSelectElement = (e: MouseEvent) => {
       if(props.elementInfo.lock) return
       e.stopPropagation()
 
       props.selectElement(e, props.elementInfo)
     }
+    const editable = ref(false)
+    const handleElementId = computed(() => store.state.handleElementId)
+
+    watch(handleElementId, () => {
+      if(handleElementId.value !== props.elementInfo.id) editable.value = false
+    })
+
+    watch(editable, () => {
+      store.commit(MutationTypes.SET_DISABLE_HOTKEYS_STATE, editable.value)
+    })
+    
+    const elementRef = ref<HTMLElement>()
+
+    const isScaling = ref(false)
+    const realHeightCache = ref(-1)
+
+    const scaleElementStateListener = (state: boolean) => {
+      isScaling.value = state
+
+      if(state) editable.value = false
+
+      if(!state && realHeightCache.value !== -1) {
+        store.commit(MutationTypes.UPDATE_ELEMENT, {
+          id: props.elementInfo.id,
+          props: { height: realHeightCache.value },
+        })
+        realHeightCache.value = -1
+      }
+    }
+
+    emitter.on(EmitterEvents.SCALE_ELEMENT_STATE, state => scaleElementStateListener(state))
+    onUnmounted(() => {
+      emitter.off(EmitterEvents.SCALE_ELEMENT_STATE, state => scaleElementStateListener(state))
+    })
+
+    const updateTableElementHeight = (entries: ResizeObserverEntry[]) => {
+      const contentRect = entries[0].contentRect
+      if(!elementRef.value) return
+
+      const realHeight = contentRect.height
+
+      if(props.elementInfo.height !== realHeight) {
+        if(!isScaling.value) {
+          store.commit(MutationTypes.UPDATE_ELEMENT, {
+            id: props.elementInfo.id,
+            props: { height: realHeight },
+          })
+        }
+        else realHeightCache.value = realHeight
+      }
+    }
+
+    const resizeObserver = new ResizeObserver(updateTableElementHeight)
+
+    onMounted(() => {
+      if(elementRef.value) resizeObserver.observe(elementRef.value)
+    })
+    onUnmounted(() => {
+      if(elementRef.value) resizeObserver.unobserve(elementRef.value)
+    })
 
     const updateTableCells = (data: TableCell[][]) => {
-      console.log(data)
+      store.commit(MutationTypes.UPDATE_ELEMENT, {
+        id: props.elementInfo.id, 
+        props: { data },
+      })
+      addHistorySnapshot()
+    }
+    const updateColWidths = (widths: number[]) => {
+      const width = widths.reduce((a, b) => a + b)
+      const colWidths = widths.map(item => item / width)
+
+      store.commit(MutationTypes.UPDATE_ELEMENT, {
+        id: props.elementInfo.id, 
+        props: { width, colWidths },
+      })
+      addHistorySnapshot()
     }
 
     return {
+      elementRef,
       handleSelectElement,
       updateTableCells,
+      updateColWidths,
+      editable,
     }
   },
 })
 </script>
 
 <style lang="scss" scoped>
-.editable-element-shape {
+.editable-element-table {
   position: absolute;
   cursor: move;
 
@@ -81,4 +177,12 @@ export default defineComponent({
   height: 100%;
   position: relative;
 }
+.table-mask {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 10;
+}
 </style>