diff --git a/packages/app-shell/src/views/InterfaceListPage.tsx b/packages/app-shell/src/views/InterfaceListPage.tsx index c0c832836..22b5d4825 100644 --- a/packages/app-shell/src/views/InterfaceListPage.tsx +++ b/packages/app-shell/src/views/InterfaceListPage.tsx @@ -383,7 +383,10 @@ export function InterfaceListPage({ page, className, onConfigChange, reserveEdit showGroup: false, showColor: false, allowExport: false, - inlineEdit: false, + // Inline record editing is a page-authored property: a list block opts in + // via `userActions.editInline` (default off). When on, clicking a cell + // edits it with the dedicated field widgets, same as the object views. + inlineEdit: userActions.editInline === true, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [objectDefName, viewDefJson, cfg]); diff --git a/packages/app-shell/src/views/ObjectView.tsx b/packages/app-shell/src/views/ObjectView.tsx index 759f035c5..efa3ad0ca 100644 --- a/packages/app-shell/src/views/ObjectView.tsx +++ b/packages/app-shell/src/views/ObjectView.tsx @@ -1416,6 +1416,9 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }: an onHiddenFieldsChange={(hidden: string[]) => { persistViewPatch(viewDef.id, viewDef, { hiddenFields: hidden }); }} + onInlineEditChange={(next: boolean) => { + persistViewPatch(viewDef.id, viewDef, { inlineEdit: next }); + }} onColumnStateChange={(state: { order?: string[]; widths?: Record }) => { persistViewPatch(viewDef.id, viewDef, { columnState: state }); }} diff --git a/packages/plugin-list/src/ListView.tsx b/packages/plugin-list/src/ListView.tsx index 2cb579359..8cab0b1ba 100644 --- a/packages/plugin-list/src/ListView.tsx +++ b/packages/plugin-list/src/ListView.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import { cn, Button, Input, Popover, PopoverContent, PopoverTrigger, FilterBuilder, SortBuilder, NavigationOverlay, GroupingEditor, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, RefreshIndicator, DataEmptyState } from '@object-ui/components'; import type { SortItem } from '@object-ui/components'; -import { Search, SlidersHorizontal, ArrowUpDown, X, EyeOff, Group, Paintbrush, Ruler, Inbox, Download, AlignJustify, Rows4, Rows3, Rows2, Share2, Printer, Plus, Trash2, CheckSquare, AlertTriangle, RotateCw, icons, type LucideIcon } from 'lucide-react'; +import { Search, SlidersHorizontal, ArrowUpDown, X, EyeOff, Pencil, Group, Paintbrush, Ruler, Inbox, Download, AlignJustify, Rows4, Rows3, Rows2, Share2, Printer, Plus, Trash2, CheckSquare, AlertTriangle, RotateCw, icons, type LucideIcon } from 'lucide-react'; import type { FilterGroup } from '@object-ui/components'; import { ViewSwitcherDropdown, ViewType } from './ViewSwitcher'; import { ViewSettingsPopover } from './components/ViewSettingsPopover'; @@ -31,6 +31,8 @@ export interface ListViewProps { onSearchChange?: (search: string) => void; /** Called when the user toggles fields via the Hide Fields popover. */ onHiddenFieldsChange?: (hidden: string[]) => void; + /** Called when the user toggles inline record editing in View settings. */ + onInlineEditChange?: (next: boolean) => void; /** Called when the user resizes/reorders columns in the underlying grid. */ onColumnStateChange?: (state: { order?: string[]; widths?: Record }) => void; /** Callback when a row/item is clicked (overrides NavigationConfig) */ @@ -322,6 +324,7 @@ export const ListView = React.forwardRef(({ onSortChange, onSearchChange, onHiddenFieldsChange, + onInlineEditChange, onColumnStateChange, onRowClick, showViewSwitcher: showViewSwitcherProp, @@ -1639,6 +1642,24 @@ export const ListView = React.forwardRef(({
)} + {/* Inline edit — toggle record editing for this (grid) view. Persists + `inlineEdit` on the view via onInlineEditChange. */} + {currentView === 'grid' && onInlineEditChange && !toolbarFlags.compactToolbar && ( + + )} {/* Hide Fields — hidden on mobile (collapsed into ViewSettingsPopover) */} {toolbarFlags.showHideFields && !toolbarFlags.compactToolbar && ( @@ -1982,6 +2003,9 @@ export const ListView = React.forwardRef(({ showHideFields={toolbarFlags.showHideFields} hiddenFields={hiddenFields} updateHiddenFields={updateHiddenFields} + showInlineEdit={currentView === 'grid'} + inlineEdit={!!schema.inlineEdit} + setInlineEdit={(v) => onInlineEditChange?.(v)} /> )} diff --git a/packages/plugin-list/src/components/ViewSettingsPopover.tsx b/packages/plugin-list/src/components/ViewSettingsPopover.tsx index 4a07d7203..a73a20994 100644 --- a/packages/plugin-list/src/components/ViewSettingsPopover.tsx +++ b/packages/plugin-list/src/components/ViewSettingsPopover.tsx @@ -70,6 +70,11 @@ export interface ViewSettingsPopoverProps { showHideFields?: boolean; hiddenFields?: Set; updateHiddenFields?: (next: Set) => void; + + /** Record editing — toggle inline cell editing (persists `inlineEdit` on the view). */ + showInlineEdit?: boolean; + inlineEdit?: boolean; + setInlineEdit?: (next: boolean) => void; } interface SectionProps { @@ -131,6 +136,9 @@ export function ViewSettingsPopover(props: ViewSettingsPopoverProps) { showHideFields, hiddenFields, updateHiddenFields, + showInlineEdit, + inlineEdit, + setInlineEdit, } = props; const [open, setOpen] = React.useState(false); @@ -141,6 +149,7 @@ export function ViewSettingsPopover(props: ViewSettingsPopoverProps) { !!rowColorConfig?.field, density && density.mode !== 'compact', (hiddenFields?.size ?? 0) > 0, + !!inlineEdit, ].filter(Boolean).length; const DensityIcon = @@ -266,6 +275,28 @@ export function ViewSettingsPopover(props: ViewSettingsPopoverProps) { )} + {showInlineEdit && setInlineEdit && ( +
+ +
+ )} + {showHideFields && hiddenFields && updateHiddenFields && (