Skip to content

Commit d42111b

Browse files
Copilothotlong
andcommitted
fix: i18n compliance, row selection auto-enable, column width, border contrast improvements
- Replace all hardcoded UI strings in data-table.tsx with t() calls - Replace hardcoded strings in ListView.tsx with t() calls - Add missing i18n keys to en.ts and zh.ts locale files - Auto-enable multi-select when bulk actions are defined in ObjectGrid - Reduce index/selection column widths from w-12 to w-10 - Increase row border contrast from border-border/50 to border-border - Fix filler row colSpan to include showRowNumbers Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent b22d601 commit d42111b

File tree

5 files changed

+83
-31
lines changed

5 files changed

+83
-31
lines changed

packages/components/src/renderers/complex/data-table.tsx

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@ const TABLE_DEFAULT_TRANSLATIONS: Record<string, string> = {
6868
'table.rowsPerPage': 'Rows per page',
6969
'table.pageInfo': 'Page {{current}} of {{total}}',
7070
'table.totalRecords': '{{count}} total',
71+
'table.noResults': 'No results found',
72+
'table.noResultsHint': 'Try adjusting your filters or search query.',
73+
'table.sortAsc': 'Sort ascending',
74+
'table.sortDesc': 'Sort descending',
75+
'table.hideColumn': 'Hide column',
76+
'table.cancelAll': 'Cancel All',
77+
'table.saveAll': 'Save All ({{count}})',
78+
'table.exportCSV': 'Export CSV',
79+
'table.addRecord': 'Add record',
80+
'table.open': 'Open',
81+
'table.search': 'Search...',
82+
'table.modified': '{{count}} row(s) modified',
83+
'table.selected': '{{count}} selected',
84+
'common.actions': 'Actions',
7185
};
7286

