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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-excel-grid",
3
- "version": "1.7.2",
3
+ "version": "2.0.0",
4
4
  "description": "Excel-style grid component for Vue 2.6.14 with virtual scrolling",
5
5
  "license": "MIT",
6
6
  "author": "Fozilbek Raimberdiyev",
@@ -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-body">
10
- <div v-if="!snapshot" class="eg-lsm-empty">{{ t.localSyncEmpty }}</div>
11
- <template v-else>
12
- <div class="eg-lsm-meta">
13
- <span>{{ t.localSyncSavedAt }}: {{ savedAtText }}</span>
14
- <span>{{ snapshot.data.length }} {{ t.localSyncRows }}</span>
15
- </div>
16
- <div class="eg-lsm-grid-wrap">
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="snapshot.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
- </template>
41
+ </div>
29
42
  </div>
30
43
 
31
44
  <div class="eg-lsm-footer">
32
- <template v-if="confirmingClear">
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="confirmingClear = false">{{ t.localSyncConfirmNo }}</button>
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="!snapshot"
42
- @click="confirmingClear = true"
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="!snapshot"
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
- // shows the last IndexedDB-saved v-model snapshot (see src/utils/idbStore.js)
66
- // and lets the host Restore it back into the grid or delete it. Not exported
67
- // from src/index.js (same "internal-only" convention as EgDatePicker.vue /
68
- // EgCheckbox.vue) — purpose-built for ExcelGrid.vue, not meant for standalone use.
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) rather than a hand-rolled <table> — this gets column
72
- // formatting/select-labels/date-formatting/virtual scrolling for free and
73
- // correctly resolves function-based col.options against the preview grid's
74
- // OWN row indices (impossible for a detached snapshot row via the live
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
- snapshot: { type: Object, default: null }, // { data, updatedAt } | null
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 { confirmingClear: false }
104
+ return { confirmingClearId: null }
93
105
  },
94
106
  computed: {
95
107
  t() { return getMessages(this.locale) },
96
- savedAtText() {
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 snapshot that just got deleted/restored)
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.confirmingClear = false },
105
- snapshot() { this.confirmingClear = false }
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
- this.confirmingClear = false
110
- this.$emit('clear')
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-meta {
182
+ .eg-lsm-body {
176
183
  display: flex;
177
- justify-content: space-between;
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
- margin-bottom: 10px;
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));