@object-ui/layout 17.3.0 → 17.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,38 +17,87 @@ pnpm add @object-ui/layout
17
17
  ```
18
18
 
19
19
  **Peer Dependencies:**
20
- - `react` >= 18.0.0
21
- - `react-dom` >= 18.0.0
22
- - `react-router-dom` >= 6.0.0
20
+ - `react` ^18.0.0 || ^19.0.0
21
+ - `react-dom` ^18.0.0 || ^19.0.0
22
+ - `react-router-dom` ^6.0.0 || ^7.0.0
23
+
24
+ ## Registration
25
+
26
+ Importing this package registers its component keys (`page-header`, `page:card`,
27
+ `responsive-grid`, `navigation-renderer`, `app-schema-renderer`) on the
28
+ `ComponentRegistry` as a module load side effect, so the side-effect-only import
29
+ is enough:
30
+
31
+ ```typescript
32
+ import '@object-ui/layout';
33
+ ```
34
+
35
+ That is a supported entry point, not an accident of the build: `package.json`
36
+ declares the registering modules in `sideEffects`, which is what stops a bundler
37
+ from tree-shaking a side-effect-only import away (objectui#3899 — the manifest
38
+ used to say `"sideEffects": false`, and a bundler honouring it dropped the
39
+ registration silently). `registerLayout()` is also exported for hosts that
40
+ prefer to register explicitly.
23
41
 
24
42
  ## Components
25
43
 
26
44
  ### AppShell
27
45
 
28
- Complete application shell with header, sidebar, and main content area.
46
+ Complete application shell with a top navbar, a sidebar, and a main content area.
29
47
 
30
48
  ```typescript
31
49
  import { AppShell } from '@object-ui/layout';
32
50
 
33
51
  <AppShell
34
- header={<div>Header Content</div>}
52
+ navbar={<div>Navbar Content</div>}
35
53
  sidebar={<div>Sidebar Content</div>}
36
54
  >
37
55
  <div>Main Content</div>
38
56
  </AppShell>
39
57
  ```
40
58
 
59
+ The top bar's content goes in `navbar`. `AppShell` renders the `<header>`
60
+ element itself (`src/AppShell.tsx:248`) and `{navbar}` is the only thing that
61
+ fills it (`:249`) — there is no `header` prop. This example used to pass one
62
+ (objectui#4817), and because the component destructures a fixed key list with
63
+ **no rest element** (`:233-241`), the node was built and then dropped on the
64
+ floor: copied verbatim, the snippet rendered an empty top bar and said nothing.
65
+
66
+ Like `SidebarNav` below, `AppShell` is composed in JSX and is **not** on the
67
+ `ComponentRegistry`: four of its seven props are `React.ReactNode` slots that no
68
+ JSON document can fill, so the `app-shell` key was retired in objectui#4841 and
69
+ `{ "type": "app-shell" }` now reports `Unknown component type`. When the whole
70
+ shell has to come from metadata, the key for that is `app-schema-renderer`.
71
+
72
+ #### `AppShellProps`
73
+
74
+ | Prop | Type | Default | Description |
75
+ | --- | --- | --- | --- |
76
+ | `children` | `React.ReactNode` | — (required) | Main content, rendered inside the `<main>` element. |
77
+ | `navbar` | `React.ReactNode` | — | Top bar content. `AppShell` supplies the sticky `<header>` around it, so pass only what goes inside. |
78
+ | `sidebar` | `React.ReactNode` | — | Left sidebar node, rendered as a flex sibling of the content. Pass `SidebarNav`, or your own node. |
79
+ | `rightRail` | `React.ReactNode` | — | Optional right-side rail. It reflows the content beside it rather than overlaying it (ADR-0057 P3a); absent → unchanged single-pane layout. |
80
+ | `className` | `string` | — | Tailwind overrides for the `<main>` content element — **not** for the outer container. |
81
+ | `defaultOpen` | `boolean` | `true` | Initial open state of the underlying Shadcn `SidebarProvider`. |
82
+ | `branding` | `AppShellBranding` | — | App branding, applied by `useAppShellBranding`: `primaryColor` / `accentColor` become CSS custom properties on the document root (re-derived for dark mode), `favicon` sets the icon link's `href`, and `title` sets `document.title`. |
83
+
41
84
  ### PageHeader
42
85
 
43
- Page title block with an optional description, used at the top of a page's
86
+ Page title block with an optional subtitle, used at the top of a page's
44
87
  content area.
45
88
 
46
89
  ```typescript
