@@ -49,7 +49,7 @@ import {
4949 EmptyDescription ,
5050 NavigationOverlay ,
5151} from '@object-ui/components' ;
52- import { Plus , Upload , Table as TableIcon , Lock , MoreHorizontal , RotateCcw } from 'lucide-react' ;
52+ import { Plus , Upload , Lock , MoreHorizontal , RotateCcw } from 'lucide-react' ;
5353import { FavoriteMenuItem , useObjectListFavorite } from '../layout/SidebarItemMenu.js' ;
5454import { useMetaItemRefusal } from '../hooks/useMetaItemRefusal.js' ;
5555import { useTenancyPosture } from '../hooks/useTenancyPosture.js' ;
@@ -125,6 +125,7 @@ import {
125125 isSameOptionsValue ,
126126 dispatchViewPatches ,
127127} from './objectViewOptions.js' ;
128+ import { ObjectNotFoundState , useViewTabsFit , keepInlineEditModeForTheSession } from './objectViewChrome.js' ;
128129
129130// objectstack#25992 slice 1: these module-level helpers moved to the sibling
130131// `objectView*.ts` modules above. Every name this file exported stays
@@ -155,6 +156,10 @@ export {
155156 dispatchViewPatches ,
156157} from './objectViewOptions.js' ;
157158
159+ // objectstack#25992 slice 3: `ObjectNotFoundState` moved to `./objectViewChrome.tsx`
160+ // and stays exported from here (`AppContent` imports it by this path).
161+ export { ObjectNotFoundState } from './objectViewChrome.js' ;
162+
158163/**
159164 * Props of the Console `ObjectView`, the route-level object surface
160165 * `@object-ui/app-shell` exports (objectui#7483).
@@ -186,94 +191,6 @@ export interface ConsoleObjectViewProps {
186191 externalRefreshKey ?: number ;
187192}
188193
189- /**
190- * The "object not found" state the route draws for an object it has no
191- * definition of. objectui#13052 — `packageDisabled`: the server answered that
192- * the object exists and its package is disabled (`OBJECT_PACKAGE_DISABLED`),
193- * so the description says that instead. The console route draws it for that
194- * answer (`AppContent`); this module's `ObjectView` draws it for an absence.
195- * Not part of the package entry.
196- */
197- export function ObjectNotFoundState ( { objectName, packageDisabled = false } : { objectName : string | undefined ; packageDisabled ?: boolean } ) {
198- const { t } = useObjectTranslation ( ) ;
199- return (
200- < div className = "h-full p-4 flex items-center justify-center" >
201- < Empty data-testid = { packageDisabled ? 'object-package-disabled' : 'object-not-found' } >
202- < div className = "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted" >
203- < TableIcon className = "h-6 w-6 text-muted-foreground" />
204- </ div >
205- < EmptyTitle > { t ( 'console.objectView.objectNotFound' ) } </ EmptyTitle >
206- < EmptyDescription >
207- { packageDisabled
208- ? t ( 'console.objectView.objectPackageDisabled' , { objectName } )
209- : t ( 'console.objectView.objectNotFoundDescription' , { objectName } ) }
210- { ' ' }
211- { t ( 'console.objectView.objectNotFoundHint' ) }
212- </ EmptyDescription >
213- </ Empty >
214- </ div >
215- ) ;
216- }
217-
218- /**
219- * The list toolbar's inline-edit toggle, as this page wires it: it writes
220- * nothing (objectui#5144, triage's ruling E).
221- *
222- * The view's `inlineEdit` and `userActions.editInline` are the AUTHOR's
223- * permission keys, and `normalizeListViewSchema` folds the first into the
224- * second. `editInline` is on by default since the v18 ruling (objectstack#22605,
225- * objectui#12086), so a view that declares neither key offers the toggle, and
226- * `editInline: false` withdraws it. This toggle used to persist a USER's edit
227- * mode into `inlineEdit` through `persistViewPatch`. After the fold, switching
228- * it off stored `inlineEdit: false`, which reads as "not offered", so the
229- * toggle was gone from the next load and nothing in the console could bring it
230- * back.
231- *
232- * The edit mode is now session state. `ListView` keeps it, and seeds it from
233- * the view's `inlineEdit` on each load. The callback stays wired because
234- * `ListView` offers the wide toolbar toggle only to a host that wires one.
235- *
236- * Recorded costs: the edit mode is not remembered across loads, and an overlay
237- * that already stores `inlineEdit: false` still reads off. That is existing
238- * data, and the maintainer's ruling rejects migrating it. Declaring
239- * `userActions.editInline: true` on such a view brings the toggle back.
240- */
241- /**
242- * Tailwind's `sm` breakpoint (px): from here up the list page draws its
243- * saved-view tabs in the list toolbar; below it the app bar's mobile view
244- * switcher is the page's only view control (objectui#12440).
245- */
246- const VIEW_TABS_MIN_WIDTH = 640 ;
247-
248- /**
249- * Is the viewport at least {@link VIEW_TABS_MIN_WIDTH} wide? The matchMedia
250- * idiom of `useIsMobile` / `useIsWideViewport`, except the first answer is
251- * read synchronously: the tab strip is the first thing on the page's list row,
252- * and resolving it in an effect would draw the row once without it and then
253- * jump. objectui#12440 — this decides whether the strip is HANDED to the list
254- * at all, so below `sm` nothing is passed and nothing is drawn (the CSS
255- * `hidden sm:block` it replaces only hid it).
256- */
257- function viewTabsFitNow ( ) : boolean {
258- return typeof window !== 'undefined' && window . innerWidth >= VIEW_TABS_MIN_WIDTH ;
259- }
260-
261- function useViewTabsFit ( ) : boolean {
262- const [ fits , setFits ] = useState < boolean > ( viewTabsFitNow ) ;
263- useEffect ( ( ) => {
264- if ( typeof window === 'undefined' || typeof window . matchMedia !== 'function' ) return ;
265- const mql = window . matchMedia ( `(min-width: ${ VIEW_TABS_MIN_WIDTH } px)` ) ;
266- const onChange = ( ) => setFits ( viewTabsFitNow ( ) ) ;
267- mql . addEventListener ( 'change' , onChange ) ;
268- return ( ) => mql . removeEventListener ( 'change' , onChange ) ;
269- } , [ ] ) ;
270- return fits ;
271- }
272-
273- function keepInlineEditModeForTheSession ( ) : void {
274- // Deliberately empty: see the docblock.
275- }
276-
277194export function ObjectView ( { dataSource, objects, onEdit, externalRefreshKey } : ConsoleObjectViewProps ) {
278195 const { objectName } = useParams ( ) ;
279196
0 commit comments