7387
/**
@@ -650,7 +664,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
650664
<div className="relative w-full sm:max-w-sm flex-1">
651665
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
652666
<Input
653-
placeholder="Search..."
667+
placeholder={t('table.search')}
654668
value={searchQuery}
655669
onChange={(e) => {
656670
setSearchQuery(e.target.value);
@@ -666,7 +680,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
666680
{hasPendingChanges && (
667681
<>
668682
<div className="text-sm text-muted-foreground">
669-
{pendingChanges.size} row{pendingChanges.size > 1 ? 's' : ''} modified
683+
{t('table.modified', { count: pendingChanges.size })}
670684
</div>
671685
<Button
672686
variant="outline"
@@ -675,7 +689,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
675689
disabled={isSaving}
676690
>
677691
<X className="h-4 w-4 mr-2" />
678-
Cancel All
692+
{t('table.cancelAll')}
679693
</Button>
680694
<Button
681695
variant="default"
@@ -684,7 +698,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
684698
disabled={isSaving}
685699
>
686700
<Save className="h-4 w-4 mr-2" />
687-
Save All ({pendingChanges.size})
701+
{t('table.saveAll', { count: pendingChanges.size })}
688702
</Button>
689703
</>
690704
)}
@@ -697,13 +711,13 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
697711
disabled={sortedData.length === 0}
698712
>
699713
<Download className="h-4 w-4 mr-2" />
700-
Export CSV
714+
{t('table.exportCSV')}
701715
</Button>
702716
)}
703717

704718
{selectable && selectedRowIds.size > 0 && (
705719
<div className="text-sm text-muted-foreground">
706-
{selectedRowIds.size} selected
720+
{t('table.selected', { count: selectedRowIds.size })}
707721
</div>
708722
)}
709723
</div>
@@ -717,15 +731,15 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
717731
<TableHeader className="sticky top-0 bg-muted/30 z-10">
718732
<TableRow>
719733
{selectable && (
720-
<TableHead className={cn("w-12 bg-muted/30", frozenColumns > 0 && "sticky left-0 z-20")}>
734+
<TableHead className={cn("w-10 bg-muted/30", frozenColumns > 0 && "sticky left-0 z-20")}>
721735
<Checkbox
722736
checked={allPageRowsSelected ? true : somePageRowsSelected ? 'indeterminate' : false}
723737
onCheckedChange={handleSelectAll}
724738
/>
725739
</TableHead>
726740
)}
727741
{showRowNumbers && (
728-
<TableHead className={cn("w-12 bg-muted/30 text-center", frozenColumns > 0 && "sticky z-20")} style={frozenColumns > 0 ? { left: selectable ? 48 : 0 } : undefined}>
742+
<TableHead className={cn("w-10 bg-muted/30 text-center", frozenColumns > 0 && "sticky z-20")} style={frozenColumns > 0 ? { left: selectable ? 40 : 0 } : undefined}>
729743
<span className="text-xs text-muted-foreground">#</span>
730744
</TableHead>
731745
)}
@@ -741,7 +755,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
741755
return sum + (typeof w === 'number' ? w : w ? parseInt(String(w), 10) || 150 : 150);
742756
}
743757
return sum;
744-
}, (selectable ? 48 : 0) + (showRowNumbers ? 48 : 0))
758+
}, (selectable ? 40 : 0) + (showRowNumbers ? 40 : 0))
745759
: undefined;
746760

747761
return (
@@ -797,7 +811,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
797811
);
798812
})}
799813
{rowActions && (
800-
<TableHead className="w-24 text-right bg-muted/30">Actions</TableHead>
814+
<TableHead className="w-24 text-right bg-muted/30">{t('common.actions')}</TableHead>
801815
)}
802816
</TableRow>
803817
</TableHeader>
@@ -811,8 +825,8 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
811825
>
812826
<div className="flex flex-col items-center justify-center gap-2">
813827
<Search className="h-8 w-8 text-muted-foreground/50" />
814-
<p>No results found</p>
815-
<p className="text-xs text-muted-foreground/50">Try adjusting your filters or search query.</p>
828+
<p>{t('table.noResults')}</p>
829+
<p className="text-xs text-muted-foreground/50">{t('table.noResultsHint')}</p>
816830
</div>
817831
</TableCell>
818832
</TableRow>
@@ -844,7 +858,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
844858
key={rowId}
845859
data-state={isSelected ? 'selected' : undefined}
846860
className={cn(
847-
"bg-background border-b border-border/50 hover:bg-muted/30 group/row",
861+
"bg-background border-b border-border hover:bg-muted/30 group/row",
848862
schema.onRowClick && "cursor-pointer",
849863
rowHasChanges && "bg-amber-50 dark:bg-amber-950/20",
850864
rowClassName && rowClassName(row, rowIndex)
@@ -879,7 +893,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
879893
</TableCell>
880894
)}
881895
{showRowNumbers && (
882-
<TableCell className={cn("text-center w-12 relative", frozenColumns > 0 && "sticky z-10 bg-background")} style={frozenColumns > 0 ? { left: selectable ? 48 : 0 } : undefined}>
896+
<TableCell className={cn("text-center w-10 relative", frozenColumns > 0 && "sticky z-10 bg-background")} style={frozenColumns > 0 ? { left: selectable ? 40 : 0 } : undefined}>
883897
<span className={cn("text-xs text-muted-foreground tabular-nums select-none", selectable ? "group-hover/row:hidden" : "group-hover/row:invisible")}>
884898
{globalIndex + 1}
885899
</span>
@@ -902,7 +916,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
902916
}}
903917
title="Open record"
904918
>
905-
<span>Open</span>
919+
<span>{t('table.open')}</span>
906920
<ChevronRight className="h-3 w-3" />
907921
</button>
908922
)}
@@ -923,7 +937,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
923937
return sum + (typeof w === 'number' ? w : w ? parseInt(String(w), 10) || 150 : 150);
924938
}
925939
return sum;
926-
}, (selectable ? 48 : 0) + (showRowNumbers ? 48 : 0))
940+
}, (selectable ? 40 : 0) + (showRowNumbers ? 40 : 0))
927941
: undefined;
928942

929943
return (
@@ -1016,7 +1030,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
10161030
{/* Add record row (Airtable-style) */}
10171031
{showAddRow && (
10181032
<TableRow
1019-
className="hover:bg-muted/30 cursor-pointer border-b border-border/50"
1033+
className="hover:bg-muted/30 cursor-pointer border-b border-border"
10201034
data-testid="add-record-row"
10211035
onClick={() => schema.onAddRecord?.()}
10221036
>
@@ -1026,15 +1040,15 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
10261040
>
10271041
<span className="flex items-center gap-1.5 text-muted-foreground text-sm hover:text-foreground transition-colors">
10281042
<Plus className="h-3.5 w-3.5" />
1029-
Add record
1043+
{t('table.addRecord')}
10301044
</span>
10311045
</TableCell>
10321046
</TableRow>
10331047
)}
10341048
{/* Filler rows to maintain height consistency (only when pagination is enabled) */}
10351049
{pagination && paginatedData.length > 0 && Array.from({ length: Math.max(0, pageSize - paginatedData.length) }).map((_, i) => (
10361050
<TableRow key={`empty-${i}`} className="hover:bg-transparent">
1037-
<TableCell colSpan={columns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0)} className="h-[52px] p-0" />
1051+
<TableCell colSpan={columns.length + (selectable ? 1 : 0) + (showRowNumbers ? 1 : 0) + (rowActions ? 1 : 0)} className="h-[52px] p-0" />
10381052
</TableRow>
10391053
))}
10401054
</>
@@ -1130,7 +1144,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
11301144
}}
11311145
>
11321146
<ChevronUp className="h-3.5 w-3.5" />
1133-
Sort ascending
1147+
{t('table.sortAsc')}
11341148
</button>
11351149
<button
11361150
type="button"
@@ -1142,7 +1156,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
11421156
}}
11431157
>
11441158
<ChevronDown className="h-3.5 w-3.5" />
1145-
Sort descending
1159+
{t('table.sortDesc')}
11461160
</button>
11471161
<div className="my-1 h-px bg-border" />
11481162
</>
@@ -1153,7 +1167,7 @@ const DataTableRenderer = ({ schema }: { schema: DataTableSchema }) => {
11531167
onClick={() => hideColumn(contextMenu.columnKey)}
11541168
>
11551169
<X className="h-3.5 w-3.5" />
1156-
Hide column
1170+
{t('table.hideColumn')}
11571171
</button>
11581172
</div>
11591173
)}

