@stonecrop/atable 0.31.0 → 0.33.0
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/assets/index.css +804 -1
- package/dist/atable.js +2809 -1944
- package/dist/atable.js.map +1 -1
- package/dist/src/components/ACell.vue.d.ts +17 -0
- package/dist/src/components/ACell.vue.d.ts.map +1 -0
- package/dist/src/components/AGanttCell.vue.d.ts +34 -0
- package/dist/src/components/AGanttCell.vue.d.ts.map +1 -0
- package/dist/src/components/AGanttConnection.vue.d.ts +13 -0
- package/dist/src/components/AGanttConnection.vue.d.ts.map +1 -0
- package/dist/src/components/ARow.vue.d.ts +2693 -0
- package/dist/src/components/ARow.vue.d.ts.map +1 -0
- package/dist/src/components/ARowActions.vue.d.ts +16 -0
- package/dist/src/components/ARowActions.vue.d.ts.map +1 -0
- package/dist/src/components/ATable.vue.d.ts +13358 -0
- package/dist/src/components/ATable.vue.d.ts.map +1 -0
- package/dist/src/components/ATableColumnFilter.vue.d.ts +11 -0
- package/dist/src/components/ATableColumnFilter.vue.d.ts.map +1 -0
- package/dist/src/components/ATableHeader.vue.d.ts +20 -0
- package/dist/src/components/ATableHeader.vue.d.ts.map +1 -0
- package/dist/src/components/ATableLoading.vue.d.ts +14 -0
- package/dist/src/components/ATableLoading.vue.d.ts.map +1 -0
- package/dist/src/components/ATableLoadingBar.vue.d.ts +14 -0
- package/dist/src/components/ATableLoadingBar.vue.d.ts.map +1 -0
- package/dist/src/components/ATableModal.vue.d.ts +18 -0
- package/dist/src/components/ATableModal.vue.d.ts.map +1 -0
- package/dist/src/components/ATablePaginationFooter.vue.d.ts +17 -0
- package/dist/src/components/ATablePaginationFooter.vue.d.ts.map +1 -0
- package/dist/src/icons/index.d.ts +14 -7
- package/dist/src/icons/index.d.ts.map +1 -1
- package/dist/{tsdoc-metadata.json → src/tsdoc-metadata.json} +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/package.json +35 -29
- package/dist/atable.d.ts +0 -3560
- package/dist/atable.tsbuildinfo +0 -1
- package/dist/icons/stonecrop-ui-icon-add.svg +0 -5
- package/dist/icons/stonecrop-ui-icon-delete.svg +0 -5
- package/dist/icons/stonecrop-ui-icon-duplicate.svg +0 -5
- package/dist/icons/stonecrop-ui-icon-insert-above.svg +0 -15
- package/dist/icons/stonecrop-ui-icon-insert-below.svg +0 -15
- package/dist/icons/stonecrop-ui-icon-move.svg +0 -4
- package/dist/icons/stonecrop-ui-icon-open.svg +0 -5
- package/dist/src/composables/table-pagination.js +0 -108
- package/dist/src/icons/index.js +0 -32
- package/dist/src/index.js +0 -33
- package/dist/src/linkSearchableText.js +0 -44
- package/dist/src/resolveFilterType.js +0 -27
- package/dist/src/schemaToColumns.js +0 -59
- package/dist/src/stores/table.js +0 -833
- package/dist/src/types/index.js +0 -0
- package/dist/src/utils.js +0 -39
- package/src/components/ACell.vue +0 -410
- package/src/components/AGanttCell.vue +0 -638
- package/src/components/AGanttConnection.vue +0 -164
- package/src/components/ARow.vue +0 -251
- package/src/components/ARowActions.vue +0 -407
- package/src/components/ATable.vue +0 -538
- package/src/components/ATableColumnFilter.vue +0 -216
- package/src/components/ATableHeader.vue +0 -141
- package/src/components/ATableLoading.vue +0 -86
- package/src/components/ATableLoadingBar.vue +0 -81
- package/src/components/ATableModal.vue +0 -52
- package/src/components/ATablePaginationFooter.vue +0 -100
- package/src/composables/table-pagination.ts +0 -172
- package/src/icons/index.ts +0 -38
- package/src/icons/stonecrop-ui-icon-add.svg +0 -5
- package/src/icons/stonecrop-ui-icon-delete.svg +0 -5
- package/src/icons/stonecrop-ui-icon-duplicate.svg +0 -5
- package/src/icons/stonecrop-ui-icon-insert-above.svg +0 -15
- package/src/icons/stonecrop-ui-icon-insert-below.svg +0 -15
- package/src/icons/stonecrop-ui-icon-move.svg +0 -4
- package/src/icons/stonecrop-ui-icon-open.svg +0 -5
- package/src/index.ts +0 -62
- package/src/linkSearchableText.ts +0 -42
- package/src/resolveFilterType.ts +0 -32
- package/src/schemaToColumns.ts +0 -67
- package/src/shims-vue.d.ts +0 -10
- package/src/stores/table.ts +0 -978
- package/src/types/index.ts +0 -794
- package/src/utils.ts +0 -40
package/dist/src/types/index.js
DELETED
|
File without changes
|
package/dist/src/utils.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export const isHtmlString = (htmlString) => {
|
|
2
|
-
const $document = new DOMParser().parseFromString(htmlString, 'text/html');
|
|
3
|
-
return Array.from($document.body.childNodes).some(node => node.nodeType === 1);
|
|
4
|
-
};
|
|
5
|
-
export const generateHash = (length = 8) => {
|
|
6
|
-
return Array.from({ length }, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Render a composite quantity value (`{ qty, uom }`, see `QuantityValue` in `@stonecrop/aform`)
|
|
10
|
-
* as `"<qty> <uom>"`. Shared by the two paths a quantity column can reach formatting through:
|
|
11
|
-
* the `format` that `schemaToColumns` attaches, and the table store's category-based default.
|
|
12
|
-
*/
|
|
13
|
-
export const formatQuantity = (value) => {
|
|
14
|
-
if (value === null || value === undefined)
|
|
15
|
-
return '';
|
|
16
|
-
if (typeof value === 'object') {
|
|
17
|
-
const qty = value.qty ?? '';
|
|
18
|
-
const uom = value.uom ?? '';
|
|
19
|
-
return uom ? `${qty} ${uom}`.trim() : String(qty);
|
|
20
|
-
}
|
|
21
|
-
return String(value);
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Render a composite currency value (`{ amount, currency }`, see `CurrencyValue` in
|
|
25
|
-
* `@stonecrop/aform`) as `"<amount> <currency>"`. `currency` is an AFormLinkValue FK reference —
|
|
26
|
-
* its `displayText` is preferred, falling back to `id`. Shared by the two paths a currency column
|
|
27
|
-
* can reach formatting through: the `format` that `schemaToColumns` attaches, and the table
|
|
28
|
-
* store's category-based default.
|
|
29
|
-
*/
|
|
30
|
-
export const formatCurrency = (value) => {
|
|
31
|
-
if (value === null || value === undefined)
|
|
32
|
-
return '';
|
|
33
|
-
if (typeof value === 'object') {
|
|
34
|
-
const amount = value.amount ?? '';
|
|
35
|
-
const currency = value.currency?.displayText ?? value.currency?.id ?? '';
|
|
36
|
-
return currency ? `${amount} ${currency}`.trim() : String(amount);
|
|
37
|
-
}
|
|
38
|
-
return String(value);
|
|
39
|
-
};
|
package/src/components/ACell.vue
DELETED
|
@@ -1,410 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<td
|
|
3
|
-
ref="cell"
|
|
4
|
-
:data-colindex="colIndex"
|
|
5
|
-
:data-rowindex="rowIndex"
|
|
6
|
-
:data-editable="column.edit"
|
|
7
|
-
:contenteditable="isContentEditable"
|
|
8
|
-
:tabindex="tabIndex"
|
|
9
|
-
:spellcheck="false"
|
|
10
|
-
:style="cellStyle"
|
|
11
|
-
class="atable-cell"
|
|
12
|
-
:class="cellClasses"
|
|
13
|
-
@focus="onFocus"
|
|
14
|
-
@paste="updateCellData"
|
|
15
|
-
@input="debouncedUpdateCellData"
|
|
16
|
-
@click="onCellClick">
|
|
17
|
-
<component :is="column.cellComponent" v-if="column.cellComponent" v-bind="cellComponentBindings" />
|
|
18
|
-
<component :is="'ABadge'" v-else-if="badgeFromFormat" v-bind="badgeFromFormat" presentation="cell-fill" />
|
|
19
|
-
<component :is="'ABadge'" v-else-if="badgeFromOptions" v-bind="badgeFromOptions" />
|
|
20
|
-
<span v-else-if="isHtmlValue" v-html="renderedValue" />
|
|
21
|
-
<span v-else>{{ renderedValue }}</span>
|
|
22
|
-
</td>
|
|
23
|
-
</template>
|
|
24
|
-
|
|
25
|
-
<script setup lang="ts">
|
|
26
|
-
import { KeypressHandlers, defaultKeypressHandlers, useKeyboardNav } from '@stonecrop/utilities'
|
|
27
|
-
import { isBadgeDescriptor, hasBadgeOptions } from '@stonecrop/schema'
|
|
28
|
-
import { useDebounceFn, useElementBounding } from '@vueuse/core'
|
|
29
|
-
import { computed, type CSSProperties, onMounted, ref, useTemplateRef, nextTick } from 'vue'
|
|
30
|
-
|
|
31
|
-
import { createTableStore, getIndent } from '../stores/table'
|
|
32
|
-
import { isHtmlString } from '../utils'
|
|
33
|
-
|
|
34
|
-
const {
|
|
35
|
-
colIndex,
|
|
36
|
-
rowIndex,
|
|
37
|
-
store,
|
|
38
|
-
addNavigation = true,
|
|
39
|
-
tabIndex = 0,
|
|
40
|
-
pinned = false,
|
|
41
|
-
debounce = 300,
|
|
42
|
-
} = defineProps<{
|
|
43
|
-
colIndex: number
|
|
44
|
-
rowIndex: number
|
|
45
|
-
store: ReturnType<typeof createTableStore>
|
|
46
|
-
addNavigation?: boolean | KeypressHandlers
|
|
47
|
-
tabIndex?: number
|
|
48
|
-
pinned?: boolean
|
|
49
|
-
debounce?: number
|
|
50
|
-
}>()
|
|
51
|
-
|
|
52
|
-
const cellRef = useTemplateRef<HTMLTableCellElement>('cell')
|
|
53
|
-
|
|
54
|
-
// keep a shallow copy of the original cell value for comparison
|
|
55
|
-
const originalData = store.getCellData(colIndex, rowIndex)
|
|
56
|
-
const currentData = ref('')
|
|
57
|
-
const cellModified = ref(false)
|
|
58
|
-
|
|
59
|
-
const column = store.columns[colIndex]
|
|
60
|
-
const row = store.rows[rowIndex]
|
|
61
|
-
|
|
62
|
-
const textAlign = column.align || 'center'
|
|
63
|
-
const cellWidth = column.width || '40ch'
|
|
64
|
-
|
|
65
|
-
const displayValue = computed(() => store.getCellDisplayValue(colIndex, rowIndex))
|
|
66
|
-
|
|
67
|
-
// Resolved display text for Link columns with bare string IDs.
|
|
68
|
-
// null = not yet resolved or not applicable; non-null overrides displayValue.
|
|
69
|
-
const resolvedText = ref<string | null>(null)
|
|
70
|
-
|
|
71
|
-
onMounted(() => {
|
|
72
|
-
if (!column.linkDoctype) return
|
|
73
|
-
const raw = store.getCellData(colIndex, rowIndex)
|
|
74
|
-
|
|
75
|
-
// Server returns link fields as { id, displayText } objects.
|
|
76
|
-
if (raw !== null && raw !== undefined && typeof raw === 'object') {
|
|
77
|
-
const obj = raw as Record<string, unknown>
|
|
78
|
-
const display = obj.displayText ?? obj.id
|
|
79
|
-
if (typeof display === 'string' || typeof display === 'number') {
|
|
80
|
-
resolvedText.value = String(display)
|
|
81
|
-
}
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Bare string ID — call the resolver if available.
|
|
86
|
-
const resolver = store.linkResolver
|
|
87
|
-
if (!resolver || typeof raw !== 'string' || raw === '') return
|
|
88
|
-
void resolver(column.linkDoctype, raw).then(text => {
|
|
89
|
-
if (text != null) resolvedText.value = text
|
|
90
|
-
return text
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
const renderedValue = computed(() => resolvedText.value ?? displayValue.value)
|
|
95
|
-
|
|
96
|
-
const badgeFromFormat = computed(() => {
|
|
97
|
-
if (column.cellComponent) return undefined
|
|
98
|
-
const value = renderedValue.value
|
|
99
|
-
return isBadgeDescriptor(value) ? value : undefined
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
const badgeFromOptions = computed(() => {
|
|
103
|
-
if (column.cellComponent || badgeFromFormat.value) return undefined
|
|
104
|
-
if (!hasBadgeOptions(column.options)) return undefined
|
|
105
|
-
return {
|
|
106
|
-
value: store.getCellData(colIndex, rowIndex),
|
|
107
|
-
options: column.options,
|
|
108
|
-
presentation: 'cell-fill' as const,
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const usesBadgeDisplay = computed(
|
|
113
|
-
() => !!column.cellComponent || badgeFromFormat.value !== undefined || hasBadgeOptions(column.options)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
const isContentEditable = computed(() => column.edit && !usesBadgeDisplay.value)
|
|
117
|
-
|
|
118
|
-
const cellComponentBindings = computed(() => {
|
|
119
|
-
const props = { ...column.cellComponentProps }
|
|
120
|
-
if (column.cellComponent !== 'ABadge') {
|
|
121
|
-
return { ...props, value: renderedValue.value }
|
|
122
|
-
}
|
|
123
|
-
if (isBadgeDescriptor(renderedValue.value)) {
|
|
124
|
-
return { presentation: 'cell-fill' as const, ...props, ...renderedValue.value }
|
|
125
|
-
}
|
|
126
|
-
// ABadge resolves its own label, so it needs the badge map as well as the value — and the value
|
|
127
|
-
// has to be the stored one, because badge maps are keyed on that rather than on formatted text.
|
|
128
|
-
return {
|
|
129
|
-
presentation: 'cell-fill' as const,
|
|
130
|
-
options: column.options,
|
|
131
|
-
...props,
|
|
132
|
-
value: store.getCellData(colIndex, rowIndex),
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
const isHtmlValue = computed(() => {
|
|
137
|
-
// TODO: check if display value is a native DOM element
|
|
138
|
-
return typeof renderedValue.value === 'string' ? isHtmlString(renderedValue.value) : false
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
const cellStyle = computed((): CSSProperties => {
|
|
142
|
-
return {
|
|
143
|
-
textAlign,
|
|
144
|
-
width: cellWidth,
|
|
145
|
-
fontWeight: !cellModified.value ? 'inherit' : 'bold',
|
|
146
|
-
paddingLeft: getIndent(colIndex, store.display[rowIndex]?.indent),
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
const cellClasses = computed(() => {
|
|
151
|
-
return {
|
|
152
|
-
'sticky-column': pinned,
|
|
153
|
-
'cell-modified': cellModified.value,
|
|
154
|
-
'atable-cell--badge-fill': usesBadgeDisplay.value,
|
|
155
|
-
}
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
const onCellClick = () => {
|
|
159
|
-
// First, select all text if the cell is editable
|
|
160
|
-
selectAllText()
|
|
161
|
-
|
|
162
|
-
// Then handle modal display (original showModal behavior)
|
|
163
|
-
showModal()
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const showModal = () => {
|
|
167
|
-
const { left, bottom, width, height } = useElementBounding(cellRef)
|
|
168
|
-
|
|
169
|
-
if (column.mask) {
|
|
170
|
-
// TODO: add masking to cell values
|
|
171
|
-
// column.mask(event)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (column.modalComponent) {
|
|
175
|
-
store.$patch(state => {
|
|
176
|
-
state.modal.visible = true
|
|
177
|
-
state.modal.colIndex = colIndex
|
|
178
|
-
state.modal.rowIndex = rowIndex
|
|
179
|
-
// Snapshot the cell's box as it was when the modal opened. These are refs, and assigning
|
|
180
|
-
// them raw happened to work only because reactive reads unwrap refs — which also left the
|
|
181
|
-
// stored values live, tracking the cell for as long as the modal stayed open.
|
|
182
|
-
state.modal.left = left.value
|
|
183
|
-
state.modal.bottom = bottom.value
|
|
184
|
-
state.modal.width = width.value
|
|
185
|
-
state.modal.height = height.value
|
|
186
|
-
state.modal.cell = cellRef.value
|
|
187
|
-
|
|
188
|
-
if (typeof column.modalComponent === 'function') {
|
|
189
|
-
// `table` is a computed, so Pinia exposes it as a getter — it is not on `$state`, which
|
|
190
|
-
// is what $patch hands back. Reading it off the store is what passes a real table.
|
|
191
|
-
state.modal.component = column.modalComponent({ table: store.table, row, column })
|
|
192
|
-
} else {
|
|
193
|
-
state.modal.component = column.modalComponent
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
state.modal.componentProps = column.modalComponentExtraProps
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (addNavigation) {
|
|
202
|
-
let handlers = {
|
|
203
|
-
...defaultKeypressHandlers,
|
|
204
|
-
'keydown.f2': showModal,
|
|
205
|
-
'keydown.alt.up': showModal,
|
|
206
|
-
'keydown.alt.down': showModal,
|
|
207
|
-
'keydown.alt.left': showModal,
|
|
208
|
-
'keydown.alt.right': showModal,
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (typeof addNavigation === 'object') {
|
|
212
|
-
handlers = {
|
|
213
|
-
...handlers,
|
|
214
|
-
...addNavigation,
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
useKeyboardNav([
|
|
219
|
-
{
|
|
220
|
-
selectors: cellRef,
|
|
221
|
-
handlers: handlers,
|
|
222
|
-
},
|
|
223
|
-
])
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// const updateData = (event: Event) => {
|
|
227
|
-
// if (event) {
|
|
228
|
-
// // custom components need to handle their own updateData, this is the default
|
|
229
|
-
// if (!column.component) {
|
|
230
|
-
// store.setCellData(colIndex, rowIndex, cell.value.innerHTML)
|
|
231
|
-
// }
|
|
232
|
-
// cellModified.value = true
|
|
233
|
-
// }
|
|
234
|
-
// }
|
|
235
|
-
|
|
236
|
-
const selectAllText = () => {
|
|
237
|
-
if (cellRef.value && column.edit) {
|
|
238
|
-
// Use the Selection API to select all text content in the contenteditable cell
|
|
239
|
-
const selection = window.getSelection()
|
|
240
|
-
if (selection) {
|
|
241
|
-
try {
|
|
242
|
-
const range = document.createRange()
|
|
243
|
-
if (range.selectNodeContents) {
|
|
244
|
-
range.selectNodeContents(cellRef.value)
|
|
245
|
-
selection.removeAllRanges()
|
|
246
|
-
selection.addRange(range)
|
|
247
|
-
}
|
|
248
|
-
} catch {
|
|
249
|
-
// Fallback for environments where Range API is not fully supported
|
|
250
|
-
// This is expected in some test environments
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const onFocus = () => {
|
|
257
|
-
if (cellRef.value) {
|
|
258
|
-
currentData.value = cellRef.value.textContent!
|
|
259
|
-
// Select all text when the cell receives focus
|
|
260
|
-
selectAllText()
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const saveCursorPosition = () => {
|
|
265
|
-
try {
|
|
266
|
-
const selection = window.getSelection()
|
|
267
|
-
if (selection && selection.rangeCount > 0 && cellRef.value) {
|
|
268
|
-
const range = selection.getRangeAt(0)
|
|
269
|
-
// Save the offset from the start of the cell
|
|
270
|
-
const preCaretRange = range.cloneRange()
|
|
271
|
-
if (preCaretRange.selectNodeContents && preCaretRange.setEnd) {
|
|
272
|
-
preCaretRange.selectNodeContents(cellRef.value)
|
|
273
|
-
preCaretRange.setEnd(range.endContainer, range.endOffset)
|
|
274
|
-
return preCaretRange.toString().length
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} catch {
|
|
278
|
-
// Fallback for environments where Selection API is not fully supported
|
|
279
|
-
}
|
|
280
|
-
return 0
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const restoreCursorPosition = (position: number) => {
|
|
284
|
-
if (!cellRef.value) return
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
const selection = window.getSelection()
|
|
288
|
-
if (!selection) return
|
|
289
|
-
|
|
290
|
-
let charIndex = 0
|
|
291
|
-
const walker = document.createTreeWalker
|
|
292
|
-
? document.createTreeWalker(cellRef.value, NodeFilter.SHOW_TEXT, null)
|
|
293
|
-
: null
|
|
294
|
-
|
|
295
|
-
if (!walker) return
|
|
296
|
-
|
|
297
|
-
let node: Node | null
|
|
298
|
-
let range: Range | null = null
|
|
299
|
-
|
|
300
|
-
while ((node = walker.nextNode())) {
|
|
301
|
-
const textNode = node as Text
|
|
302
|
-
const nextCharIndex = charIndex + textNode.textContent.length
|
|
303
|
-
|
|
304
|
-
if (position <= nextCharIndex) {
|
|
305
|
-
range = document.createRange()
|
|
306
|
-
if (range.setStart && range.setEnd) {
|
|
307
|
-
range.setStart(textNode, position - charIndex)
|
|
308
|
-
range.setEnd(textNode, position - charIndex)
|
|
309
|
-
break
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
charIndex = nextCharIndex
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (range && selection.removeAllRanges && selection.addRange) {
|
|
316
|
-
selection.removeAllRanges()
|
|
317
|
-
selection.addRange(range)
|
|
318
|
-
}
|
|
319
|
-
} catch {
|
|
320
|
-
// Fallback for environments where DOM APIs are not fully supported
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const updateCellData = (payload: Event) => {
|
|
325
|
-
if (!column.edit) return
|
|
326
|
-
|
|
327
|
-
const target = payload.target as HTMLTableCellElement
|
|
328
|
-
if (target.textContent === currentData.value) {
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Save cursor position before updating
|
|
333
|
-
const cursorPosition = saveCursorPosition()
|
|
334
|
-
|
|
335
|
-
currentData.value = target.textContent!
|
|
336
|
-
|
|
337
|
-
// only apply changes if the cell value has changed after being mounted
|
|
338
|
-
if (column.format) {
|
|
339
|
-
cellModified.value = target.textContent !== store.getFormattedValue(colIndex, rowIndex, originalData)
|
|
340
|
-
// TODO: need to setup reverse format function?
|
|
341
|
-
store.setCellText(colIndex, rowIndex, target.textContent)
|
|
342
|
-
} else {
|
|
343
|
-
cellModified.value = target.textContent !== originalData
|
|
344
|
-
store.setCellData(colIndex, rowIndex, target.textContent)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Use nextTick to restore cursor position after Vue's reactive updates but before browser repaint
|
|
348
|
-
void nextTick().then(() => {
|
|
349
|
-
return restoreCursorPosition(cursorPosition)
|
|
350
|
-
})
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const debouncedUpdateCellData = useDebounceFn(updateCellData, debounce)
|
|
354
|
-
|
|
355
|
-
defineExpose({
|
|
356
|
-
currentData,
|
|
357
|
-
})
|
|
358
|
-
</script>
|
|
359
|
-
|
|
360
|
-
<style>
|
|
361
|
-
.atable-cell {
|
|
362
|
-
border-radius: 0px;
|
|
363
|
-
box-sizing: border-box;
|
|
364
|
-
margin: 0px;
|
|
365
|
-
outline: none;
|
|
366
|
-
box-shadow: none;
|
|
367
|
-
color: var(--sc-cell-text-color);
|
|
368
|
-
overflow: hidden;
|
|
369
|
-
padding-left: 0.5ch !important;
|
|
370
|
-
padding-right: 0.5ch;
|
|
371
|
-
padding-top: var(--sc-atable-row-padding);
|
|
372
|
-
padding-bottom: var(--sc-atable-row-padding);
|
|
373
|
-
border-spacing: 0px;
|
|
374
|
-
border-collapse: collapse;
|
|
375
|
-
overflow: hidden;
|
|
376
|
-
text-overflow: ellipsis;
|
|
377
|
-
order: 1;
|
|
378
|
-
white-space: nowrap;
|
|
379
|
-
max-width: 40ch;
|
|
380
|
-
border-top: 1px solid var(--sc-row-border-color);
|
|
381
|
-
margin-left: 1px;
|
|
382
|
-
}
|
|
383
|
-
.atable-cell a {
|
|
384
|
-
color: var(--sc-cell-text-color);
|
|
385
|
-
text-decoration: none;
|
|
386
|
-
}
|
|
387
|
-
.atable-cell:focus,
|
|
388
|
-
.atable-cell:focus-within {
|
|
389
|
-
background-color: var(--sc-focus-cell-background);
|
|
390
|
-
outline-width: var(--sc-atable-cell-border-width);
|
|
391
|
-
outline-style: solid;
|
|
392
|
-
outline-offset: calc(var(--sc-atable-cell-border-width) * -1);
|
|
393
|
-
outline-color: var(--sc-focus-cell-outline);
|
|
394
|
-
box-shadow: none;
|
|
395
|
-
overflow: hidden;
|
|
396
|
-
text-wrap: nowrap;
|
|
397
|
-
box-sizing: border-box;
|
|
398
|
-
}
|
|
399
|
-
.cell-modified {
|
|
400
|
-
font-weight: bold;
|
|
401
|
-
font-style: italic;
|
|
402
|
-
}
|
|
403
|
-
.cell-modified-highlight {
|
|
404
|
-
background-color: var(--sc-cell-changed-color);
|
|
405
|
-
}
|
|
406
|
-
.atable-cell--badge-fill {
|
|
407
|
-
padding: 0 !important;
|
|
408
|
-
margin-left: 0;
|
|
409
|
-
}
|
|
410
|
-
</style>
|