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
24 changes: 22 additions & 2 deletions content/docs/guide/component-registry.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -88,6 +88,7 @@ Now you can use it in schemas:

All registered components receive the schema as props:

<!-- doc-snippet: fragment — continues the custom-component block above — `BaseSchema` and `MyComponentSchema` are declared there, and re-declaring them here would teach the reader to write the same interface twice (measured: TS2304 x3) -->
```tsx
interface ComponentProps<T extends BaseSchema = BaseSchema> {
// The complete schema object
Expand DownExpand Up@@ -119,6 +120,7 @@ function MyRenderer(props: ComponentProps<MyComponentSchema>) {

Register components with additional metadata:

<!-- doc-snippet: fragment — continues the custom-component block above — `MyComponent` and the `ComponentRegistry` import come from it (measured: TS2304 x2). The `ComponentMeta` literal this block shows is type-checked on the complete example at the end of the page, which does compile -->
```tsx
ComponentRegistry.register('my-component', MyComponent, {
label: 'My Custom Component',
Expand All@@ -137,6 +139,7 @@ This metadata is used by the Visual Designer to provide better editing experienc

Register components that load on demand:

<!-- doc-snippet: fragment — `./HeavyComponent` is the reader's own module, so the dynamic import cannot resolve here (measured: TS2307 x1, plus TS2304 on the `ComponentRegistry` the block above imports) -->
```tsx
// The loader runs the first time a schema asks for `heavy-component`.
ComponentRegistry.registerLazy('heavy-component', () => import('./HeavyComponent'))
Expand All@@ -146,6 +149,7 @@ ComponentRegistry.registerLazy('heavy-component', () => import('./HeavyComponent

Override default components with your own:

<!-- doc-snippet: fragment — `MyCustomButton` is the reader's own replacement component; there is nothing in this repo to import it from (measured: TS2304 x1) -->
```tsx
import { ComponentRegistry } from '@object-ui/core'
import { initializeComponents } from '@object-ui/components'
Expand All@@ -163,6 +167,7 @@ ComponentRegistry.register('button', MyCustomButton)
Default components are organized by category:

### Form Components
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the eleven form-component keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- input
- textarea
Expand All@@ -178,6 +183,7 @@ Default components are organized by category:
```

### Data Display
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the seven data-display keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- table
- list
Expand All@@ -189,6 +195,7 @@ Default components are organized by category:
```

### Layout
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the eight layout keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- page
- container
Expand All@@ -201,6 +208,7 @@ Default components are organized by category:
```

### Feedback
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the nine feedback keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- alert
- toast
Expand All@@ -214,6 +222,7 @@ Default components are organized by category:
```

### Navigation
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the four navigation keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- menu
- breadcrumb
Expand All@@ -222,6 +231,7 @@ Default components are organized by category:
```

### Other
<!-- doc-snippet: fragment — not TypeScript at all — this fence holds a markdown bullet list naming the eight remaining keys the registry ships, and a leading `-` reads as unary minus on an undeclared name (measured: TS2304 across the list, and the form list does not even parse because `switch` is a keyword — TS1109/TS1005 x3). The fence language is the underlying defect; correcting it is a separate change -->
```tsx
- button
- link
Expand All@@ -238,13 +248,17 @@ Default components are organized by category:
### Get All Registered Types

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

const types = ComponentRegistry.getAllTypes()
console.log(types) // ['input', 'button', 'form', ...]
```

### Check if Type is Registered

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

if (ComponentRegistry.has('my-component')) {
console.log('Component is registered')
}
Expand All@@ -253,6 +267,8 @@ if (ComponentRegistry.has('my-component')) {
### Get Component Metadata

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

const metadata = ComponentRegistry.getMeta('input')
console.log(metadata)
// {
Expand DownExpand Up@@ -302,6 +318,7 @@ Use kebab-case for component types:

### 4. Provide Meaningful Metadata

<!-- doc-snippet: fragment — `RatingComponent` is the component built in the complete example at the end of this page, and the `ComponentRegistry` import comes with it (measured: TS2304 x2) -->
```tsx
ComponentRegistry.register('rating', RatingComponent, {
label: 'Star Rating',
Expand All@@ -313,6 +330,7 @@ ComponentRegistry.register('rating', RatingComponent, {

### 5. Handle Missing Props Gracefully

<!-- doc-snippet: fragment — continues the component-interface block above — `ComponentProps` is declared there and `MySchema` stands for whatever schema the reader's component takes (measured: TS2304 x2) -->
```tsx
function MyComponent(props: ComponentProps<MySchema>) {
const { schema } = props
Expand All@@ -332,6 +350,7 @@ function MyComponent(props: ComponentProps<MySchema>) {

Group related components into plugin packages:

<!-- doc-snippet: fragment — the three chart components are files the reader is being told to write, so `./BarChart` / `./LineChart` / `./PieChart` cannot resolve here (measured: TS2307 x3) -->
```tsx
// @my-org/objectui-plugin-charts
import { ComponentRegistry } from '@object-ui/core'
Expand All@@ -348,6 +367,7 @@ export function registerChartComponents() {

Usage:

<!-- doc-snippet: fragment — `@my-org/objectui-plugin-charts` is the package the reader has just been shown how to create, not one this repo publishes (measured: TS2307 x1) -->
```tsx
import { initializeComponents } from '@object-ui/components'
import '@object-ui/fields'
Expand All@@ -362,10 +382,10 @@ registerChartComponents()
Here's a complete example of a custom form component:

```tsx
import { forwardRef } from 'react'
import { forwardRef, useState } from 'react'
import { ComponentRegistry } from '@object-ui/core'
import type { BaseSchema } from '@object-ui/types'
import { cn } from '@/lib/utils'
import { cn } from '@object-ui/components'

interface RatingSchema extends BaseSchema {
type: 'rating'
Expand Down
9 changes: 9 additions & 0 deletions content/docs/guide/layout.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,6 +40,7 @@ your JSON pages *inside* it. `app-shell` is not a component key either — what

### Basic Usage

<!-- doc-snippet: fragment — `lucide-react` supplies the `NavItem.icon` COMPONENTS this example is about. It is a dependency of ten workspace packages (`@object-ui/layout` among them) but not a root one, so the specifier does not resolve in this gate's program (measured: TS2307 x1), and `pageSchema` is the reader's own page JSON (TS2304 x1). Probed with the icon import shimmed and `pageSchema` typed `BaseSchema`: the `NavItem[]` literal and the `AppShell` / `SidebarNav` props underneath are clean — 0 diagnostics -->
```tsx
import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout';
import { SchemaRenderer } from '@object-ui/react';
Expand DownExpand Up@@ -171,6 +172,7 @@ The `Page` component provides a consistent wrapper for individual pages with opt

### Schema API

<!-- doc-snippet: fragment — a SHAPE excerpt, not an expression — the keys carry `?` optional markers and trailing prose comments, so the object literal cannot parse as TypeScript (measured: TS1109 / TS1005 / TS1011) -->
```typescript
{
type: 'page',
Expand DownExpand Up@@ -258,6 +260,7 @@ Unresolvable tokens collapse to an empty string rather than leaking the raw temp

### Schema API

<!-- doc-snippet: fragment — a SHAPE excerpt, not an expression — the keys carry `?` optional markers and trailing prose comments, so the object literal cannot parse as TypeScript (measured: TS1109 / TS1005 / TS1011) -->
```typescript
{
type: 'page-header',
Expand DownExpand Up@@ -316,6 +319,7 @@ React, or use `navigation-renderer` when the tree has to come from metadata.
`SidebarNav` renders a Shadcn `Sidebar`, so it must be inside a `SidebarProvider` —
`AppShell` supplies one. Its rows are `NavLink`s, so it also needs a router above it.

<!-- doc-snippet: fragment — `lucide-react` supplies the `NavItem.icon` COMPONENTS this example is about. It is a dependency of ten workspace packages (`@object-ui/layout` among them) but not a root one, so the specifier does not resolve in this gate's program (measured: TS2307 x1), and `pageSchema` is the reader's own page JSON (TS2304 x1). Probed with the icon import shimmed and `pageSchema` typed `BaseSchema`: the `NavItem[]` literal and the `AppShell` / `SidebarNav` props underneath are clean — 0 diagnostics -->
```tsx
import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout';
import { SchemaRenderer } from '@object-ui/react';
Expand DownExpand Up@@ -418,6 +422,7 @@ branding for a whole-shell-from-metadata setup.

The shell is React; the page inside it is your JSON.

<!-- doc-snippet: fragment — `lucide-react` supplies the `NavItem.icon` COMPONENTS this example is about. It is a dependency of ten workspace packages (`@object-ui/layout` among them) but not a root one, so the specifier does not resolve in this gate's program (measured: TS2307 x1), and `pageSchema` is the reader's own page JSON (TS2304 x1). Probed with the icon import shimmed and `pageSchema` typed `BaseSchema`: the `NavItem[]` literal and the `AppShell` / `SidebarNav` props underneath are clean — 0 diagnostics -->
```tsx
import { AppShell, SidebarNav, type NavItem } from '@object-ui/layout';
import { SchemaRenderer } from '@object-ui/react';
Expand DownExpand Up@@ -453,6 +458,7 @@ and `defaultOpen` is the shell's own initial-state prop.

Omit `sidebar` and the content fills the width under the top bar.

<!-- doc-snippet: fragment — a JSX excerpt showing one prop — `AppShell`, `SchemaRenderer` and `landingSchema` all come from the Basic Usage block above, and restating its five setup lines here would bury the one line the section is about (measured: TS2304 x3) -->
```tsx
<AppShell navbar={<span className="font-semibold">Welcome</span>}>
<SchemaRenderer schema={landingSchema} />
Expand DownExpand Up@@ -562,6 +568,7 @@ them reads `lg` (1024px), so 800px and 1400px get the same layout.

Add Tailwind classes to layout components:

<!-- doc-snippet: fragment — a JSX excerpt showing the `className` / `navbar` / `sidebar` slots — `AppShell`, `SidebarNav`, `SchemaRenderer`, `navItems` and `pageSchema` all come from the blocks above (measured: TS2304 x6) -->
```tsx
<AppShell
className="bg-background"
Expand DownExpand Up@@ -603,6 +610,7 @@ Control page content padding:

Compose the shell once and let the page JSON change per route:

<!-- doc-snippet: fragment — a JSX excerpt showing one shell reused across routes — `AppShell`, `SidebarNav`, `SchemaRenderer`, `navbar`, `navItems` and `pageSchema` all come from the blocks above (measured: TS2304 x6) -->
```tsx
// One shell for the whole app; `pageSchema` is whatever the route resolves to.
<AppShell navbar={navbar} sidebar={<SidebarNav items={navItems} />}>
Expand DownExpand Up@@ -660,6 +668,7 @@ Group related items with `NavGroup`. Pass groups instead of a flat list and `Sid
labels each section and draws the separator between them itself — there is no `divider`
item, and a row is either a link or a group, never both:

<!-- doc-snippet: fragment — `lucide-react` supplies the `NavItem.icon` COMPONENTS this example is about. It is a dependency of ten workspace packages (`@object-ui/layout` among them) but not a root one, so the specifier does not resolve in this gate's program (measured: TS2307 x1). Probed with the icon import shimmed: the `NavGroup[]` literal and the `SidebarNav` props underneath are clean — 0 diagnostics -->
```tsx
import { SidebarNav, type NavGroup } from '@object-ui/layout';
import { DollarSign, Home, Settings } from 'lucide-react';
Expand Down
Loading
Loading