Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions content/docs/guide/objectos-integration.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -325,22 +325,26 @@ const schema = {

### Custom Data Hooks

`@object-ui/data-objectstack` ships the *adapter*, not hooks. Reads and writes
go through `useViewData` from `@object-ui/react`, which resolves the adapter
from context and hands back both the rows and the `DataSource` to write with.

```typescript
// Implement custom hooks for data operations
import { useObjectQuery, useObjectMutation } from '@object-ui/data-objectstack';
import { useViewData } from '@object-ui/react';

function ContactList() {
const { data, loading, error } = useObjectQuery('contact', {
filter: { field: 'status', operator: 'eq', value: 'active' },
sort: [{ field: 'name', order: 'asc' }],
page: 1,
pageSize: 20
const { data, loading, error, dataSource, refresh } = useViewData({
resource: 'contact',
params: {
$filter: { status: 'active' },
$orderby: [{ field: 'name', order: 'asc' }],
$top: 20,
},
});

const { mutate: createContact } = useObjectMutation('contact', 'create');

const handleCreate = async (formData: any) => {
await createContact(formData);
const handleCreate = async (formData: Record<string, unknown>) => {
await dataSource?.create('contact', formData);
await refresh();
};

if (loading) return <div>Loading...</div>;
Expand All@@ -350,7 +354,7 @@ function ContactList() {
<SchemaRenderer
schema={{
type: 'object-grid',
rowData: data.records,
rowData: data,
// ... other props
}}
/>
Expand Down
61 changes: 33 additions & 28 deletions packages/app-shell/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,8 @@ A lightweight, framework-agnostic rendering engine that enables third-party syst

This package provides the essential building blocks for rendering ObjectUI schemas:
- Basic layout components (AppShell, Sidebar, Main)
- Renderer components for objects, dashboards, pages, and forms
- Route-level views for objects, dashboards, pages and records
(`ObjectView`, `DashboardView`, `PageView`, `RecordDetailView`)
- Zero console-specific dependencies
- Bring-your-own-router design

Expand All@@ -23,24 +24,30 @@ pnpm add @object-ui/app-shell
### Basic Setup

```tsx
import { AppShell, ObjectRenderer } from '@object-ui/app-shell';
import { AppShell } from '@object-ui/app-shell';
import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react';
import type { ObjectViewSchema } from '@object-ui/types';

const contactView: ObjectViewSchema = { type: 'object-view', objectName: 'contact' };

function MyCustomConsole() {
return (
<AppShell sidebar={<MySidebar />}>
<ObjectRenderer
objectName="contact"
dataSource={myDataSource}
/>
<SchemaRendererProvider dataSource={myDataSource}>
<SchemaRenderer schema={contactView} />
</SchemaRendererProvider>
</AppShell>
);
}
```

### With Dashboard

`DashboardRenderer` ships from `@object-ui/plugin-dashboard` — this package's
own `DashboardView` imports it from there.

```tsx
import { DashboardRenderer } from '@object-ui/app-shell';
import { DashboardRenderer } from '@object-ui/plugin-dashboard';

function MyDashboard() {
return (
Expand DownExpand Up@@ -103,40 +110,38 @@ Basic layout container with sidebar support.
</AppShell>
```

### ObjectRenderer
### ObjectView

Renders object views (Grid, Kanban, List, etc.).
The route-level object surface (Grid, Kanban, List, etc.). It resolves the
object and view from the host's route, so it takes no `objectName` prop —
mount it on a route that supplies them, as the console does with
`/apps/:appName/:objectName` and `/apps/:appName/:objectName/view/:viewId`.

```tsx
<ObjectRenderer
objectName="contact"
viewId="grid-view"
dataSource={dataSource}
onRecordClick={(record) => navigate(`/detail/${record.id}`)}
/>
<ObjectView dataSource={dataSource} />
```

### DashboardRenderer
To render an object view from a schema instead of from a route, use
`SchemaRenderer` from `@object-ui/react` — see [Basic Setup](#basic-setup).

### DashboardView / PageView

Renders dashboard layouts from schema.
`DashboardView` and `PageView` are the route-level equivalents for dashboards
and custom pages; like `ObjectView` they resolve their target from the route
(`dashboardName` / `pageName`) rather than from a `schema` prop.

```tsx
<DashboardRenderer
schema={dashboardSchema}
dataSource={dataSource}
/>
<DashboardView dataSource={dataSource} />
```

### PageRenderer

Renders custom page schemas.

```tsx
<PageRenderer
schema={pageSchema}
/>
<PageView />
```

The schema-driven renderers live elsewhere: `DashboardRenderer` in
`@object-ui/plugin-dashboard`, and everything else through `SchemaRenderer` in
`@object-ui/react`, which resolves `type` against the component registry.

### ActionParamDialog

Collects user input for a declared action's `params` before execution. Every
Expand Down
22 changes: 15 additions & 7 deletions packages/components/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -70,20 +70,24 @@ entry goes on generating the classes your own source uses, as it always did.
### 2. Register Components

```tsx
import { registerDefaultRenderers } from '@object-ui/components'
import { initializeComponents } from '@object-ui/components'

registerDefaultRenderers()
initializeComponents()
```

Importing the package already registers its components as a side effect;
`initializeComponents()` is the explicit call for bundlers that would otherwise
tree-shake that import away.

## Usage

### With SchemaRenderer

```tsx
import { SchemaRenderer } from '@object-ui/react'
import { registerDefaultRenderers } from '@object-ui/components'
import { initializeComponents } from '@object-ui/components'

registerDefaultRenderers()
initializeComponents()

const schema = {
type: 'card',
Expand DownExpand Up@@ -200,16 +204,20 @@ All components accept `className` for Tailwind classes:
Register your own components:

```tsx
import { registerRenderer } from '@object-ui/react'
import { ComponentRegistry } from '@object-ui/core'
import { Button } from '@object-ui/components'

function CustomButton(props) {
function CustomButton(props: Record<string, unknown>) {
return <Button {...props} className="my-custom-style" />
}

registerRenderer('custom-button', CustomButton)
ComponentRegistry.register('custom-button', CustomButton)
```

`ComponentRegistry` is a process-level singleton exported by `@object-ui/core`;
`SchemaRenderer` resolves every `type` against it, so a component registered
here is renderable from schema anywhere in the app.

## API Reference

See [full documentation](https://objectui.org/docs/components) for detailed API reference.
Expand Down
49 changes: 30 additions & 19 deletions packages/core/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,8 @@ Core logic, types, and validation for Object UI. Zero React dependencies.

## Features

- 🎯 **Type Definitions** - Complete TypeScript schemas for all components
- 🎯 **Type Definitions** - Re-exported runtime types; the component schema
vocabulary itself is `@object-ui/types`
- 🔍 **Component Registry** - Framework-agnostic component registration system
- 📊 **Data Scope** - Data scope management and expression evaluation
- ✅ **Validation** - Zod-based schema validation
Expand All@@ -20,15 +21,20 @@ npm install @object-ui/core

### Type Definitions

The component schema vocabulary lives in `@object-ui/types`. Core depends on
that package and does not re-export it, so import the types from there. The
page node type is `PageNodeSchema` — the SDUI node, as distinct from the
authored page document.

```typescript
import type {
PageSchema,
FormSchema,
import type {
PageNodeSchema,
FormSchema,
InputSchema,
BaseSchema
} from '@object-ui/core'
BaseSchema
} from '@object-ui/types'

const mySchema: PageSchema = {
const mySchema: PageNodeSchema = {
type: 'page',
title: 'My Page',
body: []
Expand All@@ -47,15 +53,20 @@ const metadata = registry.get('button')

### Data Scope

`DataScopeManager` owns the named scopes a component tree reads from, and
`evaluateExpression` evaluates a `${...}` expression against a context. They
are separate exports: a scope holds data, it does not evaluate.

```typescript
import { DataScope } from '@object-ui/core'
import { DataScopeManager, evaluateExpression } from '@object-ui/core'

const scope = new DataScope({
user: { name: 'John', role: 'admin' }
})
const manager = new DataScopeManager()
manager.registerScope('user', { data: { name: 'John', role: 'admin' } })

const userName = scope.get('user.name') // 'John'
const isAdmin = scope.evaluate('${user.role === "admin"}') // true
const userName = manager.getScope('user')?.data.name // 'John'
const isAdmin = evaluateExpression('${user.role === "admin"}', {
user: { name: 'John', role: 'admin' },
}) // true
```

### Server Action Dispatch (`createServerActionHandler`)
Expand DownExpand Up@@ -87,16 +98,16 @@ action identity (ADR-0110), the record-id resolution dance (`_rowRecord`,
guard, and the `/actions` response-envelope rule (`interpretActionResponse` /
`readActionPayload`, also exported).

### System Views (`defineView`)
### System Views (`defineSystemView`)

Schemas authored in source code are part of the product contract and must
not be mutated at runtime. Wrap them with `defineView()` to deep-freeze the
graph and tag it as a *System View*.
not be mutated at runtime. Wrap them with `defineSystemView()` to deep-freeze
the graph and tag it as a *System View*.

```typescript
import { defineView, cloneAsOverride, isSystemView } from '@object-ui/core'
import { defineSystemView, cloneAsOverride, isSystemView } from '@object-ui/core'

export const userListView = defineView({
export const userListView = defineSystemView({
type: 'list',
data: { object: 'User' },
columns: [{ name: 'email' }],
Expand All@@ -115,7 +126,7 @@ isSystemView(draft) // false — clone is no longer Sys

| Tier | Source | Mutable? | API |
| ----------- | --------------------- | -------- | --------------------------- |
| System View | code (`import` / `as const`) | ❌ frozen | `defineView()` |
| System View | code (`import` / `as const`) | ❌ frozen | `defineSystemView()` |
| Tenant View | backend / DB | ⚠️ admin only | `cloneAsOverride()` + persist |
| User View | localStorage / API | ✅ user-editable | `cloneAsOverride()` + persist |

Expand Down
17 changes: 9 additions & 8 deletions packages/react/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -158,18 +158,19 @@ falling back to the object's full scope. Use `useElementDataSourceSchema` (plus
the exported `ElementDataSourceErrorPanel` / `ElementDataSourceLoadingPanel`) when
a block cannot be wrapped — a renderer whose hooks must run before the panels.

### useRegistry
### ComponentRegistry

Access the component registry:
There is no registry hook: the registry is a process-level singleton exported
by `@object-ui/core`, so read it directly. Subscribe to it only when a lazily
registered plugin must trigger a re-render.

```tsx
import { useRegistry } from '@object-ui/react'
import { ComponentRegistry } from '@object-ui/core'

function MyComponent() {
const registry = useRegistry()
const Component = registry.get('button')

return <Component {...props} />
function MyComponent(props: Record<string, unknown>) {
const Component = ComponentRegistry.get('button')

return Component ? <Component {...props} /> : null
}
```

Expand Down
10 changes: 5 additions & 5 deletions scripts/check-doc-snippet-types.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -211,7 +211,7 @@ const TS_FENCE_LANGUAGES = new Set(['ts', 'tsx', 'typescript']);
*/
const UNGATED_DOCS = {
'content/docs/guide/objectos-integration.mdx':
'36 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 7 unresolved-module diagnostic(s); plus TS2305x3 TS2339x1 — candidate real defects, un-triaged',
'36 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 7 unresolved-module diagnostic(s); plus TS2305x1 TS2339x1 — candidate real defects, un-triaged',
'content/docs/plugins/plugin-calendar-view.mdx':
'2 unresolved-module diagnostic(s) — and NOT a defect: the page is a migration guide whose ' +
'"Before" blocks quote the retired `@object-ui/plugin-calendar-view` import on purpose. Covering ' +
Expand All@@ -238,15 +238,15 @@ const UNGATED_DOCS = {
'content/docs/utilities/runner.mdx':
'5 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 3 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; 3 unresolved-module diagnostic(s)',
'packages/app-shell/README.md':
'1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 18 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2305x2 — candidate real defects, un-triaged',
'1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 14 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines',
'packages/auth/README.md':
'1 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 15 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2741x1 — candidate real defects, un-triaged',
'packages/collaboration/README.md':
'13 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2339x2 TS2353x1 TS2554x1 TS2739x1 — candidate real defects, un-triaged',
'packages/components/README.md':
'2 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2305x3 — candidate real defects, un-triaged',
'1 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines',
'packages/core/README.md':
'5 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2305x6 TS2351x1 — candidate real defects, un-triaged',
'5 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2339x2 TS2351x1 — candidate real defects, un-triaged',
'packages/data-objectstack/README.md':
'10 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 41 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines',
'packages/fields/README.md':
Expand DownExpand Up@@ -298,7 +298,7 @@ const UNGATED_DOCS = {
'packages/react-runtime/README.md':
'25 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2813x1 TS2814x1 — candidate real defects, un-triaged',
'packages/react/README.md':
'10 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2305x1 TS2339x2 — candidate real defects, un-triaged',
'9 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines; plus TS2339x2 — candidate real defects, un-triaged',
'packages/types/README.md':
'3 parse diagnostic(s) — blocks fenced `ts` that are bare object literals or elided bodies; 3 undefined-name diagnostic(s) — blocks continue an earlier block, or use ambient names the page never defines',
};
Expand Down
Loading