@stonecrop/atable 0.2.16 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonecrop/atable",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "author": {
@@ -33,8 +33,8 @@
33
33
  "dependencies": {
34
34
  "uuid": "^9.0.0",
35
35
  "vue": "^3.4.23",
36
- "@stonecrop/themes": "0.2.16",
37
- "@stonecrop/utilities": "0.2.16"
36
+ "@stonecrop/themes": "0.2.17",
37
+ "@stonecrop/utilities": "0.2.17"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@histoire/plugin-vue": "^0.17.17",
@@ -42,23 +42,25 @@
42
42
  "@typescript-eslint/eslint-plugin": "^7.6.0",
43
43
  "@typescript-eslint/parser": "^7.6.0",
44
44
  "@vitejs/plugin-vue": "^5.0.4",
45
- "@vitest/coverage-v8": "^1.5.0",
46
- "@vitest/ui": "^1.5.0",
47
- "@vue/test-utils": "^2.3.2",
45
+ "@vitest/coverage-istanbul": "^1.6.0",
46
+ "@vitest/ui": "^1.6.0",
47
+ "@vue/test-utils": "^2.4.6",
48
+ "@vueuse/components": "^10.9.0",
49
+ "@vueuse/core": "^10.9.0",
48
50
  "cypress": "^12.11.0",
49
- "eslint": "^8.40.0",
50
51
  "eslint-config-prettier": "^8.8.0",
51
52
  "eslint-plugin-vue": "^9.11.1",
53
+ "eslint": "^8.40.0",
52
54
  "histoire": "^0.17.17",
53
- "jsdom": "^22.0.0",
55
+ "jsdom": "^24.0.0",
54
56
  "typescript": "^5.4.5",
55
57
  "vite": "^5.2.9",
56
- "vitest": "^1.5.0",
58
+ "vitest": "^1.6.0",
57
59
  "vue-router": "^4",
58
- "@stonecrop/aform": "0.2.16"
60
+ "@stonecrop/aform": "0.2.17"
59
61
  },
60
62
  "peerDependencies": {
61
- "@stonecrop/aform": "0.2.16"
63
+ "@stonecrop/aform": "0.2.17"
62
64
  },
