Skip to content

Commit aa0d82f

Browse files
feat(cta-ui-base): slot-based composition for RootComponent and AppSidebar (#153)
## Summary Introduces slot APIs to make the UI shell composable and overridable. Enables custom sidebar actions and host-app overrides (header, sidebar, file navigator, startup dialog) without forking core components. This is primarily in support of integrating the builder UI into tanstack.com, but could be used for customizing the UI builder in other contexts as wel.
1 parent af5b02f commit aa0d82f

File tree

4 files changed

+45
-11
lines changed

4 files changed

+45
-11
lines changed

packages/cta-ui-base/src/app.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,41 @@ import FileNavigator from './components/file-navigator'
55
import StartupDialog from './components/startup-dialog'
66
import { CTAProvider } from './components/cta-provider'
77

8-
export default function RootComponent() {
8+
export interface RootComponentProps {
9+
slots?: Partial<{
10+
header: React.ReactNode
11+
sidebar: React.ReactNode
12+
fileNavigator: React.ReactNode
13+
startupDialog: React.ReactNode
14+
}>
15+
}
16+
17+
export const defaultSlots: RootComponentProps['slots'] = {
18+
header: <AppHeader />,
19+
sidebar: <AppSidebar />,
20+
fileNavigator: <FileNavigator />,
21+
startupDialog: <StartupDialog />,
22+
}
23+
24+
export default function RootComponent(props: RootComponentProps) {
25+
const slots = Object.assign({}, defaultSlots, props.slots)
26+
927
return (
1028
<CTAProvider>
1129
<main className="min-w-[1280px]">
1230
<BackgroundAnimation />
1331
<div className="min-h-dvh p-2 sm:p-4 space-y-2 sm:space-y-4 @container">
14-
<AppHeader />
32+
{slots.header}
1533
<div className="flex flex-row">
1634
<div className="w-1/3 @8xl:w-1/4 pr-2">
17-
<AppSidebar />
35+
{slots.sidebar}
1836
</div>
1937
<div className="w-2/3 @8xl:w-3/4 pl-2">
20-
<FileNavigator />
38+
{slots.fileNavigator}
2139
</div>
2240
</div>
2341
</div>
24-
<StartupDialog />
42+
{slots.startupDialog}
2543
</main>
2644
</CTAProvider>
2745
)

packages/cta-ui-base/src/components/cta-sidebar.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,27 @@ import TypescriptSwitch from './sidebar-items/typescript-switch'
99
import StarterDialog from './sidebar-items/starter'
1010
import SidebarGroup from './sidebar-items/sidebar-group'
1111

12-
export function AppSidebar() {
12+
export interface SidebarProps {
13+
slots?: {
14+
actions: React.ReactNode
15+
}
16+
}
17+
18+
export const DefaultAppSidebarActions = () => (
19+
<div className="mt-5">
20+
<RunAddOns />
21+
<RunCreateApp />
22+
</div>
23+
)
24+
25+
const defaultSlots: SidebarProps['slots'] = {
26+
actions: <DefaultAppSidebarActions />,
27+
}
28+
29+
export function AppSidebar(props: SidebarProps) {
1330
const ready = useReady()
1431
const mode = useApplicationMode()
32+
const slots = Object.assign({}, defaultSlots, props.slots)
1533

1634
return (
1735
<div className="flex flex-col gap-2">
@@ -34,10 +52,7 @@ export function AppSidebar() {
3452
)}
3553
</>
3654
)}
37-
<div className="mt-5">
38-
<RunAddOns />
39-
<RunCreateApp />
40-
</div>
55+
{slots.actions}
4156
</div>
4257
)
4358
}

packages/cta-ui-base/src/components/sidebar-items/run-create-app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import StatusList from '../status-list'
2121
import { createAppStreaming, shutdown } from '../../lib/api'
2222

23+
2324
export default function RunCreateApp() {
2425
const [isRunning, setIsRunning] = useState(false)
2526
const { streamItems, monitorStream, finished } = useStreamingStatus()

packages/cta-ui-base/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ export {
4141
useReady,
4242
}
4343

44-
export default RootComponent
44+
export default RootComponent

0 commit comments

Comments
 (0)