@alaarab/ogrid-vue-vuetify 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.
@@ -1,4 +1,15 @@
1
- export * from '@alaarab/ogrid-vue';
1
+ export type { ColumnFilterType, IDateFilterValue, IColumnFilterDef, IColumnMeta, IValueParserParams, ICellValueChangedEvent, CellEditorParams, IColumnGroupDef, HeaderCell, HeaderRow, IColumnDefinition, RowId, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, RowSelectionMode, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, SideBarPanelId, ISideBarDef, IVirtualScrollConfig, IColumnReorderConfig, IOGridApi, CsvColumn, StatusBarPart, StatusBarPartsInput, PaginationViewModel, GridContextMenuItem, IColumnHeaderMenuItem, GridContextMenuHandlerProps, ColumnHeaderMenuInput, ColumnHeaderMenuHandlers, ParseValueResult, AggregationResult, GridRowComparatorProps, ColumnPinState, IDropTarget, ICalculateDropTargetParams, IVisibleRange, OverlayRect, ISortState, ArrowNavigationContext, ArrowNavigationResult, } from '@alaarab/ogrid-vue';
2
+ export { toUserLike, isInSelectionRange, normalizeSelectionRange, escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, isFilterConfig, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, areGridRowPropsEqual, isRowInRange, getPinStateForColumn, reorderColumnArray, calculateDropTarget, computeVisibleRange, computeTotalHeight, getScrollTopForRow, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, debounce, measureRange, injectGlobalStyles, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, findCtrlArrowTarget, computeTabNavigation, computeArrowNavigation, applyCellDeletion, rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, applyRangeRowSelection, computeRowSelectionState, formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, applyFillValues, UndoRedoStack, validateColumns, validateRowIds, CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, DEFAULT_DEBOUNCE_MS, PEOPLE_SEARCH_DEBOUNCE_MS, SIDEBAR_TRANSITION_MS, Z_INDEX, } from '@alaarab/ogrid-vue';
3
+ export type { ZIndexKey } from '@alaarab/ogrid-vue';
4
+ export type { IColumnDef, ICellEditorProps, IOGridProps, IOGridClientProps, IOGridServerProps, IOGridDataGridProps, } from '@alaarab/ogrid-vue';
5
+ export type { UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, UseActiveCellResult, EditingCell, UseCellEditingParams, UseCellEditingResult, UseCellSelectionParams, UseCellSelectionResult, UseClipboardParams, UseClipboardResult, UseRowSelectionParams, UseRowSelectionResult, UseKeyboardNavigationParams, UseKeyboardNavigationResult, UseFillHandleParams, UseFillHandleResult, UseUndoRedoParams, UseUndoRedoResult, ContextMenuPosition, UseContextMenuResult, UseColumnResizeParams, UseColumnResizeResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, UseFilterOptionsResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, InlineCellEditorType, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, DebouncedFn, UseColumnPinningParams, UseColumnPinningResult, UseColumnHeaderMenuStateParams, UseColumnHeaderMenuStateResult, UseDataGridTableSetupParams, UseDataGridTableSetupResult, MaybeShallowRef, } from '@alaarab/ogrid-vue';
6
+ export { useOGrid, useDataGridState, useActiveCell, useCellEditing, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useFillHandle, useUndoRedo, useContextMenu, useColumnResize, useColumnReorder, useVirtualScroll, useFilterOptions, useDebounce, useDebouncedCallback, useTableLayout, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useRichSelectState, useSideBarState, useColumnPinning, useColumnHeaderMenuState, useDataGridTableSetup, } from '@alaarab/ogrid-vue';
7
+ export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, CellInteractionProps, } from '@alaarab/ogrid-vue';
8
+ export { getCellInteractionProps } from '@alaarab/ogrid-vue';
9
+ export { createDataGridTable, type IDataGridTableUIBindings } from '@alaarab/ogrid-vue';
10
+ export { createInlineCellEditor, type CreateInlineCellEditorOptions } from '@alaarab/ogrid-vue';
11
+ export { createOGrid, type IOGridUIBindings } from '@alaarab/ogrid-vue';
12
+ export type { SideBarProps, SideBarFilterColumn } from '@alaarab/ogrid-vue';
2
13
  export { OGrid } from './OGrid/OGrid';
