@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 +13 -11
- package/src/components/ACell.vue +6 -13
- package/src/components/ARow.vue +16 -15
- package/src/components/ATable.vue +57 -62
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/atable",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
37
|
-
"@stonecrop/utilities": "0.2.
|
|
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-
|
|
46
|
-
"@vitest/ui": "^1.
|
|
47
|
-
"@vue/test-utils": "^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": "^
|
|
55
|
+
"jsdom": "^24.0.0",
|
|
54
56
|
"typescript": "^5.4.5",
|
|
55
57
|
"vite": "^5.2.9",
|
|
56
|
-
"vitest": "^1.
|
|
58
|
+
"vitest": "^1.6.0",
|
|
57
59
|
"vue-router": "^4",
|
|
58
|
-
"@stonecrop/aform": "0.2.
|
|
60
|
+
"@stonecrop/aform": "0.2.17"
|
|
59
61
|
},
|
|
60
62
|
"peerDependencies": {
|
|
61
|
-
"@stonecrop/aform": "0.2.
|
|
63
|
+
"@stonecrop/aform": "0.2.17"
|
|
62
64
|
},
|
|
63
65
|
"publishConfig": {
|
|
64
66
|
"access": "public"
|
package/src/components/ACell.vue
CHANGED
|
@@ -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.
|
|
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.
|
|
152
|
-
currentData = cell.value.
|
|
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
|
}
|
package/src/components/ARow.vue
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<tr ref="rowEl" :tabindex="tabIndex" v-show="
|
|
2
|
+
<tr ref="rowEl" :tabindex="tabIndex" v-show="isRowVisible" class="table-row">
|
|
3
3
|
<!-- render numbered/tree view index -->
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
|
149
|
-
if (!
|
|
150
|
-
if
|
|
151
|
-
|
|
152
|
-
|
|
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('
|
|
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
|
-
|
|
168
|
-
|
|
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
|
}
|