@alaarab/ogrid-vue 2.1.2 → 2.1.4

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.
Files changed (44) hide show
  1. package/dist/esm/index.js +4336 -15
  2. package/package.json +4 -4
  3. package/dist/esm/components/MarchingAntsOverlay.js +0 -144
  4. package/dist/esm/components/SideBar.js +0 -1
  5. package/dist/esm/components/StatusBar.js +0 -49
  6. package/dist/esm/components/createDataGridTable.js +0 -514
  7. package/dist/esm/components/createInlineCellEditor.js +0 -194
  8. package/dist/esm/components/createOGrid.js +0 -383
  9. package/dist/esm/composables/index.js +0 -33
  10. package/dist/esm/composables/useActiveCell.js +0 -77
  11. package/dist/esm/composables/useCellEditing.js +0 -27
  12. package/dist/esm/composables/useCellSelection.js +0 -359
  13. package/dist/esm/composables/useClipboard.js +0 -87
  14. package/dist/esm/composables/useColumnChooserState.js +0 -74
  15. package/dist/esm/composables/useColumnHeaderFilterState.js +0 -189
  16. package/dist/esm/composables/useColumnHeaderMenuState.js +0 -113
  17. package/dist/esm/composables/useColumnPinning.js +0 -64
  18. package/dist/esm/composables/useColumnReorder.js +0 -110
  19. package/dist/esm/composables/useColumnResize.js +0 -73
  20. package/dist/esm/composables/useContextMenu.js +0 -23
  21. package/dist/esm/composables/useDataGridState.js +0 -425
  22. package/dist/esm/composables/useDataGridTableSetup.js +0 -66
  23. package/dist/esm/composables/useDateFilterState.js +0 -36
  24. package/dist/esm/composables/useDebounce.js +0 -60
  25. package/dist/esm/composables/useFillHandle.js +0 -205
  26. package/dist/esm/composables/useFilterOptions.js +0 -39
  27. package/dist/esm/composables/useInlineCellEditorState.js +0 -42
  28. package/dist/esm/composables/useKeyboardNavigation.js +0 -232
  29. package/dist/esm/composables/useLatestRef.js +0 -27
  30. package/dist/esm/composables/useMultiSelectFilterState.js +0 -59
  31. package/dist/esm/composables/useOGrid.js +0 -491
  32. package/dist/esm/composables/usePeopleFilterState.js +0 -66
  33. package/dist/esm/composables/useRichSelectState.js +0 -59
  34. package/dist/esm/composables/useRowSelection.js +0 -75
  35. package/dist/esm/composables/useSideBarState.js +0 -41
  36. package/dist/esm/composables/useTableLayout.js +0 -85
  37. package/dist/esm/composables/useTextFilterState.js +0 -26
  38. package/dist/esm/composables/useUndoRedo.js +0 -65
  39. package/dist/esm/composables/useVirtualScroll.js +0 -87
  40. package/dist/esm/types/columnTypes.js +0 -1
  41. package/dist/esm/types/dataGridTypes.js +0 -1
  42. package/dist/esm/types/index.js +0 -1
  43. package/dist/esm/utils/dataGridViewModel.js +0 -23
  44. package/dist/esm/utils/index.js +0 -1
