@stonecrop/atable 0.4.32 → 0.4.33
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/dist/atable.js +749 -701
- package/dist/atable.js.map +1 -1
- package/dist/atable.umd.cjs +2 -2
- package/dist/atable.umd.cjs.map +1 -1
- package/package.json +3 -3
- package/src/components/ACell.vue +100 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/atable",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"@vueuse/core": "^13.6.0",
|
|
43
43
|
"pinia": "^3.0.3",
|
|
44
44
|
"vue": "^3.5.18",
|
|
45
|
-
"@stonecrop/themes": "0.4.
|
|
46
|
-
"@stonecrop/utilities": "0.4.
|
|
45
|
+
"@stonecrop/themes": "0.4.33",
|
|
46
|
+
"@stonecrop/utilities": "0.4.33"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@microsoft/api-documenter": "^7.26.31",
|
package/src/components/ACell.vue
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
@focus="onFocus"
|
|
12
12
|
@paste="updateCellData"
|
|
13
13
|
@input="debouncedUpdateCellData"
|
|
14
|
-
@click="
|
|
14
|
+
@click="onCellClick"
|
|
15
15
|
class="atable-cell"
|
|
16
16
|
:class="cellClasses">
|
|
17
17
|
<component
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<script setup lang="ts">
|
|
28
28
|
import { KeypressHandlers, defaultKeypressHandlers, useKeyboardNav } from '@stonecrop/utilities'
|
|
29
29
|
import { useDebounceFn, useElementBounding } from '@vueuse/core'
|
|
30
|
-
import { computed, type CSSProperties, ref, useTemplateRef } from 'vue'
|
|
30
|
+
import { computed, type CSSProperties, ref, useTemplateRef, nextTick } from 'vue'
|
|
31
31
|
|
|
32
32
|
import { createTableStore } from '../stores/table'
|
|
33
33
|
import { isHtmlString } from '../utils'
|
|
@@ -85,6 +85,14 @@ const cellClasses = computed(() => {
|
|
|
85
85
|
}
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
+
const onCellClick = () => {
|
|
89
|
+
// First, select all text if the cell is editable
|
|
90
|
+
selectAllText()
|
|
91
|
+
|
|
92
|
+
// Then handle modal display (original showModal behavior)
|
|
93
|
+
showModal()
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
const showModal = () => {
|
|
89
97
|
const { left, bottom, width, height } = useElementBounding(cellRef)
|
|
90
98
|
|
|
@@ -153,9 +161,91 @@ if (addNavigation) {
|
|
|
153
161
|
// }
|
|
154
162
|
// }
|
|
155
163
|
|
|
164
|
+
const selectAllText = () => {
|
|
165
|
+
if (cellRef.value && column.edit) {
|
|
166
|
+
// Use the Selection API to select all text content in the contenteditable cell
|
|
167
|
+
const selection = window.getSelection()
|
|
168
|
+
if (selection) {
|
|
169
|
+
try {
|
|
170
|
+
const range = document.createRange()
|
|
171
|
+
if (range.selectNodeContents) {
|
|
172
|
+
range.selectNodeContents(cellRef.value)
|
|
173
|
+
selection.removeAllRanges()
|
|
174
|
+
selection.addRange(range)
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
// Fallback for environments where Range API is not fully supported
|
|
178
|
+
// This is expected in some test environments
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
156
184
|
const onFocus = () => {
|
|
157
185
|
if (cellRef.value) {
|
|
158
186
|
currentData.value = cellRef.value.textContent!
|
|
187
|
+
// Select all text when the cell receives focus
|
|
188
|
+
selectAllText()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const saveCursorPosition = () => {
|
|
193
|
+
try {
|
|
194
|
+
const selection = window.getSelection()
|
|
195
|
+
if (selection && selection.rangeCount > 0 && cellRef.value) {
|
|
196
|
+
const range = selection.getRangeAt(0)
|
|
197
|
+
// Save the offset from the start of the cell
|
|
198
|
+
const preCaretRange = range.cloneRange()
|
|
199
|
+
if (preCaretRange.selectNodeContents && preCaretRange.setEnd) {
|
|
200
|
+
preCaretRange.selectNodeContents(cellRef.value)
|
|
201
|
+
preCaretRange.setEnd(range.endContainer, range.endOffset)
|
|
202
|
+
return preCaretRange.toString().length
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
// Fallback for environments where Selection API is not fully supported
|
|
207
|
+
}
|
|
208
|
+
return 0
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const restoreCursorPosition = (position: number) => {
|
|
212
|
+
if (!cellRef.value) return
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const selection = window.getSelection()
|
|
216
|
+
if (!selection) return
|
|
217
|
+
|
|
218
|
+
let charIndex = 0
|
|
219
|
+
const walker = document.createTreeWalker
|
|
220
|
+
? document.createTreeWalker(cellRef.value, NodeFilter.SHOW_TEXT, null)
|
|
221
|
+
: null
|
|
222
|
+
|
|
223
|
+
if (!walker) return
|
|
224
|
+
|
|
225
|
+
let node: Node | null
|
|
226
|
+
let range: Range | null = null
|
|
227
|
+
|
|
228
|
+
while ((node = walker.nextNode())) {
|
|
229
|
+
const textNode = node as Text
|
|
230
|
+
const nextCharIndex = charIndex + textNode.textContent!.length
|
|
231
|
+
|
|
232
|
+
if (position <= nextCharIndex) {
|
|
233
|
+
range = document.createRange()
|
|
234
|
+
if (range.setStart && range.setEnd) {
|
|
235
|
+
range.setStart(textNode, position - charIndex)
|
|
236
|
+
range.setEnd(textNode, position - charIndex)
|
|
237
|
+
break
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
charIndex = nextCharIndex
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (range && selection.removeAllRanges && selection.addRange) {
|
|
244
|
+
selection.removeAllRanges()
|
|
245
|
+
selection.addRange(range)
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
// Fallback for environments where DOM APIs are not fully supported
|
|
159
249
|
}
|
|
160
250
|
}
|
|
161
251
|
|
|
@@ -165,6 +255,9 @@ const updateCellData = (payload: Event) => {
|
|
|
165
255
|
return
|
|
166
256
|
}
|
|
167
257
|
|
|
258
|
+
// Save cursor position before updating
|
|
259
|
+
const cursorPosition = saveCursorPosition()
|
|
260
|
+
|
|
168
261
|
currentData.value = target.textContent!
|
|
169
262
|
|
|
170
263
|
// only apply changes if the cell value has changed after being mounted
|
|
@@ -176,6 +269,11 @@ const updateCellData = (payload: Event) => {
|
|
|
176
269
|
cellModified.value = target.textContent !== originalData
|
|
177
270
|
store.setCellData(colIndex, rowIndex, target.textContent)
|
|
178
271
|
}
|
|
272
|
+
|
|
273
|
+
// Use nextTick to restore cursor position after Vue's reactive updates but before browser repaint
|
|
274
|
+
void nextTick().then(() => {
|
|
275
|
+
restoreCursorPosition(cursorPosition)
|
|
276
|
+
})
|
|
179
277
|
}
|
|
180
278
|
|
|
181
279
|
const debouncedUpdateCellData = useDebounceFn(updateCellData, debounce)
|