vue2-excel-grid 2.1.0 → 2.2.1
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
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
{{ opt.label }}
|
|
58
58
|
</div>
|
|
59
59
|
<div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
|
|
60
|
+
<div v-if="clearable && selectedOpt" class="eg-select-footer">
|
|
61
|
+
<button class="eg-select-btn eg-select-btn--clear" tabindex="-1" @mousedown.prevent="pickClear">{{ t.clear }}</button>
|
|
62
|
+
</div>
|
|
60
63
|
</template>
|
|
61
64
|
|
|
62
65
|
<!-- Options panel in standalone mode (floating dropdown below trigger) -->
|
|
@@ -97,6 +100,9 @@
|
|
|
97
100
|
{{ opt.label }}
|
|
98
101
|
</div>
|
|
99
102
|
<div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
|
|
103
|
+
<div v-if="clearable && selectedOpt" class="eg-select-footer">
|
|
104
|
+
<button class="eg-select-btn eg-select-btn--clear" tabindex="-1" @mousedown.prevent="pickClear">{{ t.clear }}</button>
|
|
105
|
+
</div>
|
|
100
106
|
</div>
|
|
101
107
|
|
|
102
108
|
<div v-if="hoverTooltip" class="eg-option-tooltip" :style="hoverTooltipStyle">{{ hoverTooltip.text }}</div>
|
|
@@ -129,7 +135,8 @@ export default {
|
|
|
129
135
|
dropdownOnly: { type: Boolean, default: false },
|
|
130
136
|
triggerRect: { type: Object, default: null },
|
|
131
137
|
size: { type: String, default: 'middle' },
|
|
132
|
-
locale: { type: String, default: 'en' }
|
|
138
|
+
locale: { type: String, default: 'en' },
|
|
139
|
+
clearable: { type: Boolean, default: true }
|
|
133
140
|
},
|
|
134
141
|
data() {
|
|
135
142
|
return {
|
|
@@ -239,6 +246,11 @@ export default {
|
|
|
239
246
|
this.highlight = -1
|
|
240
247
|
}
|
|
241
248
|
},
|
|
249
|
+
// Reuses pickOption's write path (same input/select emit + dropdown-close
|
|
250
|
+
// handling) — a clear is just "pick the empty option", not a separate flow.
|
|
251
|
+
pickClear() {
|
|
252
|
+
this.pickOption({ value: '', label: '' })
|
|
253
|
+
},
|
|
242
254
|
onSearch(e) {
|
|
243
255
|
this._hideTooltip()
|
|
244
256
|
this.searchQuery = e.target.value
|
|
@@ -535,6 +547,29 @@ export default {
|
|
|
535
547
|
margin-top: 3px;
|
|
536
548
|
}
|
|
537
549
|
|
|
550
|
+
/* "Clear" footer — same recipe as EgDatePicker.vue's .eg-dp-footer/.eg-dp-btn,
|
|
551
|
+
just a single right-aligned button (no "Today" equivalent here). Also
|
|
552
|
+
duplicated in ExcelGrid.vue's own <style> — see the comment above
|
|
553
|
+
.eg-select-wrapper there for why, and keep both copies token-identical. */
|
|
554
|
+
.eg-select-footer {
|
|
555
|
+
display: flex;
|
|
556
|
+
justify-content: flex-end;
|
|
557
|
+
padding: 4px 6px 6px;
|
|
558
|
+
border-top: 1px solid var(--eg-color-border, var(--eg-color-border-default));
|
|
559
|
+
}
|
|
560
|
+
.eg-select-btn {
|
|
561
|
+
background: none;
|
|
562
|
+
border: 1px solid var(--eg-color-border, var(--eg-color-border-default));
|
|
563
|
+
border-radius: var(--eg-radius, var(--eg-radius-default));
|
|
564
|
+
padding: 4px 10px;
|
|
565
|
+
cursor: pointer;
|
|
566
|
+
font-size: 12px;
|
|
567
|
+
color: var(--eg-color-text, var(--eg-color-text-default));
|
|
568
|
+
}
|
|
569
|
+
.eg-select-btn:hover { background: var(--eg-color-option-hover-bg, var(--eg-color-option-hover-bg-default)); }
|
|
570
|
+
.eg-select-btn--clear { color: var(--eg-color-danger, #c62828); border-color: var(--eg-color-danger, #f1b0b0); }
|
|
571
|
+
.eg-select-btn--clear:hover { background: #fff0f0; }
|
|
572
|
+
|
|
538
573
|
/* Themed option/trigger tooltip — same recipe as .eg-select-dropdown
|
|
539
574
|
(cell-bg/border/radius) plus .eg-validation-tooltip's shadow, so it reads
|
|
540
575
|
as part of the grid's own chrome in every built-in theme instead of a
|
|
@@ -63,6 +63,9 @@
|
|
|
63
63
|
{{ opt.label }}
|
|
64
64
|
</div>
|
|
65
65
|
<div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
|
|
66
|
+
<div v-if="clearable && selectedLabel != null" class="eg-select-footer">
|
|
67
|
+
<button class="eg-select-btn eg-select-btn--clear" tabindex="-1" @mousedown.prevent="pickClear">{{ t.clear }}</button>
|
|
68
|
+
</div>
|
|
66
69
|
</template>
|
|
67
70
|
|
|
68
71
|
<!-- Options panel in standalone mode -->
|
|
@@ -110,6 +113,9 @@
|
|
|
110
113
|
{{ opt.label }}
|
|
111
114
|
</div>
|
|
112
115
|
<div v-if="!filteredOptions.length" class="eg-select-empty">{{ t.noOptions }}</div>
|
|
116
|
+
<div v-if="clearable && selectedLabel != null" class="eg-select-footer">
|
|
117
|
+
<button class="eg-select-btn eg-select-btn--clear" tabindex="-1" @mousedown.prevent="pickClear">{{ t.clear }}</button>
|
|
118
|
+
</div>
|
|
113
119
|
</div>
|
|
114
120
|
|
|
115
121
|
<div v-if="hoverTooltip" class="eg-option-tooltip" :style="hoverTooltipStyle">{{ hoverTooltip.text }}</div>
|
|
@@ -140,7 +146,8 @@ export default {
|
|
|
140
146
|
dropdownOnly: { type: Boolean, default: false },
|
|
141
147
|
triggerRect: { type: Object, default: null },
|
|
142
148
|
size: { type: String, default: 'middle' },
|
|
143
|
-
locale: { type: String, default: 'en' }
|
|
149
|
+
locale: { type: String, default: 'en' },
|
|
150
|
+
clearable: { type: Boolean, default: true }
|
|
144
151
|
},
|
|
145
152
|
data() {
|
|
146
153
|
return {
|
|
@@ -269,6 +276,11 @@ export default {
|
|
|
269
276
|
this.highlight = -1
|
|
270
277
|
}
|
|
271
278
|
},
|
|
279
|
+
// Reuses pickOption's write path (same input/select emit + dropdown-close
|
|
280
|
+
// handling) — a clear is just "pick the empty option", not a separate flow.
|
|
281
|
+
pickClear() {
|
|
282
|
+
this.pickOption({ value: '', label: '' })
|
|
283
|
+
},
|
|
272
284
|
toggleNode(idx) {
|
|
273
285
|
const opt = this.flatOptions[idx]
|
|
274
286
|
if (!opt || !opt.hasChildren) return
|
|
@@ -854,6 +854,7 @@
|
|
|
854
854
|
:dropdown-only="true"
|
|
855
855
|
:size="internalSize"
|
|
856
856
|
:locale="locale"
|
|
857
|
+
:clearable="selectDropdown.clearable"
|
|
857
858
|
@select="onComponentSelect"
|
|
858
859
|
@close="closeSelectDropdown"
|
|
859
860
|
/>
|
|
@@ -871,6 +872,7 @@
|
|
|
871
872
|
:dropdown-only="true"
|
|
872
873
|
:size="internalSize"
|
|
873
874
|
:locale="locale"
|
|
875
|
+
:clearable="selectDropdown.clearable"
|
|
874
876
|
@select="onComponentSelect"
|
|
875
877
|
@close="closeSelectDropdown"
|
|
876
878
|
/>
|
|
@@ -1290,6 +1292,19 @@ const ExcelGridComponent = {
|
|
|
1290
1292
|
/** Extra custom toolbar buttons: [{ key, icon, title, onClick(event, gridApi) }] */
|
|
1291
1293
|
toolbarButtons: { type: Array, default: () => [] },
|
|
1292
1294
|
|
|
1295
|
+
/**
|
|
1296
|
+
* Print (`printGrid()`/toolbar "Print" tugmasi) chiqishida jadval tepasida,
|
|
1297
|
+
* markazda ko'rinadigan sarlavha. `null` (standart) — sarlavha umuman
|
|
1298
|
+
* render qilinmaydi.
|
|
1299
|
+
* @property {String} title - sarlavha matni (bo'sh/undefined bo'lsa render qilinmaydi)
|
|
1300
|
+
* @property {Object} [style] - Vue :style uslubidagi obyekt (camelCase),
|
|
1301
|
+
* standart markazlashtirilgan/qalin uslub USTIGA qo'shiladi
|
|
1302
|
+
* @property {String} [class] - qo'shimcha CSS klass nomi(lari)
|
|
1303
|
+
* @example
|
|
1304
|
+
* <excel-grid :print-title="{ title: 'Talabalar ro\'yxati', style: { color: '#1a73e8' } }" />
|
|
1305
|
+
*/
|
|
1306
|
+
printTitle: { type: Object, default: null },
|
|
1307
|
+
|
|
1293
1308
|
/**
|
|
1294
1309
|
* Locks the entire grid: blocks all edits (typing, paste, fill, delete,
|
|
1295
1310
|
* checkbox, select/treeSelect) and structural changes (resize, column
|
|
@@ -4298,7 +4313,8 @@ const ExcelGridComponent = {
|
|
|
4298
4313
|
rawOptions: this.resolveOptionsArray(col, ri, ci),
|
|
4299
4314
|
currentValue,
|
|
4300
4315
|
rect: rectObj,
|
|
4301
|
-
enableSearch: !!col.enableSearch
|
|
4316
|
+
enableSearch: !!col.enableSearch,
|
|
4317
|
+
clearable: col.clearable !== false
|
|
4302
4318
|
}
|
|
4303
4319
|
},
|
|
4304
4320
|
|
|
@@ -4330,7 +4346,10 @@ const ExcelGridComponent = {
|
|
|
4330
4346
|
const { row, col } = this.selectDropdown
|
|
4331
4347
|
const colDef = this.leafCols[col]
|
|
4332
4348
|
if (colDef) {
|
|
4333
|
-
|
|
4349
|
+
// Clearing (value === '') always goes through, same as onDatePick()'s
|
|
4350
|
+
// empty-string bypass — "no selection" is a valid state regardless of
|
|
4351
|
+
// what cellValidator requires of a real picked value.
|
|
4352
|
+
if (value !== '' && !this.isCellValueValid(row, col, value)) {
|
|
4334
4353
|
this._showInvalidCell(row, col, value)
|
|
4335
4354
|
this.closeSelectDropdown()
|
|
4336
4355
|
return
|
|
@@ -6092,7 +6111,8 @@ const ExcelGridComponent = {
|
|
|
6092
6111
|
return String(this.getCellDisplayValue(ci, this.getCellValue(ri, ci), ri))
|
|
6093
6112
|
},
|
|
6094
6113
|
showFooter: this.hasFooter,
|
|
6095
|
-
getFooterText: ci => this.getColumnTotalDisplay(ci)
|
|
6114
|
+
getFooterText: ci => this.getColumnTotalDisplay(ci),
|
|
6115
|
+
printTitle: this.printTitle
|
|
6096
6116
|
})
|
|
6097
6117
|
this._openPrintFrame(html)
|
|
6098
6118
|
},
|
|
@@ -8268,6 +8288,24 @@ export default ExcelGridComponent
|
|
|
8268
8288
|
word-break: break-word;
|
|
8269
8289
|
z-index: 10000;
|
|
8270
8290
|
}
|
|
8291
|
+
.eg-select-footer {
|
|
8292
|
+
display: flex;
|
|
8293
|
+
justify-content: flex-end;
|
|
8294
|
+
padding: 4px 6px 6px;
|
|
8295
|
+
border-top: 1px solid var(--eg-color-border, var(--eg-color-border-default));
|
|
8296
|
+
}
|
|
8297
|
+
.eg-select-btn {
|
|
8298
|
+
background: none;
|
|
8299
|
+
border: 1px solid var(--eg-color-border, var(--eg-color-border-default));
|
|
8300
|
+
border-radius: var(--eg-radius, var(--eg-radius-default));
|
|
8301
|
+
padding: 4px 10px;
|
|
8302
|
+
cursor: pointer;
|
|
8303
|
+
font-size: 12px;
|
|
8304
|
+
color: var(--eg-color-text, var(--eg-color-text-default));
|
|
8305
|
+
}
|
|
8306
|
+
.eg-select-btn:hover { background: var(--eg-color-option-hover-bg, var(--eg-color-option-hover-bg-default)); }
|
|
8307
|
+
.eg-select-btn--clear { color: var(--eg-color-danger, #c62828); border-color: var(--eg-color-danger, #f1b0b0); }
|
|
8308
|
+
.eg-select-btn--clear:hover { background: #fff0f0; }
|
|
8271
8309
|
/* Grid `size` overrides — must live in this *unscoped* style block, not the
|
|
8272
8310
|
scoped one above: .eg-select-option is rendered by the separate EgSelect /
|
|
8273
8311
|
EgTreeSelect components, so Vue's scoped-CSS attribute (added only to this
|
package/src/utils/printTable.js
CHANGED
|
@@ -14,6 +14,15 @@ function escapeHtml(value) {
|
|
|
14
14
|
.replace(/"/g, '"')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// Serializes a Vue :style-shaped object (camelCase keys) into a CSS string —
|
|
18
|
+
// this file builds a raw HTML document, so there's no Vue runtime here to do
|
|
19
|
+
// the :style -> style="" conversion automatically.
|
|
20
|
+
function styleObjToCss(styleObj) {
|
|
21
|
+
return Object.entries(styleObj)
|
|
22
|
+
.map(([k, v]) => `${k.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}:${v}`)
|
|
23
|
+
.join(';')
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
// Above this many visible columns, a portrait page can't fit them without
|
|
18
27
|
// crushing every column to near-zero width — switch to landscape instead.
|
|
19
28
|
const LANDSCAPE_COL_THRESHOLD = 6
|
|
@@ -38,6 +47,8 @@ function sizeTierFor(colCount) {
|
|
|
38
47
|
* @param {(rowIndex:number, colIndex:number) => string} opts.getCellText
|
|
39
48
|
* @param {boolean} opts.showFooter
|
|
40
49
|
* @param {(colIndex:number) => string} opts.getFooterText
|
|
50
|
+
* @param {Object} [opts.printTitle] - { title, style, class } — rendered centered above the
|
|
51
|
+
* table when `title` is non-empty; omitted (or empty title) renders nothing
|
|
41
52
|
* @returns {string} full HTML document
|
|
42
53
|
*/
|
|
43
54
|
export function buildPrintTableHtml({
|
|
@@ -49,7 +60,8 @@ export function buildPrintTableHtml({
|
|
|
49
60
|
colWidths,
|
|
50
61
|
getCellText,
|
|
51
62
|
showFooter = false,
|
|
52
|
-
getFooterText
|
|
63
|
+
getFooterText,
|
|
64
|
+
printTitle = null
|
|
53
65
|
}) {
|
|
54
66
|
const exportCols = leafCols.filter((col, idx) => !hiddenColSet.has(idx))
|
|
55
67
|
const exportKeySet = new Set(exportCols.map(c => c.key))
|
|
@@ -161,6 +173,15 @@ export function buildPrintTableHtml({
|
|
|
161
173
|
const totalWidth = adjustedWidths.reduce((sum, w) => sum + w, 0) || 1
|
|
162
174
|
const colgroupHtml = '<colgroup>' + adjustedWidths.map(w => `<col style="width:${(w / totalWidth * 100).toFixed(3)}%">`).join('') + '</colgroup>'
|
|
163
175
|
|
|
176
|
+
// ── Optional host-supplied title, centered above the table. The default
|
|
177
|
+
// .eg-print-title rule (below) sets the centered/bold look; a host style
|
|
178
|
+
// object is serialized as this element's OWN inline style, which wins over
|
|
179
|
+
// the stylesheet rule per normal CSS cascade — so it only overrides what
|
|
180
|
+
// the host actually specifies, the rest of the default look stays. ────────
|
|
181
|
+
const titleHtml = (printTitle && printTitle.title)
|
|
182
|
+
? `<div class="eg-print-title${printTitle.class ? ' ' + escapeHtml(printTitle.class) : ''}"${printTitle.style ? ` style="${escapeHtml(styleObjToCss(printTitle.style))}"` : ''}>${escapeHtml(printTitle.title)}</div>`
|
|
183
|
+
: ''
|
|
184
|
+
|
|
164
185
|
return `<!DOCTYPE html>
|
|
165
186
|
<html>
|
|
166
187
|
<head>
|
|
@@ -188,10 +209,12 @@ th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
|
188
209
|
td { word-break: normal; overflow-wrap: break-word; }
|
|
189
210
|
thead th { background: #f0f0f0; font-weight: bold; text-align: center; }
|
|
190
211
|
tfoot td { font-weight: bold; background: #f7f7f7; }
|
|
212
|
+
.eg-print-title { text-align: center; font-weight: bold; font-size: 20px; margin-bottom: 12px; }
|
|
191
213
|
@page { size: ${orientation}; margin: 12mm; }
|
|
192
214
|
</style>
|
|
193
215
|
</head>
|
|
194
216
|
<body>
|
|
217
|
+
${titleHtml}
|
|
195
218
|
<table>
|
|
196
219
|
${colgroupHtml}
|
|
197
220
|
<thead>${theadRowsHtml}</thead>
|