vue2-excel-grid 1.7.2 → 2.0.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/README.md +1 -1
- package/dist/vue2-excel-grid.esm.js +1193 -587
- package/dist/vue2-excel-grid.umd.js +3 -3
- package/package.json +1 -1
- package/src/components/EgLocalSyncModal.vue +99 -48
- package/src/components/ExcelGrid.vue +258 -122
- package/src/utils/dataHash.js +139 -0
- package/src/utils/i18n.js +32 -24
- package/src/utils/idbStore.js +122 -20
package/package.json
CHANGED
|
@@ -6,17 +6,30 @@
|
|
|
6
6
|
<button type="button" class="eg-lsm-close" @click="$emit('close')" :title="t.localSyncClose">✕</button>
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
|
-
<div class="eg-lsm-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
<div v-if="!hasVersions" class="eg-lsm-empty">{{ t.localSyncEmpty }}</div>
|
|
10
|
+
<div v-else class="eg-lsm-body">
|
|
11
|
+
<ul class="eg-lsm-version-list">
|
|
12
|
+
<li
|
|
13
|
+
v-for="v in versions"
|
|
14
|
+
:key="v.id"
|
|
15
|
+
class="eg-lsm-version-item"
|
|
16
|
+
:class="{ 'eg-lsm-version-item-active': v.id === selectedId }"
|
|
17
|
+
@click="$emit('select', v.id)"
|
|
18
|
+
>
|
|
19
|
+
<span class="eg-lsm-version-date">{{ formatDateTime(v.createdAt) }}</span>
|
|
20
|
+
<span class="eg-lsm-version-rows">{{ v.rowCount }} {{ t.localSyncRows }}</span>
|
|
21
|
+
<span v-if="v.updatedAt !== v.createdAt" class="eg-lsm-version-touched">
|
|
22
|
+
{{ t.localSyncTouchedAt }}: {{ formatDateTime(v.updatedAt) }}
|
|
23
|
+
</span>
|
|
24
|
+
</li>
|
|
25
|
+
</ul>
|
|
26
|
+
|
|
27
|
+
<div class="eg-lsm-preview">
|
|
28
|
+
<div v-if="!selectedVersion" class="eg-lsm-preview-loading">{{ t.localSyncLoading }}</div>
|
|
29
|
+
<div v-else class="eg-lsm-grid-wrap">
|
|
17
30
|
<component
|
|
18
31
|
:is="gridOptions"
|
|
19
|
-
:data="
|
|
32
|
+
:data="selectedVersion.data"
|
|
20
33
|
:columns="columns"
|
|
21
34
|
:height="360"
|
|
22
35
|
:locale="locale"
|
|
@@ -25,21 +38,21 @@
|
|
|
25
38
|
:toolbar="false"
|
|
26
39
|
/>
|
|
27
40
|
</div>
|
|
28
|
-
</
|
|
41
|
+
</div>
|
|
29
42
|
</div>
|
|
30
43
|
|
|
31
44
|
<div class="eg-lsm-footer">
|
|
32
|
-
<template v-if="
|
|
45
|
+
<template v-if="confirmingClearId">
|
|
33
46
|
<span class="eg-lsm-confirm-text">{{ t.localSyncConfirmClear }}</span>
|
|
34
|
-
<button type="button" class="eg-lsm-btn" @click="
|
|
47
|
+
<button type="button" class="eg-lsm-btn" @click="confirmingClearId = null">{{ t.localSyncConfirmNo }}</button>
|
|
35
48
|
<button type="button" class="eg-lsm-btn eg-lsm-btn-danger" @click="onConfirmClear">{{ t.localSyncConfirmYes }}</button>
|
|
36
49
|
</template>
|
|
37
50
|
<template v-else>
|
|
38
51
|
<button
|
|
39
52
|
type="button"
|
|
40
53
|
class="eg-lsm-btn eg-lsm-btn-danger"
|
|
41
|
-
:disabled="!
|
|
42
|
-
@click="
|
|
54
|
+
:disabled="!selectedId"
|
|
55
|
+
@click="confirmingClearId = selectedId"
|
|
43
56
|
>{{ t.localSyncClear }}</button>
|
|
44
57
|
<button
|
|
45
58
|
type="button"
|
|
@@ -50,9 +63,9 @@
|
|
|
50
63
|
<button
|
|
51
64
|
type="button"
|
|
52
65
|
class="eg-lsm-btn eg-lsm-btn-accent"
|
|
53
|
-
:disabled="!
|
|
66
|
+
:disabled="!selectedId"
|
|
54
67
|
:title="t.localSyncRestoreHint"
|
|
55
|
-
@click="$emit('restore')"
|
|
68
|
+
@click="$emit('restore', selectedId)"
|
|
56
69
|
>{{ t.localSyncRestore }}</button>
|
|
57
70
|
</template>
|
|
58
71
|
</div>
|
|
@@ -62,20 +75,17 @@
|
|
|
62
75
|
|
|
63
76
|
<script>
|
|
64
77
|
// "Dumb" presentation component for ExcelGrid.vue's localSyncKey feature —
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
// from src/index.js (same "internal-only"
|
|
68
|
-
// EgCheckbox.vue) — purpose-built for
|
|
78
|
+
// lists the locally saved IndexedDB *versions* (see src/utils/idbStore.js)
|
|
79
|
+
// for the current key, previews the selected one, and lets the host Restore
|
|
80
|
+
// or Delete it. Not exported from src/index.js (same "internal-only"
|
|
81
|
+
// convention as EgDatePicker.vue/EgCheckbox.vue) — purpose-built for
|
|
82
|
+
// ExcelGrid.vue, not meant for standalone use.
|
|
69
83
|
//
|
|
70
84
|
// The preview itself is a real, nested ExcelGrid instance (locked/read-only,
|
|
71
|
-
// no toolbar)
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// grid's own resolver). `gridOptions` is ExcelGrid.vue's own `$options`,
|
|
76
|
-
// passed down by the parent instance instead of importing ExcelGrid.vue
|
|
77
|
-
// here directly — that import would be circular (ExcelGrid.vue -> this file
|
|
78
|
-
// -> ExcelGrid.vue).
|
|
85
|
+
// no toolbar) — see the comment on ExcelGridComponent in ExcelGrid.vue for
|
|
86
|
+
// why `gridOptions` is passed down as a plain component definition instead
|
|
87
|
+
// of importing ExcelGrid.vue here (that would be circular) or using the
|
|
88
|
+
// runtime-contaminated `this.$options`.
|
|
79
89
|
import { getMessages } from '../utils/i18n.js'
|
|
80
90
|
|
|
81
91
|
export default {
|
|
@@ -84,30 +94,33 @@ export default {
|
|
|
84
94
|
visible: { type: Boolean, default: false },
|
|
85
95
|
gridOptions: { type: Object, required: true },
|
|
86
96
|
columns: { type: Array, default: () => [] },
|
|
87
|
-
|
|
97
|
+
versions: { type: Array, default: () => [] }, // [{id, createdAt, updatedAt, rowCount}]
|
|
98
|
+
selectedId: { type: String, default: null },
|
|
99
|
+
selectedVersion: { type: Object, default: null }, // {id, data, createdAt, updatedAt} | null
|
|
88
100
|
locale: { type: String, default: 'en' },
|
|
89
101
|
theme: { type: String, default: 'excel' }
|
|
90
102
|
},
|
|
91
103
|
data() {
|
|
92
|
-
return {
|
|
104
|
+
return { confirmingClearId: null }
|
|
93
105
|
},
|
|
94
106
|
computed: {
|
|
95
107
|
t() { return getMessages(this.locale) },
|
|
96
|
-
|
|
97
|
-
if (!this.snapshot) return ''
|
|
98
|
-
return new Date(this.snapshot.updatedAt).toLocaleString()
|
|
99
|
-
}
|
|
108
|
+
hasVersions() { return this.versions.length > 0 }
|
|
100
109
|
},
|
|
101
110
|
watch: {
|
|
102
|
-
// A freshly opened modal (or a
|
|
111
|
+
// A freshly opened modal (or a version that just got deleted/restored)
|
|
103
112
|
// should never inherit a stale "are you sure?" state from last time.
|
|
104
|
-
visible(val) { if (!val) this.
|
|
105
|
-
|
|
113
|
+
visible(val) { if (!val) this.confirmingClearId = null },
|
|
114
|
+
versions() { this.confirmingClearId = null }
|
|
106
115
|
},
|
|
107
116
|
methods: {
|
|
117
|
+
formatDateTime(ts) {
|
|
118
|
+
return ts ? new Date(ts).toLocaleString() : ''
|
|
119
|
+
},
|
|
108
120
|
onConfirmClear() {
|
|
109
|
-
|
|
110
|
-
this
|
|
121
|
+
const id = this.confirmingClearId
|
|
122
|
+
this.confirmingClearId = null
|
|
123
|
+
this.$emit('clear', id)
|
|
111
124
|
}
|
|
112
125
|
}
|
|
113
126
|
}
|
|
@@ -160,24 +173,62 @@ export default {
|
|
|
160
173
|
.eg-lsm-close:hover {
|
|
161
174
|
background: var(--eg-color-option-hover-bg, var(--eg-color-option-hover-bg-default));
|
|
162
175
|
}
|
|
163
|
-
.eg-lsm-body {
|
|
164
|
-
padding: 16px 18px;
|
|
165
|
-
overflow-y: auto;
|
|
166
|
-
flex: 1;
|
|
167
|
-
min-height: 0;
|
|
168
|
-
}
|
|
169
176
|
.eg-lsm-empty {
|
|
170
177
|
text-align: center;
|
|
171
178
|
padding: 32px 12px;
|
|
172
179
|
color: var(--eg-color-text-muted, var(--eg-color-text-muted-default));
|
|
173
180
|
font-size: var(--eg-font-size, var(--eg-font-size-default));
|
|
174
181
|
}
|
|
175
|
-
.eg-lsm-
|
|
182
|
+
.eg-lsm-body {
|
|
176
183
|
display: flex;
|
|
177
|
-
|
|
184
|
+
flex: 1;
|
|
185
|
+
min-height: 0;
|
|
186
|
+
overflow: hidden;
|
|
187
|
+
}
|
|
188
|
+
.eg-lsm-version-list {
|
|
189
|
+
list-style: none;
|
|
190
|
+
margin: 0;
|
|
191
|
+
padding: 8px;
|
|
192
|
+
width: 220px;
|
|
193
|
+
flex-shrink: 0;
|
|
194
|
+
overflow-y: auto;
|
|
195
|
+
border-right: 1px solid var(--eg-color-border-cell, var(--eg-color-border-cell-default));
|
|
196
|
+
}
|
|
197
|
+
.eg-lsm-version-item {
|
|
198
|
+
display: flex;
|
|
199
|
+
flex-direction: column;
|
|
200
|
+
gap: 2px;
|
|
201
|
+
padding: 8px 10px;
|
|
202
|
+
border-radius: var(--eg-radius, var(--eg-radius-default));
|
|
203
|
+
cursor: pointer;
|
|
178
204
|
font-size: 12px;
|
|
205
|
+
color: var(--eg-color-text, var(--eg-color-text-default));
|
|
206
|
+
}
|
|
207
|
+
.eg-lsm-version-item:hover {
|
|
208
|
+
background: var(--eg-color-option-hover-bg, var(--eg-color-option-hover-bg-default));
|
|
209
|
+
}
|
|
210
|
+
.eg-lsm-version-item-active {
|
|
211
|
+
background: var(--eg-color-accent-soft, var(--eg-color-accent-soft-default));
|
|
212
|
+
}
|
|
213
|
+
.eg-lsm-version-date {
|
|
214
|
+
font-weight: 600;
|
|
215
|
+
}
|
|
216
|
+
.eg-lsm-version-rows,
|
|
217
|
+
.eg-lsm-version-touched {
|
|
179
218
|
color: var(--eg-color-text-secondary, var(--eg-color-text-secondary-default));
|
|
180
|
-
|
|
219
|
+
font-size: 11px;
|
|
220
|
+
}
|
|
221
|
+
.eg-lsm-preview {
|
|
222
|
+
flex: 1;
|
|
223
|
+
min-width: 0;
|
|
224
|
+
padding: 16px 18px;
|
|
225
|
+
overflow: auto;
|
|
226
|
+
}
|
|
227
|
+
.eg-lsm-preview-loading {
|
|
228
|
+
text-align: center;
|
|
229
|
+
padding: 32px 12px;
|
|
230
|
+
color: var(--eg-color-text-muted, var(--eg-color-text-muted-default));
|
|
231
|
+
font-size: var(--eg-font-size, var(--eg-font-size-default));
|
|
181
232
|
}
|
|
182
233
|
.eg-lsm-grid-wrap {
|
|
183
234
|
border: 1px solid var(--eg-color-border-cell, var(--eg-color-border-cell-default));
|