@alaarab/ogrid-vue-vuetify 2.0.14 → 2.0.16
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.
|
@@ -302,6 +302,23 @@
|
|
|
302
302
|
overflow: visible;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
/* === Editing cell wrapper === */
|
|
306
|
+
|
|
307
|
+
.ogrid-editing-cell {
|
|
308
|
+
width: 100%;
|
|
309
|
+
height: 100%;
|
|
310
|
+
display: flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
box-sizing: border-box;
|
|
313
|
+
outline: 2px solid var(--ogrid-selection-color, #217346);
|
|
314
|
+
outline-offset: -1px;
|
|
315
|
+
z-index: 2;
|
|
316
|
+
position: relative;
|
|
317
|
+
background: var(--ogrid-bg, #fff);
|
|
318
|
+
overflow: visible;
|
|
319
|
+
padding: 0;
|
|
320
|
+
}
|
|
321
|
+
|
|
305
322
|
/* === Fill handle === */
|
|
306
323
|
|
|
307
324
|
.ogrid-fill-handle {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, ref, h, onMounted, nextTick, watch } from 'vue';
|
|
2
|
-
import { VCheckbox
|
|
2
|
+
import { VCheckbox } from 'vuetify/components';
|
|
3
3
|
const editorWrapperStyle = {
|
|
4
4
|
width: '100%',
|
|
5
5
|
height: '100%',
|
|
@@ -21,16 +21,102 @@ export const InlineCellEditor = defineComponent({
|
|
|
21
21
|
},
|
|
22
22
|
setup(props) {
|
|
23
23
|
const inputRef = ref(null);
|
|
24
|
+
const selectWrapperRef = ref(null);
|
|
25
|
+
const selectDropdownRef = ref(null);
|
|
24
26
|
const localValue = ref(props.value);
|
|
27
|
+
const highlightedIndex = ref(0);
|
|
25
28
|
// Auto-focus on mount
|
|
29
|
+
const positionDropdown = () => {
|
|
30
|
+
const wrapper = selectWrapperRef.value;
|
|
31
|
+
const dropdown = selectDropdownRef.value;
|
|
32
|
+
if (!wrapper || !dropdown)
|
|
33
|
+
return;
|
|
34
|
+
const rect = wrapper.getBoundingClientRect();
|
|
35
|
+
const maxH = 200;
|
|
36
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
37
|
+
const flipUp = spaceBelow < maxH && rect.top > spaceBelow;
|
|
38
|
+
dropdown.style.position = 'fixed';
|
|
39
|
+
dropdown.style.left = `${rect.left}px`;
|
|
40
|
+
dropdown.style.width = `${rect.width}px`;
|
|
41
|
+
dropdown.style.maxHeight = `${maxH}px`;
|
|
42
|
+
dropdown.style.zIndex = '9999';
|
|
43
|
+
dropdown.style.right = 'auto';
|
|
44
|
+
if (flipUp) {
|
|
45
|
+
dropdown.style.top = 'auto';
|
|
46
|
+
dropdown.style.bottom = `${window.innerHeight - rect.top}px`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
dropdown.style.top = `${rect.bottom}px`;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
26
52
|
onMounted(() => {
|
|
27
53
|
nextTick(() => {
|
|
54
|
+
if (selectWrapperRef.value) {
|
|
55
|
+
selectWrapperRef.value.focus();
|
|
56
|
+
positionDropdown();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
28
59
|
inputRef.value?.focus();
|
|
29
60
|
inputRef.value?.select();
|
|
30
61
|
});
|
|
31
62
|
});
|
|
32
63
|
// Sync local value when prop changes
|
|
33
64
|
watch(() => props.value, (v) => { localValue.value = v; });
|
|
65
|
+
// Initialize highlighted index to current value
|
|
66
|
+
const initHighlightedIndex = () => {
|
|
67
|
+
const values = props.column.cellEditorParams?.values ?? [];
|
|
68
|
+
const idx = values.findIndex((v) => String(v) === String(props.value));
|
|
69
|
+
highlightedIndex.value = Math.max(idx, 0);
|
|
70
|
+
};
|
|
71
|
+
initHighlightedIndex();
|
|
72
|
+
const scrollHighlightedIntoView = () => {
|
|
73
|
+
nextTick(() => {
|
|
74
|
+
const dropdown = selectDropdownRef.value;
|
|
75
|
+
if (!dropdown)
|
|
76
|
+
return;
|
|
77
|
+
const highlighted = dropdown.children[highlightedIndex.value];
|
|
78
|
+
highlighted?.scrollIntoView({ block: 'nearest' });
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const getDisplayText = (value) => {
|
|
82
|
+
const formatValue = props.column.cellEditorParams?.formatValue;
|
|
83
|
+
if (formatValue)
|
|
84
|
+
return formatValue(value);
|
|
85
|
+
return value != null ? String(value) : '';
|
|
86
|
+
};
|
|
87
|
+
const handleSelectKeyDown = (e) => {
|
|
88
|
+
const values = props.column.cellEditorParams?.values ?? [];
|
|
89
|
+
switch (e.key) {
|
|
90
|
+
case 'ArrowDown':
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
highlightedIndex.value = Math.min(highlightedIndex.value + 1, values.length - 1);
|
|
93
|
+
scrollHighlightedIntoView();
|
|
94
|
+
break;
|
|
95
|
+
case 'ArrowUp':
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0);
|
|
98
|
+
scrollHighlightedIntoView();
|
|
99
|
+
break;
|
|
100
|
+
case 'Enter':
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
e.stopPropagation();
|
|
103
|
+
if (values.length > 0 && highlightedIndex.value < values.length) {
|
|
104
|
+
props.onCommit(values[highlightedIndex.value]);
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case 'Tab':
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
if (values.length > 0 && highlightedIndex.value < values.length) {
|
|
110
|
+
props.onCommit(values[highlightedIndex.value]);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case 'Escape':
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
e.stopPropagation();
|
|
116
|
+
props.onCancel();
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
34
120
|
return () => {
|
|
35
121
|
if (props.editorType === 'checkbox') {
|
|
36
122
|
const checked = !!props.value;
|
|
@@ -49,22 +135,28 @@ export const InlineCellEditor = defineComponent({
|
|
|
49
135
|
}
|
|
50
136
|
if (props.editorType === 'select') {
|
|
51
137
|
const values = props.column.cellEditorParams?.values ?? [];
|
|
52
|
-
return h('div', {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
|
|
138
|
+
return h('div', {
|
|
139
|
+
ref: (el) => { selectWrapperRef.value = el; },
|
|
140
|
+
tabindex: 0,
|
|
141
|
+
style: { ...editorWrapperStyle, position: 'relative' },
|
|
142
|
+
onKeydown: handleSelectKeyDown,
|
|
143
|
+
}, [
|
|
144
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', cursor: 'pointer', fontSize: '13px', color: 'inherit' } }, [
|
|
145
|
+
h('span', getDisplayText(props.value)),
|
|
146
|
+
h('span', { style: { marginLeft: '4px', fontSize: '10px', opacity: '0.5' } }, '\u25BE'),
|
|
147
|
+
]),
|
|
148
|
+
h('div', {
|
|
149
|
+
ref: (el) => { selectDropdownRef.value = el; },
|
|
150
|
+
role: 'listbox',
|
|
151
|
+
style: { position: 'absolute', top: '100%', left: '0', right: '0', maxHeight: '200px', overflowY: 'auto', background: 'var(--ogrid-bg, #fff)', border: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))', zIndex: '10', boxShadow: '0 4px 16px rgba(0,0,0,0.2)' },
|
|
152
|
+
}, values.map((v, i) => h('div', {
|
|
153
|
+
key: String(v),
|
|
154
|
+
role: 'option',
|
|
155
|
+
'aria-selected': i === highlightedIndex.value,
|
|
156
|
+
onClick: () => props.onCommit(v),
|
|
157
|
+
style: { padding: '6px 8px', cursor: 'pointer', color: 'var(--ogrid-fg, #242424)', ...(i === highlightedIndex.value ? { background: 'var(--ogrid-bg-hover, #e8f0fe)' } : {}) },
|
|
158
|
+
}, getDisplayText(v)))),
|
|
159
|
+
]);
|
|
68
160
|
}
|
|
69
161
|
if (props.editorType === 'date') {
|
|
70
162
|
let dateStr = '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-vue-vuetify",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.16",
|
|
4
4
|
"description": "OGrid Vuetify – Vuetify-based data grid with sorting, filtering, pagination, column chooser, and CSV export.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"node": ">=18"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@alaarab/ogrid-vue": "2.0.
|
|
40
|
+
"@alaarab/ogrid-vue": "2.0.15"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"vue": "^3.3.0",
|