3
14
  export { DataGridTable } from './DataGridTable/DataGridTable';
4
15
  export { ColumnHeaderFilter } from './ColumnHeaderFilter/ColumnHeaderFilter';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-vue-vuetify",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
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",
@@ -9,12 +9,12 @@
9
9
  ".": {
10
10
  "types": "./dist/types/index.d.ts",
11
11
  "import": "./dist/esm/index.js",
12
- "require": "./dist/esm/index.js"
12
+ "default": "./dist/esm/index.js"
13
13
  },
14
14
  "./styles/*": "./dist/esm/*"
15
15
  },
16
16
  "scripts": {
17
- "build": "rimraf dist && tsc -p tsconfig.build.json && node scripts/copy-css.js",
17
+ "build": "rimraf dist && tsup && tsc -p tsconfig.build.json",
18
18
  "test": "jest --passWithNoTests",
19
19
  "storybook": "storybook dev -p 6011 --no-open",
20
20
  "build-storybook": "storybook build"
@@ -38,7 +38,7 @@
38
38
  "node": ">=18"
39
39
  },
40
40
  "dependencies": {
41
- "@alaarab/ogrid-vue": "2.1.2"
41
+ "@alaarab/ogrid-vue": "2.1.4"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "vue": "^3.3.0",
@@ -1,73 +0,0 @@
1
- import { defineComponent, computed, h } from 'vue';
2
- import { VBtn, VMenu, VList, VListItem, VDivider } from 'vuetify/components';
3
- import { useColumnChooserState } from '@alaarab/ogrid-vue';
4
- export const ColumnChooser = defineComponent({
5
- name: 'ColumnChooser',
6
- props: {
7
- columns: { type: Array, required: true },
8
- visibleColumns: { type: Object, required: true },
9
- onVisibilityChange: { type: Function, required: true },
10
- },
11
- setup(props) {
12
- const columnsRef = computed(() => props.columns);
13
- const visibleColumnsRef = computed(() => props.visibleColumns);
14
- const state = useColumnChooserState({
15
- columns: columnsRef,
16
- visibleColumns: visibleColumnsRef,
17
- onVisibilityChange: props.onVisibilityChange,
18
- });
19
- return () => {
20
- return h(VMenu, {
21
- modelValue: state.open.value,
22
- 'onUpdate:modelValue': (v) => { state.setOpen(v); },
23
- closeOnContentClick: false,
24
- location: 'bottom end',
25
- }, {
26
- activator: ({ props: activatorProps }) => h(VBtn, {
27
- ...activatorProps,
28
- variant: 'outlined',
29
- size: 'small',
30
- prependIcon: 'mdi-view-column',
31
- appendIcon: state.open.value ? 'mdi-chevron-up' : 'mdi-chevron-down',
32
- }, () => `Column Visibility (${state.visibleCount.value} of ${state.totalCount.value})`),
33
- default: () => h('div', { style: { minWidth: '220px' } }, [
34
- // Header
35
- h('div', {
36
- style: {
37
- padding: '8px 12px',
38
- borderBottom: '1px solid rgba(0,0,0,0.12)',
39
- backgroundColor: 'rgba(0,0,0,0.04)',
40
- fontWeight: '600',
41
- fontSize: '0.875rem',
42
- },
43
- }, `Select Columns (${state.visibleCount.value} of ${state.totalCount.value})`),
44
- // Column list
45
- h(VList, { density: 'compact', style: { maxHeight: '320px', overflowY: 'auto' } }, () => props.columns.map((column) => h(VListItem, { key: column.columnId, style: { minHeight: '32px' } }, () => h('label', {
46
- style: { display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer', width: '100%' },
47
- }, [
48
- h('input', {
49
- type: 'checkbox',
50
- checked: props.visibleColumns.has(column.columnId),
51
- onChange: (e) => state.handleCheckboxChange(column.columnId)(e.target.checked),
52
- }),
53
- h('span', { style: { fontSize: '0.875rem' } }, column.name),
54
- ])))),
55
- // Footer with actions
56
- h(VDivider),
57
- h('div', {
58
- style: {
59
- display: 'flex',
60
- justifyContent: 'flex-end',
61
- gap: '8px',
62
- padding: '8px 12px',
63
- backgroundColor: 'rgba(0,0,0,0.04)',
64
- },
65
- }, [
66
- h(VBtn, { size: 'small', variant: 'text', onClick: state.handleClearAll }, () => 'Clear All'),
67
- h(VBtn, { size: 'small', variant: 'flat', color: 'primary', onClick: state.handleSelectAll }, () => 'Select All'),
68
- ]),
69
- ]),
70
- });
71
- };
72
- },
73
- });
@@ -1,187 +0,0 @@
1
- import { defineComponent, h } from 'vue';
2
- import { VBtn, VIcon, VMenu, VTooltip, VCard } from 'vuetify/components';
3
- import { useColumnHeaderFilterState, } from '@alaarab/ogrid-vue';
4
- import { TextFilterPopover } from './TextFilterPopover';
5
- import { MultiSelectFilterPopover } from './MultiSelectFilterPopover';
6
- import { PeopleFilterPopover } from './PeopleFilterPopover';
7
- // Vuetify component types don't align with h() overloads; cast to Component
8
- const _VBtn = VBtn;
9
- const _VIcon = VIcon;
10
- const _VMenu = VMenu;
11
- const _VTooltip = VTooltip;
12
- const _VCard = VCard;
13
- export const ColumnHeaderFilter = defineComponent({
14
- name: 'ColumnHeaderFilter',
15
- props: {
16
- columnKey: { type: String, required: true },
17
- columnName: { type: String, required: true },
18
- filterType: { type: String, required: true },
19
- isSorted: { type: Boolean, default: false },
20
- isSortedDescending: { type: Boolean, default: false },
21
- onSort: { type: Function, default: undefined },
22
- selectedValues: { type: Array, default: undefined },
23
- onFilterChange: { type: Function, default: undefined },
24
- options: { type: Array, default: () => [] },
25
- isLoadingOptions: { type: Boolean, default: false },
26
- textValue: { type: String, default: '' },
27
- onTextChange: { type: Function, default: undefined },
28
- selectedUser: { type: Object, default: undefined },
29
- onUserChange: { type: Function, default: undefined },
30
- peopleSearch: { type: Function, default: undefined },
31
- dateValue: { type: Object, default: undefined },
32
- onDateChange: { type: Function, default: undefined },
33
- },
34
- setup(props) {
35
- // Pass props directly so useColumnHeaderFilterState accesses reactive prop values
36
- const state = useColumnHeaderFilterState(props);
37
- const renderPopoverContent = () => {
38
- if (props.filterType === 'multiSelect') {
39
- return h(MultiSelectFilterPopover, {
40
- searchText: state.searchText.value,
41
- onSearchChange: state.setSearchText,
42
- options: props.options ?? [],
43
- filteredOptions: state.filteredOptions.value,
44
- selected: state.tempSelected.value,
45
- onOptionToggle: state.handlers.handleCheckboxChange,
46
- onSelectAll: state.handlers.handleSelectAll,
47
- onClearSelection: state.handlers.handleClearSelection,
48
- onApply: state.handlers.handleApplyMultiSelect,
49
- isLoading: props.isLoadingOptions,
50
- });
51
- }
52
- if (props.filterType === 'text') {
53
- return h(TextFilterPopover, {
54
- value: state.tempTextValue.value ?? '',
55
- onValueChange: state.setTempTextValue,
56
- onApply: state.handlers.handleTextApply,
57
- onClear: state.handlers.handleTextClear,
58
- });
59
- }
60
- if (props.filterType === 'people') {
61
- return h(PeopleFilterPopover, {
62
- selectedUser: props.selectedUser,
63
- searchText: state.peopleSearchText.value,
64
- onSearchChange: state.setPeopleSearchText,
65
- suggestions: state.peopleSuggestions.value,
66
- isLoading: state.isPeopleLoading.value,
67
- onUserSelect: state.handlers.handleUserSelect,
68
- onClearUser: state.handlers.handleClearUser,
69
- });
70
- }
71
- if (props.filterType === 'date') {
72
- return h('div', { style: { padding: '12px', display: 'flex', flexDirection: 'column', gap: '8px' } }, [
73
- h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
74
- h('span', { style: { minWidth: '36px', fontSize: '0.75rem' } }, 'From:'),
75
- h('input', {
76
- type: 'date',
77
- value: state.tempDateFrom.value ?? '',
78
- onInput: (e) => state.setTempDateFrom(e.target.value),
79
- style: { flex: '1', padding: '4px 6px' },
80
- }),
81
- ]),
82
- h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, [
83
- h('span', { style: { minWidth: '36px', fontSize: '0.75rem' } }, 'To:'),
84
- h('input', {
85
- type: 'date',
86
- value: state.tempDateTo.value ?? '',
87
- onInput: (e) => state.setTempDateTo(e.target.value),
88
- style: { flex: '1', padding: '4px 6px' },
89
- }),
90
- ]),
91
- h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: '8px', marginTop: '4px' } }, [
92
- h('button', {
93
- onClick: state.handlers.handleDateClear,
94
- disabled: !state.tempDateFrom.value && !state.tempDateTo.value,
95
- style: { padding: '4px 12px', cursor: 'pointer' },
96
- }, 'Clear'),
97
- h('button', {
98
- onClick: state.handlers.handleDateApply,
99
- style: { padding: '4px 12px', cursor: 'pointer' },
100
- }, 'Apply'),
101
- ]),
102
- ]);
103
- }
104
- return null;
105
- };
106
- return () => {
107
- return h('div', {
108
- ref: (el) => { state.headerRef.value = el; },
109
- style: { display: 'flex', alignItems: 'center', width: '100%', minWidth: '0' },
110
- }, [
111
- // Column name with tooltip
112
- h('div', { style: { flex: '1', minWidth: '0', overflow: 'hidden' } }, h(_VTooltip, { text: props.columnName, location: 'top' }, {
113
- activator: ({ props: tipProps }) => h('span', {
114
- ...tipProps,
115
- 'data-header-label': '',
116
- style: {
117
- fontWeight: '600',
118
- fontSize: '0.875rem',
119
- lineHeight: '1.4',
120
- whiteSpace: 'nowrap',
121
- overflow: 'hidden',
122
- textOverflow: 'ellipsis',
123
- display: 'block',
124
- },
125
- }, props.columnName),
126
- })),
127
- // Sort + filter buttons
128
- h('div', { style: { display: 'flex', alignItems: 'center', marginLeft: '4px', flexShrink: '0' } }, [
129
- // Filter icon + menu
130
- ...(props.filterType !== 'none' ? [
131
- h(_VMenu, {
132
- modelValue: state.isFilterOpen.value,
133
- 'onUpdate:modelValue': (v) => state.setFilterOpen(v),
134
- closeOnContentClick: false,
135
- location: 'bottom start',
136
- }, {
137
- activator: ({ props: menuProps }) => h('div', { style: { position: 'relative' } }, [
138
- h(_VBtn, {
139
- ...menuProps,
140
- icon: true,
141
- size: 'x-small',
142
- variant: (state.hasActiveFilter.value || state.isFilterOpen.value) ? 'tonal' : 'text',
143
- color: (state.hasActiveFilter.value || state.isFilterOpen.value) ? 'primary' : 'default',
144
- 'aria-label': `Filter ${props.columnName}`,
145
- title: `Filter ${props.columnName}`,
146
- style: {
147
- opacity: (state.hasActiveFilter.value || state.isFilterOpen.value) ? '1' : '0.7',
148
- },
149
- }, () => h(_VIcon, { size: '16' }, () => 'mdi-filter-variant')),
150
- ...(state.hasActiveFilter.value ? [
151
- h('div', {
152
- style: {
153
- position: 'absolute',
154
- top: '2px',
155
- right: '2px',
156
- width: '6px',
157
- height: '6px',
158
- borderRadius: '50%',
159
- backgroundColor: 'rgb(var(--v-theme-primary))',
160
- zIndex: '1',
161
- },
162
- }),
163
- ] : []),
164
- ]),
165
- default: () => h(_VCard, {
166
- elevation: 8,
167
- ref: (el) => { state.popoverRef.value = el; },
168
- onClick: (e) => e.stopPropagation(),
169
- }, () => [
170
- h('div', {
171
- style: {
172
- borderBottom: '1px solid rgba(0,0,0,0.12)',
173
- padding: '8px 12px',
174
- fontWeight: '600',
175
- fontSize: '0.875rem',
176
- backgroundColor: 'rgb(var(--v-theme-surface))',
177
- },
178
- }, `Filter: ${props.columnName}`),
179
- renderPopoverContent(),
180
- ]),
181
- }),
182
- ] : []),
183
- ]),
184
- ]);
185
- };
186
- },
187
- });
@@ -1,66 +0,0 @@
1
- import { defineComponent, h } from 'vue';
2
- import { VBtn, VTextField, VCheckbox, VProgressCircular, VDivider } from 'vuetify/components';
3
- const _VBtn = VBtn;
4
- const _VTextField = VTextField;
5
- const _VCheckbox = VCheckbox;
6
- const _VProgressCircular = VProgressCircular;
7
- const _VDivider = VDivider;
8
- export const MultiSelectFilterPopover = defineComponent({
9
- name: 'MultiSelectFilterPopover',
10
- props: {
11
- searchText: { type: String, required: true },
12
- onSearchChange: { type: Function, required: true },
13
- options: { type: Array, required: true },
14
- filteredOptions: { type: Array, required: true },
15
- selected: { type: Object, required: true },
16
- onOptionToggle: { type: Function, required: true },
17
- onSelectAll: { type: Function, required: true },
18
- onClearSelection: { type: Function, required: true },
19
- onApply: { type: Function, required: true },
20
- isLoading: { type: Boolean, default: false },
21
- },
22
- setup(props) {
23
- return () => h('div', { style: { width: '280px' } }, [
24
- // Search
25
- h('div', { style: { padding: '12px 12px 4px' } }, [
26
- h(_VTextField, {
27
- modelValue: props.searchText,
28
- 'onUpdate:modelValue': (v) => props.onSearchChange(v),
29
- placeholder: 'Search...',
30
- density: 'compact',
31
- variant: 'outlined',
32
- hideDetails: true,
33
- autocomplete: 'off',
34
- prependInnerIcon: 'mdi-magnify',
35
- onKeydown: (e) => e.stopPropagation(),
36
- }),
37
- h('span', {
38
- style: { display: 'block', marginTop: '4px', fontSize: '0.75rem', color: 'rgba(0,0,0,0.6)' },
39
- }, `${props.filteredOptions.length} of ${props.options.length} options`),
40
- ]),
41
- // Select all / clear
42
- h('div', { style: { display: 'flex', justifyContent: 'space-between', padding: '4px 12px' } }, [
43
- h(_VBtn, { size: 'small', variant: 'text', onClick: props.onSelectAll }, () => `Select All (${props.filteredOptions.length})`),
44
- h(_VBtn, { size: 'small', variant: 'text', onClick: props.onClearSelection }, () => 'Clear'),
45
- ]),
46
- // Options list
47
- h('div', { style: { maxHeight: '240px', overflowY: 'auto', padding: '0 4px' } }, props.isLoading
48
- ? h('div', { style: { display: 'flex', justifyContent: 'center', padding: '16px 0' } }, h(_VProgressCircular, { size: 24, indeterminate: true }))
49
- : props.filteredOptions.length === 0
50
- ? h('div', { style: { padding: '16px 0', textAlign: 'center', fontSize: '0.875rem', color: 'rgba(0,0,0,0.6)' } }, 'No options found')
51
- : props.filteredOptions.map((option) => h('div', { key: option, style: { display: 'flex', alignItems: 'center', minHeight: '32px' } }, h(_VCheckbox, {
52
- modelValue: props.selected.has(option),
53
- label: option,
54
- density: 'compact',
55
- hideDetails: true,
56
- 'onUpdate:modelValue': (checked) => props.onOptionToggle(option, checked),
57
- })))),
58
- // Footer
59
- h(_VDivider),
60
- h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: '8px', padding: '8px 12px' } }, [
61
- h(_VBtn, { size: 'small', variant: 'text', onClick: props.onClearSelection }, () => 'Clear'),
62
- h(_VBtn, { size: 'small', variant: 'flat', color: 'primary', onClick: props.onApply }, () => 'Apply'),
63
- ]),
64
- ]);
65
- },
66
- });
@@ -1,81 +0,0 @@
1
- import { defineComponent, h } from 'vue';
2
- import { VBtn, VTextField, VProgressCircular, VAvatar, VIcon, VDivider } from 'vuetify/components';
3
- const _VBtn = VBtn;
4
- const _VTextField = VTextField;
5
- const _VProgressCircular = VProgressCircular;
6
- const _VAvatar = VAvatar;
7
- const _VIcon = VIcon;
8
- const _VDivider = VDivider;
9
- export const PeopleFilterPopover = defineComponent({
10
- name: 'PeopleFilterPopover',
11
- props: {
12
- selectedUser: { type: Object, default: undefined },
13
- searchText: { type: String, required: true },
14
- onSearchChange: { type: Function, required: true },
15
- suggestions: { type: Array, required: true },
16
- isLoading: { type: Boolean, default: false },
17
- onUserSelect: { type: Function, required: true },
18
- onClearUser: { type: Function, required: true },
19
- },
20
- setup(props) {
21
- return () => h('div', { style: { width: '300px' } }, [
22
- // Selected user display
23
- ...(props.selectedUser ? [
24
- h('div', { style: { padding: '12px 12px 8px', borderBottom: '1px solid rgba(0,0,0,0.12)' } }, [
25
- h('span', { style: { fontSize: '0.75rem', color: 'rgba(0,0,0,0.6)' } }, 'Currently filtered by:'),
26
- h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginTop: '4px' } }, [
27
- h(_VAvatar, { size: 32, image: props.selectedUser.photo }, () => props.selectedUser?.displayName?.[0] ?? ''),
28
- h('div', { style: { flex: '1', minWidth: '0' } }, [
29
- h('div', { style: { fontSize: '0.875rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, props.selectedUser?.displayName),
30
- h('div', { style: { fontSize: '0.75rem', color: 'rgba(0,0,0,0.6)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, props.selectedUser?.email),
31
- ]),
32
- h(_VBtn, {
33
- icon: true,
34
- size: 'x-small',
35
- variant: 'text',
36
- 'aria-label': 'Remove filter',
37
- onClick: props.onClearUser,
38
- }, () => h(_VIcon, { size: '16' }, () => 'mdi-close')),
39
- ]),
40
- ]),
41
- ] : []),
42
- // Search input
43
- h('div', { style: { padding: '12px 12px 4px' } }, h(_VTextField, {
44
- modelValue: props.searchText,
45
- 'onUpdate:modelValue': (v) => props.onSearchChange(v),
46
- placeholder: 'Search for a person...',
47
- density: 'compact',
48
- variant: 'outlined',
49
- hideDetails: true,
50
- autocomplete: 'off',
51
- prependInnerIcon: 'mdi-magnify',
52
- onKeydown: (e) => e.stopPropagation(),
53
- })),
54
- // Suggestions list
55
- h('div', { style: { maxHeight: '240px', overflowY: 'auto' } }, props.isLoading && props.searchText.trim()
56
- ? h('div', { style: { display: 'flex', justifyContent: 'center', padding: '16px 0' } }, h(_VProgressCircular, { size: 24, indeterminate: true }))
57
- : props.suggestions.length === 0 && props.searchText.trim()
58
- ? h('div', { style: { padding: '16px 0', textAlign: 'center', fontSize: '0.875rem', color: 'rgba(0,0,0,0.6)' } }, 'No results found')
59
- : props.searchText.trim()
60
- ? props.suggestions.map((user) => h('div', {
61
- key: user.id || user.email || user.displayName,
62
- style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '8px 12px', cursor: 'pointer' },
63
- onClick: () => props.onUserSelect(user),
64
- onMouseenter: (e) => { e.currentTarget.style.backgroundColor = 'rgba(0,0,0,0.04)'; },
65
- onMouseleave: (e) => { e.currentTarget.style.backgroundColor = ''; },
66
- }, [
67
- h(_VAvatar, { size: 32, image: user.photo }, () => user.displayName?.[0] ?? ''),
68
- h('div', { style: { flex: '1', minWidth: '0' } }, [
69
- h('div', { style: { fontSize: '0.875rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, user.displayName),
70
- h('div', { style: { fontSize: '0.75rem', color: 'rgba(0,0,0,0.6)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, user.email),
71
- ]),
72
- ]))
73
- : h('div', { style: { padding: '16px 0', textAlign: 'center', fontSize: '0.875rem', color: 'rgba(0,0,0,0.6)' } }, 'Type to search...')),
74
- // Clear filter button
75
- ...(props.selectedUser ? [
76
- h(_VDivider),
77
- h('div', { style: { padding: '8px 12px' } }, h(_VBtn, { size: 'small', variant: 'text', block: true, onClick: props.onClearUser }, () => 'Clear Filter')),
78
- ] : []),
79
- ]);
80
- },
81
- });
@@ -1,38 +0,0 @@
1
- import { defineComponent, h } from 'vue';
2
- import { VBtn, VTextField } from 'vuetify/components';
3
- const _VBtn = VBtn;
4
- const _VTextField = VTextField;
5
- export const TextFilterPopover = defineComponent({
6
- name: 'TextFilterPopover',
7
- props: {
8
- value: { type: String, required: true },
9
- onValueChange: { type: Function, required: true },
10
- onApply: { type: Function, required: true },
11
- onClear: { type: Function, required: true },
12
- },
13
- setup(props) {
14
- return () => h('div', { style: { width: '260px' } }, [
15
- h('div', { style: { padding: '12px' } }, h(_VTextField, {
16
- modelValue: props.value,
17
- 'onUpdate:modelValue': (v) => props.onValueChange(v),
18
- placeholder: 'Enter search term...',
19
- density: 'compact',
20
- variant: 'outlined',
21
- hideDetails: true,
22
- autocomplete: 'off',
23
- prependInnerIcon: 'mdi-magnify',
24
- onKeydown: (e) => {
25
- e.stopPropagation();
26
- if (e.key === 'Enter') {
27
- e.preventDefault();
28
- props.onApply();
29
- }
30
- },
31
- })),
32
- h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: '8px', padding: '0 12px 12px' } }, [
33
- h(_VBtn, { size: 'small', variant: 'text', disabled: !props.value, onClick: props.onClear }, () => 'Clear'),
34
- h(_VBtn, { size: 'small', variant: 'flat', color: 'primary', onClick: props.onApply }, () => 'Apply'),
35
- ]),
36
- ]);
37
- },
38
- });
@@ -1 +0,0 @@
1
- export { ColumnHeaderFilter } from './ColumnHeaderFilter';
@@ -1,79 +0,0 @@
1
- import { defineComponent, h, computed } from 'vue';
2
- import { VMenu, VList, VListItem, VDivider } from 'vuetify/components';
3
- import { getColumnHeaderMenuItems } from '@alaarab/ogrid-vue';
4
- export const ColumnHeaderMenu = defineComponent({
5
- name: 'ColumnHeaderMenu',
6
- props: {
7
- isOpen: { type: Boolean, required: true },
8
- anchorElement: { type: Object, default: null },
9
- onClose: { type: Function, required: true },
10
- onPinLeft: { type: Function, required: true },
11
- onPinRight: { type: Function, required: true },
12
- onUnpin: { type: Function, required: true },
13
- onSortAsc: { type: Function, required: true },
14
- onSortDesc: { type: Function, required: true },
15
- onClearSort: { type: Function, required: true },
16
- onAutosizeThis: { type: Function, required: true },
17
- onAutosizeAll: { type: Function, required: true },
18
- canPinLeft: { type: Boolean, required: true },
19
- canPinRight: { type: Boolean, required: true },
20
- canUnpin: { type: Boolean, required: true },
21
- currentSort: { type: String, default: null },
22
- isSortable: { type: Boolean, default: true },
23
- isResizable: { type: Boolean, default: true },
24
- },
25
- setup(props) {
26
- const handleOpenChange = (open) => {
27
- if (!open) {
28
- props.onClose();
29
- }
30
- };
31
- const items = computed(() => getColumnHeaderMenuItems({
32
- canPinLeft: props.canPinLeft,
33
- canPinRight: props.canPinRight,
34
- canUnpin: props.canUnpin,
35
- currentSort: props.currentSort,
36
- isSortable: props.isSortable,
37
- isResizable: props.isResizable,
38
- }));
39
- const handlers = {
40
- pinLeft: props.onPinLeft,
41
- pinRight: props.onPinRight,
42
- unpin: props.onUnpin,
43
- sortAsc: props.onSortAsc,
44
- sortDesc: props.onSortDesc,
45
- clearSort: props.onClearSort,
46
- autosizeThis: props.onAutosizeThis,
47
- autosizeAll: props.onAutosizeAll,
48
- };
49
- const getHandler = (itemId) => handlers[itemId] || (() => { });
50
- return () => {
51
- // If no anchor element or menu is closed, don't render
52
- if (!props.anchorElement)
53
- return null;
54
- return h(VMenu, {
55
- modelValue: props.isOpen,
56
- 'onUpdate:modelValue': handleOpenChange,
57
- location: 'bottom start',
58
- // Use target prop instead of activator for programmatic positioning
59
- target: props.anchorElement,
60
- }, {
61
- default: () => h(VList, { density: 'compact', 'aria-label': 'Column options' }, () => {
62
- const children = [];
63
- items.value.forEach((item) => {
64
- children.push(h(VListItem, {
65
- key: item.id,
66
- disabled: item.disabled,
67
- onClick: () => { getHandler(item.id)(); },
68
- }, () => item.label));
69
- // Add divider after item to separate sections
70
- if (item.divider) {
71
- children.push(h(VDivider, { key: `divider-${item.id}` }));
72
- }
73
- });
74
- return children;
75
- }),
76
- });
77
- };
78
- },
79
- });
@@ -1,28 +0,0 @@
1
- import { h } from 'vue';
2
- import { VCheckbox, VProgressCircular } from 'vuetify/components';
3
- import { createDataGridTable } from '@alaarab/ogrid-vue';
4
- import { ColumnHeaderFilter } from '../ColumnHeaderFilter';
5
- import { ColumnHeaderMenu } from '../ColumnHeaderMenu/ColumnHeaderMenu';
6
- import { InlineCellEditor } from './InlineCellEditor';
7
- import { GridContextMenu } from './GridContextMenu';
8
- import { renderEmptyState } from './EmptyState';
9
- import './DataGridTable.css';
10
- export const DataGridTable = createDataGridTable({
11
- renderCheckbox: ({ modelValue, indeterminate, ariaLabel, onChange }) => h(VCheckbox, {
12
- modelValue,
13
- indeterminate,
14
- hideDetails: true,
15
- density: 'compact',
16
- 'aria-label': ariaLabel,
17
- 'onUpdate:modelValue': (c) => onChange(!!c),
18
- }),
19
- renderSpinner: (message) => h('div', { class: 'ogrid-loading-inner' }, [
20
- h(VProgressCircular, { size: 24, indeterminate: true }),
21
- h('span', { class: 'ogrid-loading-message' }, message),
22
- ]),
23
- ColumnHeaderFilter,
24
- ColumnHeaderMenu,
25
- InlineCellEditor,
26
- GridContextMenu,
27
- renderEmptyState: (emptyState) => renderEmptyState({ emptyState }),
28
- });
@@ -1,22 +0,0 @@
1
- import { h } from 'vue';
2
- import { VBtn } from 'vuetify/components';
3
- export function renderEmptyState({ emptyState }) {
4
- return h('div', { class: 'ogrid-empty-state' }, emptyState.render
5
- ? [emptyState.render()]
6
- : [
7
- h('div', { class: 'ogrid-empty-state-title' }, 'No results found'),
8
- h('div', { class: 'ogrid-empty-state-message' }, emptyState.message != null
9
- ? String(emptyState.message)
10
- : emptyState.hasActiveFilters
11
- ? [
12
- 'No items match your current filters. Try adjusting your search or ',
13
- h(VBtn, {
14
- variant: 'text',
15
- size: 'small',
16
- onClick: emptyState.onClearAll,
17
- }, () => 'clear all filters'),
18
- ' to see all items.',
19
- ]
20
- : 'There are no items available at this time.'),
21
- ]);
22
- }