63
65
  "publishConfig": {
64
66
  "access": "public"
@@ -37,7 +37,6 @@ const props = withDefaults(
37
37
  tableid: string
38
38
  addNavigation?: boolean | KeypressHandlers
39
39
  tabIndex?: number
40
- clickHandler?: (event: MouseEvent) => void
41
40
  }>(),
42
41
  {
43
42
  tabIndex: 0,
@@ -47,8 +46,9 @@ const props = withDefaults(
47
46
 
48
47
  const tableData = inject<TableDataStore>(props.tableid)
49
48
  const cell = ref<HTMLTableCellElement>(null)
49
+ const currentData = ref('')
50
+ const cellModified = ref(false)
50
51
 
51
- let cellModified = ref(false)
52
52
  const displayValue = computed(() => {
53
53
  const data = tableData.cellData<any>(props.colIndex, props.rowIndex)
54
54
  if (tableData.columns[props.colIndex].format) {
@@ -69,12 +69,6 @@ const displayValue = computed(() => {
69
69
  })
70
70
 
71
71
  const handleInput = (event: MouseEvent) => {
72
- // Not sure if click handler is needed anymore?
73
- if (props.clickHandler) {
74
- props.clickHandler(event)
75
- return
76
- }
77
-
78
72
  if (tableData.columns[props.colIndex].mask) {
79
73
  // TODO: add masking to cell values
80
74
  // tableData.columns[props.colIndex].mask(event)
@@ -139,22 +133,21 @@ const cellWidth = computed(() => {
139
133
  return tableData.columns[props.colIndex].width || '40ch'
140
134
  })
141
135
 
142
- let currentData = ''
143
136
  const onFocus = () => {
144
137
  if (cell.value) {
145
- currentData = cell.value.innerText
138
+ currentData.value = cell.value.textContent
146
139
  }
147
140
  }
148
141
 
149
142
  const onChange = () => {
150
143
  if (cell.value) {
151
- if (cell.value.innerHTML !== currentData) {
152
- currentData = cell.value.innerText
144
+ if (cell.value.textContent !== currentData.value) {
145
+ currentData.value = cell.value.textContent
153
146
  cell.value.dispatchEvent(new Event('change'))
154
147
  cellModified.value = true // set display instead
155
148
  if (!tableData.columns[props.colIndex].format) {
156
149
  // TODO: need to setup reverse format function
157
- tableData.setCellData(props.rowIndex, props.colIndex, currentData)
150
+ tableData.setCellData(props.rowIndex, props.colIndex, currentData.value)
158
151
  }
159
152
  }
160
153
  }
@@ -1,17 +1,18 @@
1
1
  <template>
2
- <tr ref="rowEl" :tabindex="tabIndex" v-show="rowVisible()" class="table-row">
2
+ <tr ref="rowEl" :tabindex="tabIndex" v-show="isRowVisible" class="table-row">
3
3
  <!-- render numbered/tree view index -->
4
- <td v-if="tableData.config.view === 'list'" :tabIndex="-1" class="list-index">
5
- {{ rowIndex + 1 }}
6
- </td>
7
- <td
8
- v-else-if="tableData.config.view === 'tree'"
9
- :tabIndex="-1"
10
- class="tree-index"
11
- @click="toggleRowExpand(rowIndex)">
12
- {{ getRowExpandSymbol() }}
13
- </td>
14
- <slot v-else name="indexCell"></slot>
4
+ <slot name="index">
5
+ <td v-if="tableData.config.view === 'list'" :tabIndex="-1" class="list-index">
6
+ {{ rowIndex + 1 }}
7
+ </td>
8
+ <td
9
+ v-else-if="tableData.config.view === 'tree'"
10
+ :tabIndex="-1"
11
+ class="tree-index"
12
+ @click="toggleRowExpand(rowIndex)">
13
+ {{ getRowExpandSymbol() }}
14
+ </td>
15
+ </slot>
15
16
 
16
17
  <!-- render cell content -->
17
18
  <slot></slot>
@@ -20,7 +21,7 @@
20
21
 
21
22
  <script setup lang="ts">
22
23
  import { TableRow } from 'types'
23
- import { inject, ref } from 'vue'
24
+ import { computed, inject, ref } from 'vue'
24
25
  import { type KeypressHandlers, useKeyboardNav, defaultKeypressHandlers } from '@stonecrop/utilities'
25
26
 
26
27
  import TableDataStore from '.'
@@ -67,13 +68,13 @@ const getRowExpandSymbol = () => {
67
68
  }
68
69
  }
69
70
 
70
- const rowVisible = () => {
71
+ const isRowVisible = computed(() => {
71
72
  return (
72
73
  tableData.config.view !== 'tree' ||
73
74
  tableData.display[props.rowIndex].isRoot ||
74
75
  tableData.display[props.rowIndex].open
75
76
  )
76
- }
77
+ })
77
78
 
78
79
  const toggleRowExpand = (rowIndex: number) => {
79
80
  tableData.toggleRowExpand(rowIndex)
@@ -1,5 +1,8 @@
1
1
  <template>
2
- <table class="atable" :style="{ width: tableData.config.fullWidth ? '100%' : 'auto' }">
2
+ <table
3
+ class="atable"
4
+ :style="{ width: tableData.config.fullWidth ? '100%' : 'auto' }"
5
+ v-on-click-outside="closeModal">
3
6
  <slot name="header" :data="tableData">
4
7
  <ATableHeader :columns="tableData.columns" :config="tableData.config" :tableid="tableData.id" />
5
8
  </slot>
@@ -59,6 +62,7 @@
59
62
  <script setup lang="ts">
60
63
  import { v4 } from 'uuid'
61
64
  import { nextTick, provide, watch } from 'vue'
65
+ import { vOnClickOutside } from '@vueuse/components'
62
66
 
63
67
  import { TableColumn, TableConfig, TableRow } from 'types'
64
68
  import TableDataStore from '.'
@@ -85,7 +89,6 @@ const props = withDefaults(
85
89
  const emit = defineEmits(['update:modelValue'])
86
90
 
87
91
  let rows = props.modelValue ? props.modelValue : props.rows
88
-
89
92
  let tableData = new TableDataStore(props.id, props.columns, rows, props.config)
90
93
  provide(tableData.id, tableData)
91
94
 
@@ -97,47 +100,47 @@ watch(
97
100
  { deep: true }
98
101
  )
99
102
 
100
- const formatCell = (event?: KeyboardEvent, column?: TableColumn, cellData?: any) => {
101
- let colIndex: number
102
- const target = event?.target as HTMLTableCellElement
103
- if (event) {
104
- colIndex = target.cellIndex + (tableData.zeroColumn ? -1 : 0)
105
- } else if (column && cellData) {
106
- colIndex = tableData.columns.indexOf(column)
107
- }
108
-
109
- if (!column && 'format' in tableData.columns[colIndex]) {
110
- // TODO: (utils) create helper to extract format from string
111
- const format = tableData.columns[colIndex].format
112
- if (typeof format === 'function') {
113
- return format(target.innerHTML)
114
- } else if (typeof format === 'string') {
115
- // parse format function from string
116
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
117
- const formatFn: (args: any) => any = Function(`"use strict";return (${format})`)()
118
- return formatFn(target.innerHTML)
119
- } else {
120
- return target.innerHTML
121
- }
122
- } else if (cellData && 'format' in column) {
123
- const format = column.format
124
- if (typeof format === 'function') {
125
- return format(cellData)
126
- } else if (typeof format === 'string') {
127
- // parse format function from string
128
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
129
- const formatFn: (args: any) => any = Function(`"use strict";return (${format})`)()
130
- return formatFn(cellData)
131
- } else {
132
- return cellData
133
- }
134
- } else if (cellData && column.type.toLowerCase() in ['int', 'decimal', 'float', 'number', 'percent']) {
135
- return cellData
136
- // TODO: number formatting
137
- } else {
138
- return cellData
139
- }
140
- }
103
+ // const formatCell = (event?: KeyboardEvent, column?: TableColumn, cellData?: any) => {
104
+ // let colIndex: number
105
+ // const target = event?.target as HTMLTableCellElement
106
+ // if (event) {
107
+ // colIndex = target.cellIndex + (tableData.zeroColumn ? -1 : 0)
108
+ // } else if (column && cellData) {
109
+ // colIndex = tableData.columns.indexOf(column)
110
+ // }
111
+
112
+ // if (!column && 'format' in tableData.columns[colIndex]) {
113
+ // // TODO: (utils) create helper to extract format from string
114
+ // const format = tableData.columns[colIndex].format
115
+ // if (typeof format === 'function') {
116
+ // return format(target.innerHTML)
117
+ // } else if (typeof format === 'string') {
118
+ // // parse format function from string
119
+ // // eslint-disable-next-line @typescript-eslint/no-implied-eval
120
+ // const formatFn: (args: any) => any = Function(`"use strict";return (${format})`)()
121
+ // return formatFn(target.innerHTML)
122
+ // } else {
123
+ // return target.innerHTML
124
+ // }
125
+ // } else if (cellData && 'format' in column) {
126
+ // const format = column.format
127
+ // if (typeof format === 'function') {
128
+ // return format(cellData)
129
+ // } else if (typeof format === 'string') {
130
+ // // parse format function from string
131
+ // // eslint-disable-next-line @typescript-eslint/no-implied-eval
132
+ // const formatFn: (args: any) => any = Function(`"use strict";return (${format})`)()
133
+ // return formatFn(cellData)
134
+ // } else {
135
+ // return cellData
136
+ // }
137
+ // } else if (cellData && column.type.toLowerCase() in ['int', 'decimal', 'float', 'number', 'percent']) {
138
+ // return cellData
139
+ // // TODO: number formatting
140
+ // } else {
141
+ // return cellData
142
+ // }
143
+ // }
141
144
 
142
145
  // const moveCursorToEnd = (target: HTMLElement) => {
143
146
  // target.focus()
@@ -145,17 +148,17 @@ const formatCell = (event?: KeyboardEvent, column?: TableColumn, cellData?: any)
145
148
  // document.getSelection().collapseToEnd()
146
149
  // }
147
150
 
148
- const clickOutside = (event: MouseEvent) => {
149
- if (!tableData.modal.parent?.contains(event.target as HTMLElement)) {
150
- if (tableData.modal.visible) {
151
- // call set data
152
- tableData.modal.visible = false
153
- }
151
+ const closeModal = (event: MouseEvent) => {
152
+ if (!(event.target instanceof Node)) {
153
+ // if the target is not a node, it's probably a custom click event to Document or Window
154
+ // err on the side of closing the modal in that case
155
+ if (tableData.modal.visible) tableData.modal.visible = false
156
+ } else if (!tableData.modal.parent?.contains(event.target)) {
157
+ if (tableData.modal.visible) tableData.modal.visible = false
154
158
  }
155
159
  }
156
160
 
157
- window.addEventListener('click', clickOutside)
158
- window.addEventListener('keydown', (event: KeyboardEvent) => {
161
+ window.addEventListener('keydown', async (event: KeyboardEvent) => {
159
162
  if (event.key === 'Escape') {
160
163
  if (tableData.modal.visible) {
161
164
  tableData.modal.visible = false
@@ -163,17 +166,9 @@ window.addEventListener('keydown', (event: KeyboardEvent) => {
163
166
  // focus on the parent cell again
164
167
  const $parent = tableData.modal.parent
165
168
  if ($parent) {
166
- // wait for the modal to close
167
- void nextTick().then(() => {
168
- // for some reason, the parent is not immediately visible in the DOM;
169
- // re-fetching the cell to add focus instead
170
- const rowIndex = $parent.dataset.rowindex
171
- const colIndex = $parent.dataset.colindex
172
- const $parentCell = document.querySelectorAll(`[data-rowindex='${rowIndex}'][data-colindex='${colIndex}']`)
173
- if ($parentCell) {
174
- ;($parentCell[0] as HTMLTableCellElement).focus()
175
- }
176
- })
169
+ // wait for the modal to close before focusing
170
+ await nextTick()
171
+ $parent.focus()
177
172
  }
178
173
  }
179
174
  }