packages/i18n/src/locales/en.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ const en = {
7474
unfreezeColumn: 'Unfreeze column',
7575
pageInfo: 'Page {{current}} of {{total}}',
7676
totalRecords: '{{count}} total',
77+
noResults: 'No results found',
78+
noResultsHint: 'Try adjusting your filters or search query.',
79+
cancelAll: 'Cancel All',
80+
saveAll: 'Save All ({{count}})',
81+
addRecord: 'Add record',
82+
open: 'Open',
83+
search: 'Search...',
84+
modified: '{{count}} row(s) modified',
85+
selected: '{{count}} selected',
7786
},
7887
grid: {
7988
actions: 'Actions',
@@ -122,6 +131,13 @@ const en = {
122131
hideFields: 'Hide fields',
123132
noItems: 'No items found',
124133
noItemsMessage: 'There are no records to display. Try adjusting your filters or adding new data.',
134+
showAll: 'Show all',
135+
pullToRefresh: 'Pull to refresh',
136+
refreshing: 'Refreshing…',
137+
share: 'Share',
138+
print: 'Print',
139+
hideFieldsTitle: 'Hide Fields',
140+
dataLimitReached: 'Showing first {{limit}} records. More data may be available.',
125141
},
126142
kanban: {
127143
addCard: 'Add card',

packages/i18n/src/locales/zh.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ const zh = {
7474
unfreezeColumn: '取消冻结列',
7575
pageInfo: '第 {{current}} 页,共 {{total}} 页',
7676
totalRecords: '共 {{count}} 条',
77+
noResults: '未找到结果',
78+
noResultsHint: '请尝试调整筛选条件或搜索关键词。',
79+
cancelAll: '全部取消',
80+
saveAll: '全部保存 ({{count}})',
81+
addRecord: '添加记录',
82+
open: '打开',
83+
search: '搜索...',
84+
modified: '{{count}} 行已修改',
85+
selected: '已选择 {{count}} 项',
7786
},
7887
grid: {
7988
actions: '操作',
@@ -122,6 +131,13 @@ const zh = {
122131
hideFields: '隐藏字段',
123132
noItems: '未找到项目',
124133
noItemsMessage: '没有可显示的记录。请尝试调整筛选条件或添加新数据。',
134+
showAll: '显示全部',
135+
pullToRefresh: '下拉刷新',
136+
refreshing: '刷新中…',
137+
share: '分享',
138+
print: '打印',
139+
hideFieldsTitle: '隐藏字段',
140+
dataLimitReached: '显示前 {{limit}} 条记录。可能有更多数据。',
125141
},
126142
kanban: {
127143
addCard: '添加卡片',

packages/plugin-grid/src/ObjectGrid.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,12 +1065,18 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
10651065
: (schema.frozenColumns ?? 1);
10661066

10671067
// Determine selection mode (support both new and legacy formats)
1068+
// Auto-enable 'multiple' selection when bulk actions are defined
1069+
const effectiveBulkActions = schema.batchActions ?? (schema as any).bulkActions;
1070+
const hasBulkActions = effectiveBulkActions && effectiveBulkActions.length > 0;
10681071
let selectionMode: 'none' | 'single' | 'multiple' | boolean = false;
10691072
if (schema.selection?.type) {
10701073
selectionMode = schema.selection.type === 'none' ? false : schema.selection.type;
10711074
} else if (schema.selectable !== undefined) {
10721075
// Legacy support
10731076
selectionMode = schema.selectable;
1077+
} else if (hasBulkActions) {
1078+
// Auto-enable multi-select when bulk actions exist
1079+
selectionMode = 'multiple';
10741080
}
10751081

10761082
// Determine pagination settings (support both new and legacy formats)
@@ -1503,9 +1509,6 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
15031509
</div>
15041510
) : null;
15051511

1506-
// Bulk actions — support both batchActions (ObjectUI) and bulkActions (spec) names
1507-
const effectiveBulkActions = schema.batchActions ?? (schema as any).bulkActions;
1508-
15091512
// Render grid content: grouped (multiple tables with headers) or flat (single table)
15101513
const gridContent = isGrouped ? (
15111514
<div className="space-y-2">

packages/plugin-list/src/ListView.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ const LIST_DEFAULT_TRANSLATIONS: Record<string, string> = {
216216
'list.addRecord': 'Add record',
217217
'list.tabs': 'Tabs',
218218
'list.allRecords': 'All Records',
219+
'list.share': 'Share',
220+
'list.print': 'Print',
221+
'list.hideFieldsTitle': 'Hide Fields',
219222
};
220223

221224
/**
@@ -1038,7 +1041,7 @@ export const ListView: React.FC<ListViewProps> = ({
10381041
className="flex items-center justify-center text-xs text-muted-foreground"
10391042
style={{ height: pullDistance }}
10401043
>
1041-
{isRefreshing ? 'Refreshing…' : 'Pull to refresh'}
1044+
{isRefreshing ? t('list.refreshing') : t('list.pullToRefresh')}
10421045
</div>
10431046
)}
10441047
{/* Airtable-style Toolbar — Row 1: View tabs */}
@@ -1100,7 +1103,7 @@ export const ListView: React.FC<ListViewProps> = ({
11001103
)}
11011104
>
11021105
<EyeOff className="h-3.5 w-3.5 mr-1.5" />
1103-
<span className="hidden sm:inline">Hide fields</span>
1106+
<span className="hidden sm:inline">{t('list.hideFields')}</span>
11041107
{hiddenFields.size > 0 && (
11051108
<span className="ml-1 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-primary/10 text-[10px] font-medium text-primary">
11061109
{hiddenFields.size}
@@ -1111,10 +1114,10 @@ export const ListView: React.FC<ListViewProps> = ({
11111114
<PopoverContent align="start" className="w-64 p-3">
11121115
<div className="space-y-2">
11131116
<div className="flex items-center justify-between border-b pb-2">
1114-
<h4 className="font-medium text-sm">Hide Fields</h4>
1117+
<h4 className="font-medium text-sm">{t('list.hideFieldsTitle')}</h4>
11151118
{hiddenFields.size > 0 && (
11161119
<Button variant="ghost" size="sm" className="h-6 px-2 text-xs" onClick={() => setHiddenFields(new Set())}>
1117-
Show all
1120+
{t('list.showAll')}
11181121
</Button>
11191122
)}
11201123
</div>
@@ -1410,7 +1413,7 @@ export const ListView: React.FC<ListViewProps> = ({
14101413
data-testid="share-button"
14111414
>
14121415
<Share2 className="h-3.5 w-3.5 mr-1.5" />
1413-
<span className="hidden sm:inline">Share</span>
1416+
<span className="hidden sm:inline">{t('list.share')}</span>
14141417
</Button>
14151418
)}
14161419

@@ -1424,7 +1427,7 @@ export const ListView: React.FC<ListViewProps> = ({
14241427
data-testid="print-button"
14251428
>
14261429
<Printer className="h-3.5 w-3.5 mr-1.5" />
1427-
<span className="hidden sm:inline">Print</span>
1430+
<span className="hidden sm:inline">{t('list.print')}</span>
14281431
</Button>
14291432
)}
14301433

0 commit comments

Comments
 (0)