@@ -1,194 +0,0 @@
1
- import { defineComponent, ref, h, onMounted, nextTick, watch } from 'vue';
2
- const editorWrapperStyle = {
3
- width: '100%',
4
- height: '100%',
5
- display: 'flex',
6
- alignItems: 'center',
7
- padding: '0 2px',
8
- boxSizing: 'border-box',
9
- };
10
- export function createInlineCellEditor(options) {
11
- const { renderCheckbox, renderDatePicker } = options;
12
- return defineComponent({
13
- name: 'InlineCellEditor',
14
- props: {
15
- value: { default: undefined },
16
- item: { type: Object, required: true },
17
- column: { type: Object, required: true },
18
- rowIndex: { type: Number, required: true },
19
- editorType: { type: String, required: true },
20
- onCommit: { type: Function, required: true },
21
- onCancel: { type: Function, required: true },
22
- },
23
- setup(props) {
24
- const inputRef = ref(null);
25
- const selectWrapperRef = ref(null);
26
- const selectDropdownRef = ref(null);
27
- const localValue = ref(props.value);
28
- const highlightedIndex = ref(0);
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
- };
52
- onMounted(() => {
53
- nextTick(() => {
54
- if (selectWrapperRef.value) {
55
- selectWrapperRef.value.focus();
56
- positionDropdown();
57
- return;
58
- }
59
- inputRef.value?.focus();
60
- inputRef.value?.select();
61
- });
62
- });
63
- // Sync local value when prop changes
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
- };
120
- return () => {
121
- if (props.editorType === 'checkbox') {
122
- const checked = !!props.value;
123
- return h('div', { style: { ...editorWrapperStyle, justifyContent: 'center' } }, renderCheckbox({
124
- checked,
125
- onChange: (c) => props.onCommit(c),
126
- onCancel: props.onCancel,
127
- }));
128
- }
129
- if (props.editorType === 'select') {
130
- const values = props.column.cellEditorParams?.values ?? [];
131
- return h('div', {
132
- ref: (el) => { selectWrapperRef.value = el; },
133
- tabindex: 0,
134
- style: { ...editorWrapperStyle, position: 'relative' },
135
- onKeydown: handleSelectKeyDown,
136
- }, [
137
- h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', cursor: 'pointer', fontSize: '13px', color: 'inherit' } }, [
138
- h('span', getDisplayText(props.value)),
139
- h('span', { style: { marginLeft: '4px', fontSize: '10px', opacity: '0.5' } }, '\u25BE'),
140
- ]),
141
- h('div', {
142
- ref: (el) => { selectDropdownRef.value = el; },
143
- role: 'listbox',
144
- 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)' },
145
- }, values.map((v, i) => h('div', {
146
- key: String(v),
147
- role: 'option',
148
- 'aria-selected': i === highlightedIndex.value,
149
- onClick: () => props.onCommit(v),
150
- style: { padding: '6px 8px', cursor: 'pointer', color: 'var(--ogrid-fg, #242424)', ...(i === highlightedIndex.value ? { background: 'var(--ogrid-bg-hover, #e8f0fe)' } : {}) },
151
- }, getDisplayText(v)))),
152
- ]);
153
- }
154
- if (props.editorType === 'date') {
155
- let dateStr = '';
156
- if (localValue.value) {
157
- const d = new Date(String(localValue.value));
158
- if (!Number.isNaN(d.getTime())) {
159
- dateStr = d.toISOString().slice(0, 10);
160
- }
161
- }
162
- return h('div', { style: editorWrapperStyle }, renderDatePicker({
163
- value: dateStr,
164
- onChange: (val) => props.onCommit(val),
165
- onCancel: props.onCancel,
166
- }));
167
- }
168
- // Default: text editor
169
- return h('div', { style: editorWrapperStyle }, h('input', {
170
- ref: (el) => { inputRef.value = el; },
171
- type: 'text',
172
- value: localValue.value != null ? String(localValue.value) : '',
173
- style: { width: '100%', height: '100%', border: 'none', outline: 'none', padding: '0 4px', fontSize: 'inherit', boxSizing: 'border-box' },
174
- onInput: (e) => { localValue.value = e.target.value; },
175
- onKeydown: (e) => {
176
- if (e.key === 'Enter') {
177
- e.preventDefault();
178
- props.onCommit(localValue.value);
179
- }
180
- if (e.key === 'Escape') {
181
- e.preventDefault();
182
- props.onCancel();
183
- }
184
- if (e.key === 'Tab') {
185
- e.preventDefault();
186
- props.onCommit(localValue.value);
187
- }
188
- },
189
- onBlur: () => props.onCommit(localValue.value),
190
- }));
191
- };
192
- },
193
- });
194
- }
@@ -1,383 +0,0 @@
1
- /**
2
- * Shared OGrid factory for Vue UI packages.
3
- *
4
- * Both vue-vuetify and vue-primevue OGrid components are 100% identical —
5
- * they only differ in which DataGridTable, ColumnChooser, and PaginationControls
6
- * components they use. This factory extracts all shared logic into one place.
7
- */
8
- import { defineComponent, h, ref, onMounted, onUnmounted, computed } from 'vue';
9
- import { useOGrid, } from '../composables';
10
- // --- SideBar constants and styles ---
11
- const PANEL_WIDTH = 240;
12
- const TAB_WIDTH = 36;
13
- const PANEL_LABELS = {
14
- columns: 'Columns',
15
- filters: 'Filters',
16
- };
17
- const PANEL_ICONS = {
18
- columns: '\u2261', // hamburger icon
19
- filters: '\u2A65', // filter icon
20
- };
21
- /** Render the SideBar inline (tab strip + panel content). */
22
- function renderSideBar(sb) {
23
- const isOpen = sb.activePanel !== null;
24
- const position = sb.position ?? 'right';
25
- const tabStripStyle = {
26
- display: 'flex',
27
- flexDirection: 'column',
28
- width: `${TAB_WIDTH}px`,
29
- background: 'var(--ogrid-header-bg, #f5f5f5)',
30
- ...(position === 'right'
31
- ? { borderLeft: '1px solid var(--ogrid-border, #e0e0e0)' }
32
- : { borderRight: '1px solid var(--ogrid-border, #e0e0e0)' }),
33
- };
34
- const tabStrip = h('div', { style: tabStripStyle, role: 'tablist', 'aria-label': 'Side bar tabs' }, sb.panels.map((panel) => h('button', {
35
- key: panel,
36
- role: 'tab',
37
- 'aria-selected': sb.activePanel === panel,
38
- 'aria-label': PANEL_LABELS[panel],
39
- title: PANEL_LABELS[panel],
40
- onClick: () => sb.onPanelChange(sb.activePanel === panel ? null : panel),
41
- style: {
42
- width: `${TAB_WIDTH}px`,
43
- height: `${TAB_WIDTH}px`,
44
- border: 'none',
45
- cursor: 'pointer',
46
- color: 'var(--ogrid-fg, #242424)',
47
- fontSize: '14px',
48
- display: 'flex',
49
- alignItems: 'center',
50
- justifyContent: 'center',
51
- background: sb.activePanel === panel ? 'var(--ogrid-bg, #fff)' : 'transparent',
52
- fontWeight: sb.activePanel === panel ? 'bold' : 'normal',
53
- },
54
- }, PANEL_ICONS[panel])));
55
- let panelContent = null;
56
- if (isOpen && sb.activePanel) {
57
- const panelContainerStyle = {
58
- width: `${PANEL_WIDTH}px`,
59
- display: 'flex',
60
- flexDirection: 'column',
61
- overflow: 'hidden',
62
- background: 'var(--ogrid-bg, #fff)',
63
- color: 'var(--ogrid-fg, #242424)',
64
- ...(position === 'right'
65
- ? { borderLeft: '1px solid var(--ogrid-border, #e0e0e0)' }
66
- : { borderRight: '1px solid var(--ogrid-border, #e0e0e0)' }),
67
- };
68
- const panelBodyChildren = [];
69
- if (sb.activePanel === 'columns') {
70
- const allVisible = sb.columns.every((c) => sb.visibleColumns.has(c.columnId));
71
- // Select All / Clear All buttons
72
- panelBodyChildren.push(h('div', { style: { display: 'flex', gap: '8px', marginBottom: '8px' } }, [
73
- h('button', {
74
- disabled: allVisible,
75
- onClick: () => {
76
- const next = new Set(sb.visibleColumns);
77
- sb.columns.forEach((c) => next.add(c.columnId));
78
- sb.onSetVisibleColumns(next);
79
- },
80
- style: { flex: '1', cursor: 'pointer', background: 'var(--ogrid-bg-subtle, #f3f2f1)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: '4px', padding: '4px 8px' },
81
- }, 'Select All'),
82
- h('button', {
83
- onClick: () => {
84
- const next = new Set();
85
- sb.columns.forEach((c) => {
86
- if (c.required && sb.visibleColumns.has(c.columnId))
87
- next.add(c.columnId);
88
- });
89
- sb.onSetVisibleColumns(next);
90
- },
91
- style: { flex: '1', cursor: 'pointer', background: 'var(--ogrid-bg-subtle, #f3f2f1)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: '4px', padding: '4px 8px' },
92
- }, 'Clear All'),
93
- ]));
94
- // Column checkboxes
95
- sb.columns.forEach((col) => {
96
- panelBodyChildren.push(h('label', { key: col.columnId, style: { display: 'flex', alignItems: 'center', gap: '6px', padding: '2px 0', cursor: 'pointer' } }, [
97
- h('input', {
98
- type: 'checkbox',
99
- checked: sb.visibleColumns.has(col.columnId),
100
- disabled: col.required,
101
- onChange: (e) => sb.onVisibilityChange(col.columnId, e.target.checked),
102
- }),
103
- h('span', null, col.name),
104
- ]));
105
- });
106
- }
107
- if (sb.activePanel === 'filters') {
108
- if (sb.filterableColumns.length === 0) {
109
- panelBodyChildren.push(h('div', { style: { color: 'var(--ogrid-muted, #999)', fontStyle: 'italic' } }, 'No filterable columns'));
110
- }
111
- else {
112
- sb.filterableColumns.forEach((col) => {
113
- const filterKey = col.filterField;
114
- const groupChildren = [
115
- h('div', { style: { fontWeight: '500', marginBottom: '4px', fontSize: '13px' } }, col.name),
116
- ];
117
- if (col.filterType === 'text') {
118
- const filterEntry = sb.filters[filterKey];
119
- const currentVal = filterEntry?.type === 'text' ? filterEntry.value : '';
120
- groupChildren.push(h('input', {
121
- type: 'text',
122
- value: currentVal,
123
- onInput: (e) => {
124
- const val = e.target.value;
125
- sb.onFilterChange(filterKey, val ? { type: 'text', value: val } : undefined);
126
- },
127
- placeholder: `Filter ${col.name}...`,
128
- 'aria-label': `Filter ${col.name}`,
129
- style: { width: '100%', boxSizing: 'border-box', padding: '4px 6px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: '4px' },
130
- }));
131
- }
132
- if (col.filterType === 'multiSelect') {
133
- const options = sb.filterOptions[filterKey] ?? [];
134
- const msChildren = options.map((opt) => {
135
- const msFilter = sb.filters[filterKey];
136
- const selected = msFilter?.type === 'multiSelect' ? msFilter.value.includes(opt) : false;
137
- return h('label', { key: opt, style: { display: 'flex', alignItems: 'center', gap: '4px', padding: '1px 0', cursor: 'pointer', fontSize: '13px' } }, [
138
- h('input', {
139
- type: 'checkbox',
140
- checked: selected,
141
- onChange: (e) => {
142
- const curFilter = sb.filters[filterKey];
143
- const current = curFilter?.type === 'multiSelect' ? curFilter.value : [];
144
- const next = e.target.checked
145
- ? [...current, opt]
146
- : current.filter((v) => v !== opt);
147
- sb.onFilterChange(filterKey, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
148
- },
149
- }),
150
- h('span', null, opt),
151
- ]);
152
- });
153
- groupChildren.push(h('div', { style: { maxHeight: '120px', overflowY: 'auto' }, role: 'group', 'aria-label': `${col.name} options` }, msChildren));
154
- }
155
- if (col.filterType === 'date') {
156
- const dateFilter = sb.filters[filterKey];
157
- const existingValue = dateFilter?.type === 'date' ? dateFilter.value : { from: undefined, to: undefined };
158
- groupChildren.push(h('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px' } }, [
159
- h('label', { style: { display: 'flex', alignItems: 'center', gap: '4px', fontSize: '12px' } }, [
160
- 'From:',
161
- h('input', {
162
- type: 'date',
163
- value: existingValue.from ?? '',
164
- onInput: (e) => {
165
- const from = e.target.value || undefined;
166
- const to = existingValue.to;
167
- sb.onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
168
- },
169
- 'aria-label': `${col.name} from date`,
170
- style: { flex: '1', padding: '2px 4px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: '4px' },
171
- }),
172
- ]),
173
- h('label', { style: { display: 'flex', alignItems: 'center', gap: '4px', fontSize: '12px' } }, [
174
- 'To:',
175
- h('input', {
176
- type: 'date',
177
- value: existingValue.to ?? '',
178
- onInput: (e) => {
179
- const to = e.target.value || undefined;
180
- const from = existingValue.from;
181
- sb.onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
182
- },
183
- 'aria-label': `${col.name} to date`,
184
- style: { flex: '1', padding: '2px 4px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: '4px' },
185
- }),
186
- ]),
187
- ]));
188
- }
189
- panelBodyChildren.push(h('div', { key: col.columnId, style: { marginBottom: '12px' } }, groupChildren));
190
- });
191
- }
192
- }
193
- panelContent = h('div', { role: 'tabpanel', 'aria-label': PANEL_LABELS[sb.activePanel], style: panelContainerStyle }, [
194
- // Panel header
195
- h('div', {
196
- style: {
197
- display: 'flex',
198
- justifyContent: 'space-between',
199
- alignItems: 'center',
200
- padding: '8px 12px',
201
- borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
202
- fontWeight: '600',
203
- },
204
- }, [
205
- h('span', null, PANEL_LABELS[sb.activePanel]),
206
- h('button', {
207
- onClick: () => sb.onPanelChange(null),
208
- style: { border: 'none', background: 'transparent', cursor: 'pointer', fontSize: '16px', color: 'var(--ogrid-fg, #242424)' },
209
- 'aria-label': 'Close panel',
210
- }, '\u00D7'),
211
- ]),
212
- // Panel body
213
- h('div', { style: { flex: '1', overflowY: 'auto', padding: '8px 12px' } }, panelBodyChildren),
214
- ]);
215
- }
216
- const children = [];
217
- if (position === 'left') {
218
- children.push(tabStrip);
219
- if (panelContent)
220
- children.push(panelContent);
221
- }
222
- else {
223
- if (panelContent)
224
- children.push(panelContent);
225
- children.push(tabStrip);
226
- }
227
- return h('div', {
228
- style: { display: 'flex', flexDirection: 'row', flexShrink: '0' },
229
- role: 'complementary',
230
- 'aria-label': 'Side bar',
231
- }, children);
232
- }
233
- /**
234
- * Creates an OGrid component with framework-specific UI bindings.
235
- * All orchestration logic, sidebar, toolbar, and layout are shared.
236
- */
237
- export function createOGrid(ui) {
238
- return defineComponent({
239
- name: 'OGrid',
240
- props: {
241
- gridProps: { type: Object, required: true },
242
- },
243
- setup(props, { expose }) {
244
- const propsRef = computed(() => props.gridProps);
245
- const { dataGridProps, pagination, columnChooser, layout, api } = useOGrid(propsRef);
246
- // Expose the ref container so parent always gets the latest API value
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); });
258
- return () => {
259
- const sideBar = layout.value.sideBarProps;
260
- const hasSideBar = sideBar != null;
261
- const sideBarPosition = sideBar?.position ?? 'right';
262
- // Toolbar
263
- const toolbarChildren = [];
264
- if (layout.value.toolbar) {
265
- toolbarChildren.push(layout.value.toolbar);
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;
292
- // ColumnChooser in toolbar
293
- const toolbarEnd = columnChooser.value.placement === 'toolbar'
294
- ? h(ui.ColumnChooser, {
295
- columns: columnChooser.value.columns,
296
- visibleColumns: columnChooser.value.visibleColumns,
297
- onVisibilityChange: columnChooser.value.onVisibilityChange,
298
- })
299
- : null;
300
- // Pagination
301
- const paginationNode = h(ui.PaginationControls, {
302
- currentPage: pagination.value.page,
303
- pageSize: pagination.value.pageSize,
304
- totalCount: pagination.value.displayTotalCount,
305
- onPageChange: pagination.value.setPage,
306
- onPageSizeChange: (size) => {
307
- pagination.value.setPageSize(size);
308
- },
309
- pageSizeOptions: pagination.value.pageSizeOptions,
310
- entityLabelPlural: pagination.value.entityLabelPlural,
311
- });
312
- // Grid content area
313
- const gridChild = h('div', {
314
- style: { flex: '1', minWidth: '0', minHeight: '0', display: 'flex', flexDirection: 'column' },
315
- }, [
316
- h(ui.DataGridTable, {
317
- gridProps: dataGridProps.value,
318
- }),
319
- ]);
320
- // Main content area (sidebar + grid)
321
- const mainAreaChildren = [];
322
- if (hasSideBar && sideBarPosition === 'left') {
323
- mainAreaChildren.push(renderSideBar(sideBar));
324
- }
325
- mainAreaChildren.push(gridChild);
326
- if (hasSideBar && sideBarPosition !== 'left') {
327
- mainAreaChildren.push(renderSideBar(sideBar));
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;
336
- return h('div', {
337
- class: layout.value.className,
338
- style: rootStyle,
339
- }, [
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)
370
- h('div', {
371
- style: {
372
- display: 'flex',
373
- alignItems: 'center',
374
- padding: '8px 0',
375
- borderTop: '1px solid var(--ogrid-border, rgba(0,0,0,0.12))',
376
- },
377
- }, [paginationNode]),
378
- ]),
379
- ]);
380
- };
381
- },
382
- });
383
- }
@@ -1,33 +0,0 @@
1
- // Main orchestration composables
2
- export { useOGrid } from './useOGrid';
3
- export { useDataGridState } from './useDataGridState';
4
- // Feature composables
5
- export { useActiveCell } from './useActiveCell';
6
- export { useCellEditing } from './useCellEditing';
7
- export { useCellSelection } from './useCellSelection';
8
- export { useClipboard } from './useClipboard';
9
- export { useRowSelection } from './useRowSelection';
10
- export { useKeyboardNavigation } from './useKeyboardNavigation';
11
- export { useFillHandle } from './useFillHandle';
12
- export { useUndoRedo } from './useUndoRedo';
13
- export { useContextMenu } from './useContextMenu';
14
- export { useColumnResize } from './useColumnResize';
15
- export { useFilterOptions } from './useFilterOptions';
16
- export { useDebounce, useDebouncedCallback } from './useDebounce';
17
- export { useLatestRef } from './useLatestRef';
18
- export { useTableLayout } from './useTableLayout';
19
- // Headless state composables
20
- export { useColumnHeaderFilterState } from './useColumnHeaderFilterState';
21
- export { useTextFilterState } from './useTextFilterState';
22
- export { useMultiSelectFilterState } from './useMultiSelectFilterState';
23
- export { usePeopleFilterState } from './usePeopleFilterState';
24
- export { useDateFilterState } from './useDateFilterState';
25
- export { useColumnChooserState } from './useColumnChooserState';
26
- export { useInlineCellEditorState } from './useInlineCellEditorState';
27
- export { useRichSelectState } from './useRichSelectState';
28
- export { useSideBarState } from './useSideBarState';
29
- export { useColumnReorder } from './useColumnReorder';
30
- export { useVirtualScroll } from './useVirtualScroll';
31
- export { useColumnPinning } from './useColumnPinning';
32
- export { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
33
- export { useDataGridTableSetup } from './useDataGridTableSetup';