47
90
  import { PageHeader } from '@object-ui/layout';
48
91
 
49
- <PageHeader title="Dashboard" description="View your metrics" />
92
+ <PageHeader title="Dashboard" subtitle="View your metrics" />
50
93
  ```
51
94
 
95
+ `subtitle` is the only spelling for the secondary line — it is the key
96
+ `@objectstack/spec/ui`'s `PageHeaderProps` declares. The legacy `description`
97
+ alias this component used to read as well was retired in objectui#3789; stored
98
+ metadata still carrying it is rewritten to `subtitle` at load time by the
99
+ ADR-0087 D2 conversion `page-header-subtitle-alias`.
100
+
52
101
  > **Rendering a whole `page` node?** That belongs to `PageRenderer` in
53
102
  > `@object-ui/components`, which is what the `page` component key resolves to —
54
103
  > it handles page types (record/home/app/utility), named regions and page
@@ -60,17 +109,91 @@ import { PageHeader } from '@object-ui/layout';
60
109
  Navigation sidebar component with React Router integration.
61
110
 
62
111
  ```typescript
63
- import { SidebarNav } from '@object-ui/layout';
112
+ import { SidebarNav, type NavItem } from '@object-ui/layout';
113
+ import { Home, Settings, Users } from 'lucide-react';
64
114
 
