Skip to content

Commit ce57ad7

Browse files
os-zhuangclaude
andauthored
feat(app-shell): Studio data grid uses the rich runtime ListView (#2096)
The Data pillar rendered a bare object-grid — a standalone ugly search box, no view/filter/sort/group menus. Switch it to the SAME rich ListView the runtime list pages use, via the plugin ObjectView's renderListView slot (so the object-view still owns data fetching): - Standard list toolbar: view switcher, search, sort, filter, group, hide-fields - Airtable-style inline data management (inlineEdit + addDeleteRecordsInline) - Columns lead with the object's own fields in metadata order, dropping framework-managed/audit fields so the grid opens on meaningful columns - Field authoring (column + add / pencil edit / drag reorder) preserved through the GridFieldAuthoring context Verified live on the showcase env: toolbar + data + columns render, field inspector opens on column edit; type-check + lint clean. Inline-edit write path is correct (data-API update is permission-gated, not broken). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a262397 commit ce57ad7

1 file changed

Lines changed: 72 additions & 2 deletions

File tree

packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import * as React from 'react';
1818
import { useParams, Link } from 'react-router-dom';
1919
import { SchemaRenderer, useAdapter } from '@object-ui/react';
2020
import { GridFieldAuthoringProvider } from '@object-ui/components';
21+
import { ObjectView as PluginObjectView } from '@object-ui/plugin-view';
22+
import { ListView } from '@object-ui/plugin-list';
2123
import {
2224
Boxes,
2325
FileText,
@@ -630,6 +632,58 @@ function nextFieldName(existing: string[]): string {
630632
* affordance, to open ObjectFieldInspector (full type list + per-type config)
631633
* in the right panel; changes persist via the object draft → publish overlay.
632634
*/
635+
/**
636+
* Framework-managed/audit fields. They lead the raw metadata order but aren't
637+
* what a user manages in a data grid, so the Data pillar drops them from the
638+
* column set (mirrors ObjectGrid's regular-vs-system split) to open on the
639+
* meaningful fields first — the same way Airtable hides system columns.
640+
*/
641+
const STUDIO_SYSTEM_FIELD_NAMES = new Set<string>([
642+
'_id', 'id', 'organization_id', 'org_id', 'space_id',
643+
'created_at', 'created_by', 'updated_at', 'updated_by',
644+
'modified_at', 'modified_by', 'created_time', 'modified_time', 'updated_time',
645+
'deleted_at', 'deleted_by',
646+
]);
647+
648+
/**
649+
* Render the Data pillar's records grid using the SAME rich list surface as the
650+
* runtime list pages — the standard toolbar (view switcher, search, sort, filter,
651+
* group, hide-fields) plus Airtable-style inline data management. This is the
652+
* plugin ObjectView's `renderListView` slot, so the object-view still owns data
653+
* fetching while ListView owns the toolbar + grid. Defined at module scope (not
654+
* inline) so it stays a static component reference.
655+
*/
656+
function renderStudioGridList(props: {
657+
schema: Record<string, unknown>;
658+
dataSource: unknown;
659+
onEdit?: (record: Record<string, unknown>) => void;
660+
className?: string;
661+
refreshKey?: number;
662+
}): React.ReactElement {
663+
const { schema: listSchema, dataSource: ds, onEdit, className, refreshKey } = props;
664+
return (
665+
<ListView
666+
schema={
667+
{
668+
...listSchema,
669+
viewType: 'grid',
670+
showSearch: true,
671+
showSort: true,
672+
showFilters: true,
673+
showGroup: true,
674+
showHideFields: true,
675+
inlineEdit: true,
676+
addDeleteRecordsInline: true,
677+
} as never
678+
}
679+
dataSource={ds as never}
680+
onEdit={onEdit}
681+
className={className}
682+
refreshKey={refreshKey}
683+
/>
684+
);
685+
}
686+
633687
function DataPillar({ packageId }: { packageId: string }): React.ReactElement {
634688
const client = useMetadataClient();
635689
const adapter = useAdapter();
@@ -880,9 +934,25 @@ function DataPillar({ packageId }: { packageId: string }): React.ReactElement {
880934
onReorderFields: doReorderFields,
881935
}}
882936
>
883-
<SchemaRenderer
937+
<PluginObjectView
884938
key={`${current.name}:${gridVer}`}
885-
schema={{ type: 'object-view', objectName: current.name } as never}
939+
schema={
940+
{
941+
type: 'object-view',
942+
objectName: current.name,
943+
// No saved view exists in design mode, so show the object's
944+
// own fields as columns (in metadata order), dropping
945+
// framework-managed/audit fields so the grid opens on the
946+
// meaningful columns first — the way Airtable does.
947+
table: {
948+
fields: readFields(objDraft.fields)
949+
.entries.map((e) => e.name)
950+
.filter((n) => !STUDIO_SYSTEM_FIELD_NAMES.has(n)),
951+
},
952+
} as never
953+
}
954+
dataSource={adapter as never}
955+
renderListView={renderStudioGridList}
886956
/>
887957
</GridFieldAuthoringProvider>
888958
</div>

0 commit comments

Comments
 (0)