@alaarab/ogrid-vue 2.1.1 → 2.1.3
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/esm/components/createDataGridTable.js +2 -1
- package/dist/esm/components/createOGrid.js +78 -36
- package/dist/esm/composables/useDataGridState.js +1 -0
- package/dist/esm/composables/useOGrid.js +2 -0
- package/dist/esm/styles/ogrid-layout.css +13 -2
- package/dist/types/composables/useDataGridState.d.ts +1 -0
- package/dist/types/composables/useOGrid.d.ts +1 -0
- package/dist/types/types/dataGridTypes.d.ts +6 -0
- package/package.json +2 -2
|
@@ -107,6 +107,7 @@ export function createDataGridTable(ui) {
|
|
|
107
107
|
const layoutMode = p.layoutMode ?? 'fill';
|
|
108
108
|
const rowSelection = p.rowSelection ?? 'none';
|
|
109
109
|
const suppressHorizontalScroll = p.suppressHorizontalScroll;
|
|
110
|
+
const stickyHeader = p.stickyHeader ?? true;
|
|
110
111
|
const isLoading = p.isLoading ?? false;
|
|
111
112
|
const loadingMessage = p.loadingMessage ?? 'Loading\u2026';
|
|
112
113
|
const ariaLabel = p['aria-label'];
|
|
@@ -255,7 +256,7 @@ export function createDataGridTable(ui) {
|
|
|
255
256
|
style: { minWidth: `${minTableWidth}px` },
|
|
256
257
|
}, [
|
|
257
258
|
// Header
|
|
258
|
-
h('thead', { class: 'ogrid-thead' }, headerRows.map((row, rowIdx) => h('tr', { key: rowIdx, class: 'ogrid-header-row' }, [
|
|
259
|
+
h('thead', { class: stickyHeader ? 'ogrid-thead ogrid-sticky-header' : 'ogrid-thead' }, headerRows.map((row, rowIdx) => h('tr', { key: rowIdx, class: 'ogrid-header-row' }, [
|
|
259
260
|
// Checkbox header cell
|
|
260
261
|
...(rowIdx === headerRows.length - 1 && hasCheckboxCol ? [
|
|
261
262
|
h('th', {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* they only differ in which DataGridTable, ColumnChooser, and PaginationControls
|
|
6
6
|
* components they use. This factory extracts all shared logic into one place.
|
|
7
7
|
*/
|
|
8
|
-
import { defineComponent, h, computed } from 'vue';
|
|
8
|
+
import { defineComponent, h, ref, onMounted, onUnmounted, computed } from 'vue';
|
|
9
9
|
import { useOGrid, } from '../composables';
|
|
10
10
|
// --- SideBar constants and styles ---
|
|
11
11
|
const PANEL_WIDTH = 240;
|
|
@@ -245,6 +245,16 @@ export function createOGrid(ui) {
|
|
|
245
245
|
const { dataGridProps, pagination, columnChooser, layout, api } = useOGrid(propsRef);
|
|
246
246
|
// Expose the ref container so parent always gets the latest API value
|
|
247
247
|
expose({ api });
|
|
248
|
+
// Fullscreen state
|
|
249
|
+
const isFullScreen = ref(false);
|
|
250
|
+
const toggleFullScreen = () => { isFullScreen.value = !isFullScreen.value; };
|
|
251
|
+
// ESC key to exit fullscreen
|
|
252
|
+
const handleEscKey = (e) => {
|
|
253
|
+
if (e.key === 'Escape' && isFullScreen.value)
|
|
254
|
+
isFullScreen.value = false;
|
|
255
|
+
};
|
|
256
|
+
onMounted(() => { document.addEventListener('keydown', handleEscKey); });
|
|
257
|
+
onUnmounted(() => { document.removeEventListener('keydown', handleEscKey); });
|
|
248
258
|
return () => {
|
|
249
259
|
const sideBar = layout.value.sideBarProps;
|
|
250
260
|
const hasSideBar = sideBar != null;
|
|
@@ -254,6 +264,31 @@ export function createOGrid(ui) {
|
|
|
254
264
|
if (layout.value.toolbar) {
|
|
255
265
|
toolbarChildren.push(layout.value.toolbar);
|
|
256
266
|
}
|
|
267
|
+
// Fullscreen toggle button
|
|
268
|
+
const showFullScreen = layout.value.fullScreen === true;
|
|
269
|
+
const fullscreenButton = showFullScreen
|
|
270
|
+
? h('button', {
|
|
271
|
+
type: 'button',
|
|
272
|
+
title: isFullScreen.value ? 'Exit fullscreen' : 'Fullscreen',
|
|
273
|
+
'aria-label': isFullScreen.value ? 'Exit fullscreen' : 'Fullscreen',
|
|
274
|
+
onClick: toggleFullScreen,
|
|
275
|
+
style: {
|
|
276
|
+
background: 'none',
|
|
277
|
+
border: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
278
|
+
borderRadius: '4px',
|
|
279
|
+
padding: '4px 6px',
|
|
280
|
+
cursor: 'pointer',
|
|
281
|
+
display: 'flex',
|
|
282
|
+
alignItems: 'center',
|
|
283
|
+
justifyContent: 'center',
|
|
284
|
+
color: 'var(--ogrid-fg, rgba(0,0,0,0.87))',
|
|
285
|
+
},
|
|
286
|
+
}, [
|
|
287
|
+
isFullScreen.value
|
|
288
|
+
? h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', 'stroke-width': '1.5', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', innerHTML: '<polyline points="4 10 0 10 0 14"/><polyline points="12 6 16 6 16 2"/><line x1="0" y1="10" x2="4" y2="6"/><line x1="16" y1="6" x2="12" y2="10"/>' })
|
|
289
|
+
: h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', 'stroke-width': '1.5', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', innerHTML: '<polyline points="10 2 14 2 14 6"/><polyline points="6 14 2 14 2 10"/><line x1="14" y1="2" x2="10" y2="6"/><line x1="2" y1="14" x2="6" y2="10"/>' }),
|
|
290
|
+
])
|
|
291
|
+
: null;
|
|
257
292
|
// ColumnChooser in toolbar
|
|
258
293
|
const toolbarEnd = columnChooser.value.placement === 'toolbar'
|
|
259
294
|
? h(ui.ColumnChooser, {
|
|
@@ -291,49 +326,56 @@ export function createOGrid(ui) {
|
|
|
291
326
|
if (hasSideBar && sideBarPosition !== 'left') {
|
|
292
327
|
mainAreaChildren.push(renderSideBar(sideBar));
|
|
293
328
|
}
|
|
329
|
+
const hasToolbar = toolbarChildren.length > 0 || toolbarEnd != null || fullscreenButton != null;
|
|
330
|
+
const rootStyle = isFullScreen.value
|
|
331
|
+
? { position: 'fixed', inset: '0', zIndex: 9999, display: 'flex', flexDirection: 'column', background: 'var(--ogrid-bg, #fff)' }
|
|
332
|
+
: { display: 'flex', flexDirection: 'column', border: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))', borderRadius: '4px', overflow: 'hidden' };
|
|
333
|
+
const containerStyle = isFullScreen.value
|
|
334
|
+
? { display: 'flex', flexDirection: 'column', flex: '1', minHeight: '0', overflow: 'hidden', background: 'var(--ogrid-bg, #fff)' }
|
|
335
|
+
: undefined;
|
|
294
336
|
return h('div', {
|
|
295
337
|
class: layout.value.className,
|
|
296
|
-
style:
|
|
297
|
-
display: 'flex',
|
|
298
|
-
flexDirection: 'column',
|
|
299
|
-
border: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
300
|
-
borderRadius: '4px',
|
|
301
|
-
overflow: 'hidden',
|
|
302
|
-
},
|
|
338
|
+
style: rootStyle,
|
|
303
339
|
}, [
|
|
304
|
-
//
|
|
305
|
-
|
|
340
|
+
// Inner container (for fullscreen: no border/radius)
|
|
341
|
+
h('div', { style: containerStyle ?? {} }, [
|
|
342
|
+
// Toolbar strip
|
|
343
|
+
...(hasToolbar ? [
|
|
344
|
+
h('div', {
|
|
345
|
+
style: {
|
|
346
|
+
display: 'flex',
|
|
347
|
+
alignItems: 'center',
|
|
348
|
+
justifyContent: 'space-between',
|
|
349
|
+
padding: '8px 12px',
|
|
350
|
+
borderBottom: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
351
|
+
gap: '8px',
|
|
352
|
+
},
|
|
353
|
+
}, [
|
|
354
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', flex: '1' } }, toolbarChildren),
|
|
355
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
|
|
356
|
+
...(toolbarEnd ? [toolbarEnd] : []),
|
|
357
|
+
...(fullscreenButton ? [fullscreenButton] : []),
|
|
358
|
+
]),
|
|
359
|
+
]),
|
|
360
|
+
] : []),
|
|
361
|
+
// Below toolbar strip
|
|
362
|
+
...(layout.value.toolbarBelow ? [
|
|
363
|
+
h('div', {
|
|
364
|
+
style: { padding: '8px 12px', borderBottom: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))' },
|
|
365
|
+
}, [layout.value.toolbarBelow]),
|
|
366
|
+
] : []),
|
|
367
|
+
// Main content area (sidebar + grid)
|
|
368
|
+
h('div', { style: { display: 'flex', flex: '1', minHeight: '0' } }, mainAreaChildren),
|
|
369
|
+
// Footer strip (pagination)
|
|
306
370
|
h('div', {
|
|
307
371
|
style: {
|
|
308
372
|
display: 'flex',
|
|
309
373
|
alignItems: 'center',
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
borderBottom: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
313
|
-
gap: '8px',
|
|
374
|
+
padding: '8px 0',
|
|
375
|
+
borderTop: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
314
376
|
},
|
|
315
|
-
}, [
|
|
316
|
-
|
|
317
|
-
...(toolbarEnd ? [toolbarEnd] : []),
|
|
318
|
-
]),
|
|
319
|
-
] : []),
|
|
320
|
-
// Below toolbar strip
|
|
321
|
-
...(layout.value.toolbarBelow ? [
|
|
322
|
-
h('div', {
|
|
323
|
-
style: { padding: '8px 12px', borderBottom: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))' },
|
|
324
|
-
}, [layout.value.toolbarBelow]),
|
|
325
|
-
] : []),
|
|
326
|
-
// Main content area (sidebar + grid)
|
|
327
|
-
h('div', { style: { display: 'flex', flex: '1', minHeight: '0' } }, mainAreaChildren),
|
|
328
|
-
// Footer strip (pagination)
|
|
329
|
-
h('div', {
|
|
330
|
-
style: {
|
|
331
|
-
display: 'flex',
|
|
332
|
-
alignItems: 'center',
|
|
333
|
-
padding: '8px 0',
|
|
334
|
-
borderTop: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
|
|
335
|
-
},
|
|
336
|
-
}, [paginationNode]),
|
|
377
|
+
}, [paginationNode]),
|
|
378
|
+
]),
|
|
337
379
|
]);
|
|
338
380
|
};
|
|
339
381
|
},
|
|
@@ -329,6 +329,7 @@ export function useDataGridState(params) {
|
|
|
329
329
|
setColumnSizingOverrides,
|
|
330
330
|
onColumnResized: props.value.onColumnResized,
|
|
331
331
|
measuredColumnWidths: measuredColumnWidths.value,
|
|
332
|
+
stickyHeader: props.value.stickyHeader ?? true,
|
|
332
333
|
}));
|
|
333
334
|
const rowSelectionState = computed(() => ({
|
|
334
335
|
selectedRowIds: rowSelectionResult.selectedRowIds.value,
|
|
@@ -367,6 +367,7 @@ export function useOGrid(props) {
|
|
|
367
367
|
getUserByEmail: ds?.getUserByEmail,
|
|
368
368
|
layoutMode: p.layoutMode,
|
|
369
369
|
suppressHorizontalScroll: p.suppressHorizontalScroll,
|
|
370
|
+
stickyHeader: p.stickyHeader ?? true,
|
|
370
371
|
columnReorder: p.columnReorder,
|
|
371
372
|
virtualScroll: p.virtualScroll,
|
|
372
373
|
rowHeight: p.rowHeight,
|
|
@@ -402,6 +403,7 @@ export function useOGrid(props) {
|
|
|
402
403
|
className: props.value.className,
|
|
403
404
|
emptyState: props.value.emptyState,
|
|
404
405
|
sideBarProps: sideBarProps.value,
|
|
406
|
+
fullScreen: props.value.fullScreen,
|
|
405
407
|
}));
|
|
406
408
|
const filtersResult = computed(() => ({
|
|
407
409
|
hasActiveFilters: hasActiveFilters.value,
|
|
@@ -77,20 +77,27 @@
|
|
|
77
77
|
|
|
78
78
|
.ogrid-header-cell {
|
|
79
79
|
font-weight: 600;
|
|
80
|
-
position: sticky;
|
|
81
|
-
top: 0;
|
|
82
80
|
background-color: var(--ogrid-header-bg);
|
|
83
81
|
z-index: 8;
|
|
84
82
|
}
|
|
85
83
|
|
|
84
|
+
.ogrid-sticky-header .ogrid-header-cell {
|
|
85
|
+
position: sticky;
|
|
86
|
+
top: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
86
89
|
.ogrid-header-cell--pinned-left {
|
|
87
90
|
z-index: 10;
|
|
88
91
|
will-change: transform;
|
|
92
|
+
border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
|
|
93
|
+
box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
.ogrid-header-cell--pinned-right {
|
|
92
97
|
z-index: 10;
|
|
93
98
|
will-change: transform;
|
|
99
|
+
border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
|
|
100
|
+
box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
.ogrid-header-content {
|
|
@@ -185,6 +192,8 @@
|
|
|
185
192
|
z-index: 6;
|
|
186
193
|
background-color: var(--ogrid-bg);
|
|
187
194
|
will-change: transform;
|
|
195
|
+
border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
|
|
196
|
+
box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
|
|
188
197
|
}
|
|
189
198
|
|
|
190
199
|
.ogrid-data-cell--pinned-right {
|
|
@@ -192,6 +201,8 @@
|
|
|
192
201
|
z-index: 6;
|
|
193
202
|
background-color: var(--ogrid-bg);
|
|
194
203
|
will-change: transform;
|
|
204
|
+
border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
|
|
205
|
+
box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
.ogrid-cell-content {
|
|
@@ -28,6 +28,7 @@ export interface DataGridLayoutState<T> {
|
|
|
28
28
|
* UI packages use these as a minWidth floor to prevent columns from
|
|
29
29
|
* shrinking when new data loads (e.g. during server-side pagination). */
|
|
30
30
|
measuredColumnWidths: Record<string, number>;
|
|
31
|
+
stickyHeader: boolean;
|
|
31
32
|
}
|
|
32
33
|
export interface DataGridRowSelectionState {
|
|
33
34
|
selectedRowIds: Set<RowId>;
|
|
@@ -63,6 +63,10 @@ interface IOGridBaseProps<T> {
|
|
|
63
63
|
layoutMode?: 'content' | 'fill';
|
|
64
64
|
/** When true, horizontal scrolling is suppressed (overflow-x hidden). */
|
|
65
65
|
suppressHorizontalScroll?: boolean;
|
|
66
|
+
/** When true (default), header row sticks to the top of the scroll container. */
|
|
67
|
+
stickyHeader?: boolean;
|
|
68
|
+
/** When true, shows a fullscreen toggle button in the toolbar. Default: false. */
|
|
69
|
+
fullScreen?: boolean;
|
|
66
70
|
/** Side bar configuration. `true` shows default panels (columns + filters). Pass ISideBarDef for options. */
|
|
67
71
|
sideBar?: boolean | ISideBarDef;
|
|
68
72
|
/** Page size options shown in the pagination dropdown. Default: [10, 20, 50, 100]. */
|
|
@@ -122,6 +126,8 @@ export interface IOGridDataGridProps<T> {
|
|
|
122
126
|
layoutMode?: 'content' | 'fill';
|
|
123
127
|
/** When true, horizontal scrolling is suppressed (overflow-x hidden). */
|
|
124
128
|
suppressHorizontalScroll?: boolean;
|
|
129
|
+
/** When true (default), header row sticks to the top of the scroll container. */
|
|
130
|
+
stickyHeader?: boolean;
|
|
125
131
|
isLoading?: boolean;
|
|
126
132
|
loadingMessage?: string;
|
|
127
133
|
editable?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alaarab/ogrid-vue",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "OGrid Vue – Vue 3 composables, headless components, and utilities for OGrid data grids.",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=18"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@alaarab/ogrid-core": "2.1.
|
|
39
|
+
"@alaarab/ogrid-core": "2.1.3"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"vue": "^3.3.0"
|