65
- const navItems = [
66
- { label: 'Dashboard', path: '/dashboard', icon: 'home' },
67
- { label: 'Users', path: '/users', icon: 'users' },
68
- { label: 'Settings', path: '/settings', icon: 'settings' }
115
+ const navItems: NavItem[] = [
116
+ { title: 'Dashboard', href: '/dashboard', icon: Home },
117
+ { title: 'Users', href: '/users', icon: Users },
118
+ { title: 'Settings', href: '/settings', icon: Settings },
69
119
  ];
70
120
 
71
121
  <SidebarNav items={navItems} />
72
122
  ```
73
123
 
124
+ An item's label is `title` and its target is `href` — and `icon` is a **component**,
125
+ not an icon name: it is rendered as `<item.icon />` (`src/SidebarNav.tsx:60`, `:109`),
126
+ so pass the imported Lucide component itself. This example used to be written with
127
+ `label` / `path` / `icon: 'home'`, none of which `NavItem` declares (objectui#3999);
128
+ copied as-is it produced rows with no label at all, a `NavLink` whose `to` was
129
+ `undefined`, and the string `'home'` handed to React as an unknown lowercase tag.
130
+ Annotating the array as `NavItem[]` is what turns that whole class of typo back into
131
+ a compile error where it is written, instead of a blank sidebar at runtime.
132
+
133
+ #### `SidebarNavProps`
134
+
135
+ | Prop | Type | Default | Description |
136
+ | --- | --- | --- | --- |
137
+ | `items` | `NavItem[] \| NavGroup[]` | — (required) | Flat item list, or grouped sections. The array must be homogeneous: only the **first** element is probed to decide which of the two it is. |
138
+ | `title` | `string` | `'Application'` | Section label shown above a flat `NavItem[]`. Ignored when `items` is a `NavGroup[]` — each group prints its own `label`. |
139
+ | `className` | `string` | — | Tailwind overrides, forwarded to the root `Sidebar`. |
140
+ | `collapsible` | `'offcanvas' \| 'icon' \| 'none'` | `'icon'` | Collapse behaviour of the underlying Shadcn `Sidebar`. |
141
+ | `searchEnabled` | `boolean` | `false` | Renders a search box that filters items by `title` (an item also survives when one of its `children` matches). |
142
+ | `searchPlaceholder` | `string` | `'Search…'` | Placeholder for that search box. |
143
+
144
+ #### `NavItem`
145
+
146
+ | Key | Type | Default | Description |
147
+ | --- | --- | --- | --- |
148
+ | `title` | `string` | — (required) | The visible label, and what search matches against. |
149
+ | `href` | `string` | — (required) | `NavLink` target; also the React key, so keep it unique within its list. Active state is `pathname === href`. |
150
+ | `icon` | `React.ComponentType<{ className?: string }>` | — | The icon **component** (e.g. `Home` from `lucide-react`), not its name. |
151
+ | `badge` | `string \| number` | — | Trailing badge content. Rendered whenever it is not `null`/`undefined`, so `0` shows. |
152
+ | `badgeVariant` | `'default' \| 'destructive' \| 'outline'` | `'default'` | Badge styling. |
153
+ | `children` | `NavItem[]` | — | Nested sub-items. A non-empty list turns the row into a collapsible group: the parent's own `href` is then no longer a link, only the children are. |
154
+
155
+ #### `NavGroup`
156
+
157
+ | Key | Type | Description |
158
+ | --- | --- | --- |
159
+ | `label` | `string` | Section heading printed above the group. |
160
+ | `items` | `NavItem[]` | The group's items — same `NavItem` shape as above, nesting included. |
161
+
162
+ ```typescript
163
+ import { SidebarNav, type NavGroup } from '@object-ui/layout';
164
+ import { FolderOpen, Home, Settings } from 'lucide-react';
165
+
166
+ const navGroups: NavGroup[] = [
167
+ {
168
+ label: 'Workspace',
169
+ items: [
170
+ { title: 'Dashboard', href: '/dashboard', icon: Home },
171
+ {
172
+ title: 'Projects',
173
+ href: '/projects',
174
+ icon: FolderOpen,
175
+ badge: 3,
176
+ children: [
177
+ { title: 'Active', href: '/projects/active' },
178
+ { title: 'Archived', href: '/projects/archived', badge: 'WIP', badgeVariant: 'outline' },
179
+ ],
180
+ },
181
+ ],
182
+ },
183
+ {
184
+ label: 'System',
185
+ items: [{ title: 'Settings', href: '/settings', icon: Settings }],
186
+ },
187
+ ];
188
+
189
+ <SidebarNav items={navGroups} searchEnabled />
190
+ ```
191
+
192
+ Note that `SidebarNav` is a plain React component: unlike the keys listed under
193
+ [Registration](#registration) it is **not** on the `ComponentRegistry`, so it is
194
+ composed in JSX rather than authored as a JSON node. Full guide:
195
+ [SidebarNav docs](https://www.objectui.org/docs/layout/sidebar-nav).
196
+
74
197
  ## Usage with React Router
75
198
 
76
199
  The layout components are designed to work seamlessly with React Router:
@@ -78,17 +201,18 @@ The layout components are designed to work seamlessly with React Router:
78
201
  ```typescript
79
202
  import { BrowserRouter, Routes, Route } from 'react-router-dom';
80
203
  import { AppShell, SidebarNav } from '@object-ui/layout';
204
+ import { Home, Users } from 'lucide-react';
81
205
 
82
206
  function App() {
83
207
  return (
84
208
  <BrowserRouter>
85
209
  <AppShell
86
- header={<div className="p-4">My App</div>}
210
+ navbar={<div className="p-4">My App</div>}
87
211
  sidebar={
88
212
  <SidebarNav
89
213
  items={[
90
- { label: 'Dashboard', path: '/', icon: 'home' },
91
- { label: 'Users', path: '/users', icon: 'users' }
214
+ { title: 'Dashboard', href: '/', icon: Home },
215
+ { title: 'Users', href: '/users', icon: Users },
92
216
  ]}
93
217
  />
94
218
  }
@@ -105,36 +229,28 @@ function App() {
105
229
 
106
230
  ## Customization
107
231
 
108
- All components accept `className` prop for Tailwind customization:
232
+ `AppShell` takes a single `className`, and it lands on the `<main>` content
233
+ element (`src/AppShell.tsx:256`) rather than on the outer container. There is no
234
+ per-slot `headerClassName` / `sidebarClassName` — the navbar and the sidebar are
235
+ nodes **you** build, so style them where you build them:
109
236
 
110
237
  ```typescript
111
238
  <AppShell
112
239
  className="bg-gray-50"
113
- headerClassName="border-b"
114
- sidebarClassName="bg-white shadow-lg"
240
+ navbar={<div className="border-b px-4">My App</div>}
241
+ sidebar={<div className="bg-white shadow-lg">Sidebar Content</div>}
115
242
  >
116
- {children}
243
+ <div>Main Content</div>
117
244
  </AppShell>
118
245
  ```
119
246
 
120
247
  ## API Reference
121
248
 
122
- For detailed API documentation, visit the [Object UI Documentation](https://www.objectui.org/docs/layout).
123
-
124
- <!-- release-metadata:v3.3.0 -->
125
-
126
- ## Compatibility
127
-
128
- - **React:** 18.x or 19.x
129
- - **Node.js:** ≥ 18
130
- - **TypeScript:** ≥ 5.0 (strict mode)
131
- - **`@objectstack/spec`:** ^3.3.0
132
- - **`@objectstack/client`:** ^3.3.0
133
- - **Tailwind CSS:** ≥ 3.4 (for packages with UI)
249
+ For detailed API documentation, visit the [Object UI Documentation](https://www.objectui.org/docs/layout/app-shell).
134
250
 
135
251
  ## Links
136
252
 
137
- - 📚 [Documentation](https://www.objectui.org/docs/layout)
253
+ - 📚 [Documentation](https://www.objectui.org/docs/guide/layout)
138
254
  - 📦 [npm package](https://www.npmjs.com/package/@object-ui/layout)
139
255
  - 📝 [Changelog](./CHANGELOG.md)
140
256
  - 🐛 [Report an issue](https://github.com/objectstack-ai/objectui/issues)
@@ -1,8 +1,26 @@
1
1
  import { default as React } from 'react';
2
2
  import { AppComponentSchema, NavigationItem } from '@object-ui/types';
3
- import { VisibilityEvaluator, PermissionChecker, CapabilityChecker } from './NavigationRenderer';
4
- /** Mobile navigation display mode */
5
- export type MobileNavMode = 'drawer' | 'bottom_nav' | 'hamburger';
3
+ import { VisibilityEvaluator, PermissionChecker, CapabilityChecker } from './NavigationRenderer.js';
4
+ /**
5
+ * Mobile navigation display mode — the two modes this renderer implements.
6
+ *
7
+ * `drawer` is the default (the sidebar rides in the AppShell's mobile sheet
8
+ * overlay); `bottom_nav` additionally renders the fixed bottom bar. Those are
9
+ * the renderer's only two read points for the prop — the default below and the
10
+ * `=== 'bottom_nav'` comparison that gates the bottom bar — so every value that
11
+ * is not `bottom_nav` produces drawer behaviour.
12
+ *
13
+ * A third member (spelled after the collapsed-sidebar menu button) rode in this
14
+ * union with a JSDoc line promising "collapsed sidebar" and NO read point
15
+ * anywhere, which made it behaviourally identical to `drawer` while reading as
16
+ * a capability. Retired in objectui#3985 under ADR-0049 enforce-or-remove
17
+ * (maintainer ruling 2026-08-10): it never was behaviour, so it is gone from
18
+ * this union and from the published vocabulary rather than being documented as
19
+ * a synonym for the default. Wanting a collapsed-sidebar mode is an
20
+ * implementation card first, and the value comes back with a read point — never
21
+ * as a declaration on its own.
22
+ */
23
+ export type MobileNavMode = 'drawer' | 'bottom_nav';
6
24
  export interface AppSchemaRendererProps {
7
25
  /** The AppSchema JSON to render */
8
26
  schema: AppComponentSchema;
@@ -52,8 +70,8 @@ export interface AppSchemaRendererProps {
52
70
  * - Area switcher when multiple areas are VISIBLE — area visibility is
53
71
  * derived from the items inside, not authored (objectui#3311): an area
54
72
  * whose items are all gated away is hidden and never auto-activated
55
- * - Mobile modes: `drawer` (sheet overlay, default), `bottom_nav` (fixed
56
- * bottom bar), `hamburger` (collapsed sidebar)
73
+ * - Mobile modes: `drawer` (sheet overlay, default) and `bottom_nav` (fixed
74
+ * bottom bar) — see `MobileNavMode` for why that list is two long
57
75
  * - Evaluates `visible` expressions and `requiredPermissions` on every item
58
76
  *
59
77
  * @example
@@ -10,8 +10,6 @@ export interface AppShellBranding {
10
10
  accentColor?: string;
11
11
  /** Favicon URL — replaces the <link rel="icon"> href */
12
12
  favicon?: string;
13
- /** Logo URL — passed to sidebar/navbar via context */
14
- logo?: string;
15
13
  /** Page title suffix (sets document.title) */
16
14
  title?: string;
17
15
  }
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { NavigationItem } from '@object-ui/types';
2
+ import { NavigationItem, KeyedI18nLabel } from '@object-ui/types';
3
3
  /**
4
4
  * Callback to evaluate a visibility expression.
5
5
  * Return `true` if the item should be visible.
@@ -100,22 +100,6 @@ export interface NavigationRendererProps {
100
100
  * as two `商机` rows where one is the list and the other is a Kanban view).
101
101
  */
102
102
  resolveViewLabel?: (objectName: string, viewName: string, fallbackLabel: string) => string;
103
- /**
104
- * Optional label resolver for navigation group items.
105
- * Called with `(groupId, fallbackLabel)` for items where
106
- * `item.type === 'group'` and `item.label` is a plain string.
107
- * Enables convention-based i18n via
108
- * `{ns}.apps.{appName}.navigation.{groupId}.label`.
109
- */
110
- resolveGroupLabel?: (groupId: string, fallbackLabel: string) => string;
111
- /**
112
- * Optional label resolver for non-object/non-dashboard/non-group nav
113
- * items (url, page, report, custom). Called with `(itemId, fallbackLabel)`
114
- * for items that have a plain-string label and a stable `id`.
115
- * Convention: `{ns}.apps.{appName}.navigation.{itemId}.label` — same
116
- * key shape as `resolveGroupLabel` so a single i18n helper covers both.
117
- */
118
- resolveItemLabel?: (itemId: string, fallbackLabel: string) => string;
119
103
  /**
120
104
  * Optional i18n translation function for resolving I18nLabel objects
121
105
  * (`{ key, defaultValue }`). When provided, labels are translated
@@ -141,14 +125,18 @@ export interface NavigationRendererProps {
141
125
  export declare function resolveIcon(name?: string): React.ComponentType<any>;
142
126
  /**
143
127
  * Resolve a NavigationItem label to a plain string.
144
- * Handles both plain strings and I18nLabel objects { key, defaultValue }.
145
- * When a `t` function is provided, I18nLabel objects are translated via i18next.
128
+ *
129
+ * Handles both plain strings and the KEYED i18n form
130
+ * `{ key, defaultValue?, params? }` — named `KeyedI18nLabel` in
131
+ * `@object-ui/types` since #4581, which is what this signature now states
132
+ * instead of a third inline copy of the same object literal. When a `t`
133
+ * function is provided the key is translated via i18next.
134
+ *
135
+ * "Keyed", not the spec's `I18nLabel`: that one is the INLINE LOCALE MAP
136
+ * (`{ en: 'Owner' }`) resolved against a BCP-47 locale, and the two answer
137
+ * wrongly for each other's input, silently (objectui#4167).
146
138
  */
147
- export declare function resolveLabel(label: string | {
148
- key: string;
149
- defaultValue?: string;
150
- params?: Record<string, any>;
151
- }, t?: (key: string, options?: any) => string): string;
139
+ export declare function resolveLabel(label: string | KeyedI18nLabel, t?: (key: string, options?: any) => string): string;
152
140
  /** Guard callbacks for {@link hasVisibleNavigationItems}. */
153
141
  export interface NavigationVisibilityOptions {
154
142
  /** Evaluator for item `visible` expressions. Defaults to always-visible. */
@@ -209,6 +197,25 @@ export interface NavTemplateContext {
209
197
  */
210
198
  contextValues?: Record<string, string | null | undefined>;
211
199
  }
200
+ /**
201
+ * The wire encoding of the declared `runAction` nav slot — ONE definition,
202
+ * sited with `resolveHref` because that is the only thing that writes it.
203
+ *
204
+ * `ObjectNavItemSchema.runAction` (objectstack#7253) declares WHICH action an
205
+ * object nav entry auto-runs on arrival; the URL is how that declaration
206
+ * survives the navigation, because the list surface mounts in a fresh render
207
+ * tree with nothing but the location to read. Naming the param after the slot
208
+ * is deliberate: the slot owns the name, so producer and consumer cannot drift.
209
+ *
210
+ * Before this constant existed, the name was a bare `'runAction'` literal
211
+ * hand-written at both ends — a private convention no schema declared, so
212
+ * `objectui validate` could not see it, `RESERVED_URL_PARAMS` did not list it,
213
+ * and a page could have repurposed the name with nothing to catch the
214
+ * collision. Import this; never re-spell it. `@object-ui/app-shell`'s
215
+ * `urlParams` re-exports it into the reserved registry rather than restating
216
+ * it, so there is still exactly one definition.
217
+ */
218
+ export declare const NAV_RUN_ACTION_PARAM = "runAction";
212
219
  /**
213
220
  * Resolve a NavigationItem to an absolute href (relative to `basePath`).
214
221
  *
@@ -264,4 +271,4 @@ export declare function filterNavigationItems(items: NavigationItem[], query: st
264
271
  * />
265
272
  * ```
266
273
  */
267
- export declare function NavigationRenderer({ items, basePath, evaluateVisibility: evalVis, checkPermission: checkPerm, checkCapability: checkCap, onAction, searchQuery, enablePinning, onPinToggle, enableReorder, onReorder, resolveObjectLabel, resolveDashboardLabel, resolveGroupLabel, resolveViewLabel, resolveItemLabel, t: tProp, templateContext, }: NavigationRendererProps): React.JSX.Element;
274
+ export declare function NavigationRenderer({ items, basePath, evaluateVisibility: evalVis, checkPermission: checkPerm, checkCapability: checkCap, onAction, searchQuery, enablePinning, onPinToggle, enableReorder, onReorder, resolveObjectLabel, resolveDashboardLabel, resolveViewLabel, t: tProp, templateContext, }: NavigationRendererProps): React.JSX.Element;
@@ -24,12 +24,19 @@ import { default as React } from 'react';
24
24
  export interface PageHeaderComponentProps extends React.HTMLAttributes<HTMLDivElement> {
25
25
  title: string;
26
26
  /**
27
- * Optional secondary line under the title. Spec schemas use `subtitle`,
28
- * the legacy console pages use `description` — both are supported and
29
- * resolve {field.path} tokens against the current record context.
27
+ * Optional secondary line under the title. `subtitle` is the only spelling:
28
+ * it is the key `@objectstack/spec/ui`'s `PageHeaderProps` declares, and it
29
+ * resolves {field.path} tokens against the current record context.
30
+ *
31
+ * The legacy `description` alias this component used to read alongside it
32
+ * was retired in objectui#3789, once the ADR-0087 D2 conversion
33
+ * `page-header-subtitle-alias` was measured to reach EVERY spec-valid header
34
+ * position on the load path (regions, slots, and containers nested to any
35
+ * depth — objectstack#6775 / #6776 / PR #7034). Stored metadata authored with
36
+ * `description` is rewritten to `subtitle` before it ever reaches a renderer,
37
+ * so the second line survives without this side holding a second dialect open.
30
38
  */
31
39
  subtitle?: string;
32
- description?: string;
33
40
  /**
34
41
  * Optional icon for the header chip. Accepts either a string Lucide icon
35
42
  * name (resolved via `LazyIcon`, the standard ObjectUI helper) or a
@@ -66,4 +73,4 @@ export interface PageHeaderComponentProps extends React.HTMLAttributes<HTMLDivEl
66
73
  };
67
74
  };
68
75
  }
69
- export declare function PageHeader({ title, subtitle, description, icon, action, actions, showBack, schema, className, children, ...props }: PageHeaderComponentProps): React.JSX.Element;
76
+ export declare function PageHeader({ title, subtitle, icon, action, actions, showBack, schema, className, children, ...props }: PageHeaderComponentProps): React.JSX.Element;
@@ -1,28 +1,25 @@
1
1
  import { default as React } from 'react';
2
+ import { BreakpointColumnMap, BreakpointOrderMap } from '@objectstack/spec/ui';
2
3
  /**
3
- * Spec-aligned breakpoint column map (mirrors @objectstack/spec BreakpointColumnMapSchema).
4
- * Maps breakpoint names to grid column counts (1-12).
5
- */
6
- export interface BreakpointColumnMap {
7
- xs?: number;
8
- sm?: number;
9
- md?: number;
10
- lg?: number;
11
- xl?: number;
12
- '2xl'?: number;
13
- }
14
- /**
15
- * Spec-aligned breakpoint order map (mirrors @objectstack/spec BreakpointOrderMapSchema).
16
- * Maps breakpoint names to display order numbers.
4
+ * Breakpoint column map (`BreakpointColumnMapSchema`) and breakpoint order map
5
+ * (`BreakpointOrderMapSchema`), both re-exported from `@objectstack/spec/ui`.
6
+ *
7
+ * These were hand copies until objectui#4167, each carrying the word "mirrors"
8
+ * — the doc-comment shape objectstack#4115 catalogues, where the claim is what
9
+ * the next agent reads as canonical and the code is what actually decides. rc.6
10
+ * publishes both names, and both copies happened to still be exact: six
11
+ * optional numbers keyed `xs`…`2xl`, on a `$strict` schema. "Still exact" is
12
+ * the argument FOR binding them, not against — the copies had nothing to
13
+ * protect, so the only thing they could do from here was drift, and a
14
+ * breakpoint the spec adds now arrives instead of silently not existing.
15
+ *
16
+ * `BreakpointOrderMap` has no read point in this package (`ResponsiveGrid`
17
+ * resolves columns only); it is published because `ResponsiveConfigSchema`
18
+ * pairs the two and an author configuring order needs the type. Bound rather
19
+ * than deleted for that reason — deleting a published type is a separate
20
+ * decision from stopping it being a fork.
17
21
  */
18
- export interface BreakpointOrderMap {
19
- xs?: number;
20
- sm?: number;
21
- md?: number;
22
- lg?: number;
23
- xl?: number;
24
- '2xl'?: number;
25
- }
22
+ export type { BreakpointColumnMap, BreakpointOrderMap };
26
23
  export interface ResponsiveGridProps {
27
24
  /** Grid column map per breakpoint */
28
25
  columns?: BreakpointColumnMap;
package/dist/index.d.ts CHANGED
@@ -2,11 +2,11 @@
2
2
  * ObjectUI Layout
3
3
  * Copyright (c) 2024-present ObjectStack Inc.
4
4
  */
5
- export * from './PageHeader';
6
- export * from './AppShell';
7
- export * from './PageCard';
8
- export * from './SidebarNav';
9
- export * from './ResponsiveGrid';
10
- export * from './NavigationRenderer';
11
- export * from './AppSchemaRenderer';
5
+ export * from './PageHeader.js';
6
+ export * from './AppShell.js';
7
+ export * from './PageCard.js';
8
+ export * from './SidebarNav.js';
9
+ export * from './ResponsiveGrid.js';
10
+ export * from './NavigationRenderer.js';
11
+ export * from './AppSchemaRenderer.js';
12
12
  export declare function registerLayout(): void;