- {filterColumns.map(col => (
-
{renderFilterInput(col)}
- ))}
+ {renderFilterBar ? (
+ /* External filter bar (e.g. FilterUI from plugin-view) */
+
+ {renderFilterBar({
+ filterColumns: effectiveFilterColumns,
+ values: filterValues,
+ onChange: handleFilterChange,
+ onClear: handleFilterClear,
+ activeCount: activeFilterCount,
+ })}
+ ) : (
+ /* Built-in filter bar (default) */
+ <>
+
+
+ {activeFilterCount > 0 && (
+
+ )}
+
+ {filterBarOpen && (
+
+ {effectiveFilterColumns.map(col => (
+
{renderFilterInput(col)}
+ ))}
+
+ )}
+ >
)}
>
@@ -745,142 +881,169 @@ export function RecordPickerDialog({
)}
- {/* Loading state (initial) */}
- {loading && records.length === 0 && !error && (
-
-
-
Loading…
+ {/* Grid area — external ObjectGrid via renderGrid slot, or built-in table */}
+ {renderGrid ? (
+ /* External grid component (e.g. ObjectGrid from plugin-grid) */
+
+ {renderGrid({
+ columns: resolvedColumns,
+ records,
+ loading,
+ totalCount,
+ currentPage,
+ pageSize,
+ sortField,
+ sortDirection,
+ onSort: handleSort,
+ onPageChange: setCurrentPage,
+ onRowClick: handleRowClick,
+ isSelected,
+ multiple,
+ idField,
+ cellRenderer,
+ })}
- )}
+ ) : (
+ /* Built-in table (default) */
+ <>
+ {/* Loading state (initial) */}
+ {loading && records.length === 0 && !error && (
+
+ )}
- {/* Empty state */}
- {!loading && !error && records.length === 0 && (
-
- )}
+ {/* Empty state */}
+ {!loading && !error && records.length === 0 && (
+
+ )}
- {/* Table */}
- {!error && records.length > 0 && (
-
-
0 ? { tableLayout: 'fixed' } : undefined}>
-
-
- {multiple && (
-
- )}
- {resolvedColumns.map(col => {
- const w = columnWidths[col.field];
- const styleWidth = w ? { width: `${w}px`, minWidth: `${w}px` } : col.width ? { width: col.width } : undefined;
- return (
- handleSort(col.field)}
- aria-sort={sortField === col.field ? (sortDirection === 'asc' ? 'ascending' : 'descending') : 'none'}
- >
-
- {col.label || fieldToLabel(col.field)}
- {renderSortIcon(col.field)}
-
- {/* Column resize handle */}
- {
- const th = e.currentTarget.parentElement;
- const rect = th?.getBoundingClientRect();
- handleResizeStart(e, col.field, rect?.width ?? 100);
- }}
- onClick={e => e.stopPropagation()}
- data-testid={`resize-handle-${col.field}`}
- />
-
- );
- })}
-
-
-
- {records.map((record, idx) => {
- const rid = getRecordId(record);
- const selected = isSelected(record);
- const focused = idx === focusedRow;
-
- return (
- handleRowClick(record)}
- data-testid={`record-row-${rid}`}
- aria-selected={selected}
- >
+ {/* Table */}
+ {!error && records.length > 0 && (
+
+
0 ? { tableLayout: 'fixed' } : undefined}>
+
+
{multiple && (
-
- {selected && }
-
+
)}
- {resolvedColumns.map(col => (
-
- {renderCellContent(record, col)}
-
- ))}
+ {resolvedColumns.map(col => {
+ const w = columnWidths[col.field];
+ const styleWidth = w ? { width: `${w}px`, minWidth: `${w}px` } : col.width ? { width: col.width } : undefined;
+ return (
+ handleSort(col.field)}
+ aria-sort={sortField === col.field ? (sortDirection === 'asc' ? 'ascending' : 'descending') : 'none'}
+ >
+
+ {col.label || fieldToLabel(col.field)}
+ {renderSortIcon(col.field)}
+
+ {/* Column resize handle */}
+ {
+ const th = e.currentTarget.parentElement;
+ const rect = th?.getBoundingClientRect();
+ handleResizeStart(e, col.field, rect?.width ?? 100);
+ }}
+ onClick={e => e.stopPropagation()}
+ data-testid={`resize-handle-${col.field}`}
+ />
+
+ );
+ })}
- );
- })}
-
-
-
- )}
+
+
+ {records.map((record, idx) => {
+ const rid = getRecordId(record);
+ const selected = isSelected(record);
+ const focused = idx === focusedRow;
+
+ return (
+ handleRowClick(record)}
+ data-testid={`record-row-${rid}`}
+ aria-selected={selected}
+ >
+ {multiple && (
+
+ {selected && }
+
+ )}
+ {resolvedColumns.map(col => (
+
+ {renderCellContent(record, col)}
+
+ ))}
+
+ );
+ })}
+
+
+
+ )}
- {/* Pagination */}
- {!error && totalCount > 0 && (
- <>
-
-
-
- {totalCount} {totalCount === 1 ? 'record' : 'records'}
- {totalPages > 1 && ` · Page ${currentPage} of ${totalPages}`}
-
- {totalPages > 1 && (
-
-
-
+ {/* Pagination */}
+ {!error && totalCount > 0 && (
+ <>
+
+
+
+ {totalCount} {totalCount === 1 ? 'record' : 'records'}
+ {totalPages > 1 && ` · Page ${currentPage} of ${totalPages}`}
+
+ {totalPages > 1 && (
+
+
+
+
+ )}
- )}
-
+ >
+ )}
>
)}
From 85c77f9ab3ae16eace2b80e818e2937e103c290a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 18 Mar 2026 10:34:37 +0000
Subject: [PATCH 6/6] fix: improve filter type inference order and option label
generation
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
.../fields/src/widgets/RecordPickerDialog.tsx | 23 +++++++++++++------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/packages/fields/src/widgets/RecordPickerDialog.tsx b/packages/fields/src/widgets/RecordPickerDialog.tsx
index 6f54b55a..ff9d4a17 100644
--- a/packages/fields/src/widgets/RecordPickerDialog.tsx
+++ b/packages/fields/src/widgets/RecordPickerDialog.tsx
@@ -358,23 +358,32 @@ export function RecordPickerDialog({
// Auto-derive from lookupFilters: each filter entry becomes a filterable field
if (lookupFilters && lookupFilters.length > 0) {
return lookupFilters.map(f => {
+ // Infer filter input type from value type first, then fall back to operator
let type: RecordPickerFilterColumn['type'] = 'text';
- if (f.operator === 'gt' || f.operator === 'lt' || f.operator === 'gte' || f.operator === 'lte') {
- type = 'number';
- } else if (f.operator === 'in' || f.operator === 'notIn') {
- type = 'select';
- } else if (typeof f.value === 'boolean') {
+ if (typeof f.value === 'boolean') {
type = 'boolean';
+ } else if (Array.isArray(f.value)) {
+ type = 'select';
} else if (typeof f.value === 'number') {
type = 'number';
+ } else if (f.operator === 'gt' || f.operator === 'lt' || f.operator === 'gte' || f.operator === 'lte') {
+ type = 'number';
+ } else if (f.operator === 'in' || f.operator === 'notIn') {
+ type = 'select';
}
return {
field: f.field,
label: fieldToLabel(f.field),
type,
- // For 'in' filters, derive options from the value array
+ // For array values (in/notIn), derive selectable options
...(Array.isArray(f.value) ? {
- options: (f.value as any[]).map(v => ({ label: String(v), value: v })),
+ options: (f.value as any[]).map(v => {
+ if (v != null && typeof v === 'object') {
+ const obj = v as Record
;
+ return { label: String(obj.name || obj.label || obj.title || v), value: v };
+ }
+ return { label: String(v), value: v };
+ }),
} : {}),
};
});