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
77 changes: 8 additions & 69 deletions docs/core/shared/_installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import TabItem from '@theme/TabItem';
import PkgTabs from '@site/src/components/PkgTabs';
import PkgInstall from '@site/src/components/PkgInstall';
import Link from '@docusaurus/Link';
import ProviderSetupCode from '@site/src/components/ProviderSetupCode';

::::react

Expand All @@ -19,34 +20,15 @@ values={[

<TabItem value="web">

```tsx title="index.tsx"
import { DataProvider } from '@data-client/react';
import { createRoot } from 'react-dom/client';

createRoot(document.body).render(
<DataProvider>
<App />
</DataProvider>,
);
```
<ProviderSetupCode platform="web" />

Alternatively [integrate state with redux](../guides/redux.md)

</TabItem>

<TabItem value="native">

```tsx title="index.tsx"
import { DataProvider } from '@data-client/react';
import { AppRegistry } from 'react-native';

const Root = () => (
<DataProvider>
<App />
</DataProvider>
);
AppRegistry.registerComponent('MyApp', () => Root);
```
<ProviderSetupCode platform="native" />

Alternatively [integrate state with redux](../guides/redux.md)

Expand All @@ -58,43 +40,13 @@ Alternatively [integrate state with redux](../guides/redux.md)
<Link className="button button--primary" to="../guides/ssr#nextjs">Full NextJS Guide</Link>
</div>

```tsx title="app/layout.tsx"
// highlight-next-line
import { DataProvider } from '@data-client/react/nextjs';

export default function RootLayout({ children }) {
return (
<html>
<body>
// highlight-next-line
<DataProvider>{children}</DataProvider>
</body>
</html>
);
}
```
<ProviderSetupCode platform="nextjs" />

</TabItem>

<TabItem value="expo">

```tsx title="app/_layout.tsx"
import { Stack } from 'expo-router';
import { DataProvider } from '@data-client/react';

export default function RootLayout() {
return (
// highlight-start
<DataProvider>
// highlight-end
<Stack>
<Stack.Screen name="index" />
</Stack>
// highlight-start
</DataProvider>
// highlight-end
);
}
```
<ProviderSetupCode platform="expo" />

</TabItem>

Expand All @@ -118,21 +70,8 @@ Anansi includes Reactive Data Client automatically.

<PkgInstall pkgs="@data-client/vue @data-client/rest" />

```tsx title="main.ts"
import { createApp } from 'vue';
import { DataClientPlugin } from '@data-client/vue';

const app = createApp(App);
<ProviderSetupCode platform="vue" />

app.use(DataClientPlugin, {
// optional overrides
// managers: getDefaultManagers(),
// initialState,
// Controller,
// gcPolicy,
});

app.mount('#app');
```
`app.use(DataClientPlugin, options)` optionally overrides [`managers`](../api/getDefaultManagers.md), `initialState`, `Controller`, and `gcPolicy`.

::::
111 changes: 73 additions & 38 deletions website/src/components/ProviderSetupCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,105 +3,140 @@ import type { ReactNode } from 'react';

import { parseCodeDocuments } from './Playground/editor/codeModel';

interface Setup {
title: string;
imports: string[];
body: string;
}

const PLATFORMS = {
web: {
web: (names, managers) => ({
title: 'index.tsx',
imports: (names: string[]) => [
imports: [
...importLine(['DataProvider', ...names], '@data-client/react'),
"import { createRoot } from 'react-dom/client';",
],
body: `createRoot(document.body).render(
<DataProvider managers={managers}>
<DataProvider${managersProp(managers)}>
<App />
</DataProvider>,
);`,
},
native: {
}),
native: (names, managers) => ({
title: 'index.tsx',
imports: (names: string[]) => [
imports: [
...importLine(['DataProvider', ...names], '@data-client/react'),
"import { AppRegistry } from 'react-native';",
],
body: `const Root = () => (
<DataProvider managers={managers}>
<DataProvider${managersProp(managers)}>
<App />
</DataProvider>
);
AppRegistry.registerComponent('MyApp', () => Root);`,
},
nextjs: {
title: 'app/Provider.tsx',
imports: (names: string[]) => [
"'use client';",
...importLine(names, '@data-client/react'),
"import { DataProvider } from '@data-client/react/nextjs';",
],
body: `export default function Provider({
}),
// managers are client-only, so they need their own 'use client' Provider
nextjs: (names, managers) =>
managers ?
{
title: 'app/Provider.tsx',
imports: [
"'use client';",
...importLine(names, '@data-client/react'),
"import { DataProvider } from '@data-client/react/nextjs';",
],
body: `export default function Provider({
children,
}: {
children: React.ReactNode;
}) {
return <DataProvider managers={managers}>{children}</DataProvider>;
}`,
},
expo: {
}
: {
title: 'app/layout.tsx',
imports: ["import { DataProvider } from '@data-client/react/nextjs';"],
body: `export default function RootLayout({ children }) {
return (
<html>
<body>
<DataProvider>{children}</DataProvider>
</body>
</html>
);
}`,
},
expo: (names, managers) => ({
title: 'app/_layout.tsx',
imports: (names: string[]) => [
imports: [
"import { Stack } from 'expo-router';",
...importLine(['DataProvider', ...names], '@data-client/react'),
],
body: `export default function RootLayout() {
return (
<DataProvider managers={managers}>
<DataProvider${managersProp(managers)}>
<Stack>
<Stack.Screen name="index" />
</Stack>
</DataProvider>
);
}`,
},
vue: {
}),
vue: (names, managers) => ({
title: 'main.ts',
imports: (names: string[]) => [
imports: [
"import { createApp } from 'vue';",
...importLine(['DataClientPlugin', ...names], '@data-client/vue'),
"import App from './App.vue';",
],
body: `const app = createApp(App);
app.use(DataClientPlugin, { managers });
app.use(DataClientPlugin${managers ? ', { managers }' : ''});
app.mount('#app');`,
},
};
}),
} satisfies Record<string, (names: string[], managers: boolean) => Setup>;

/**
* App setup file that passes `managers` to DataProvider (React) or
* DataClientPlugin (Vue). Used by docs/core/shared/_provider_managers.mdx
* App setup file that renders DataProvider (React) or DataClientPlugin (Vue),
* passing `managers` when given.
* Used by docs/core/shared/_provider_managers.mdx and _installation.mdx
*
* @param imports names imported from `@data-client/react` or `@data-client/vue`
* @param children code block defining `managers`
* @param children optional code block defining `managers`
*/
export default function ProviderSetupCode({
platform,
imports,
imports = [],
children,
}: {
platform: keyof typeof PLATFORMS;
imports: string[];
children: ReactNode;
imports?: string[];
children?: ReactNode;
}) {
const managers = parseCodeDocuments(children)[0]?.value;
// nothing to show without a managers block (e.g. the partial rendered alone)
if (!managers) return null;
const { title, imports: importLines, body } = PLATFORMS[platform];
const managers =
children ? parseCodeDocuments(children)[0]?.value : undefined;
if (children && !managers)
throw new Error('<ProviderSetupCode> children must be a code block');
const {
title,
imports: importLines,
body,
} = PLATFORMS[platform](imports, !!managers);
const header = importLines.join('\n');
// the managers block's own imports continue the import list
const gap = managers.startsWith('import ') ? '\n' : '\n\n';
const sep = managers?.startsWith('import ') ? '\n' : '\n\n';
return (
<CodeBlock language={platform === 'vue' ? 'ts' : 'tsx'} title={title}>
{`${importLines(imports).join('\n')}${gap}${managers}\n\n${body}`}
{managers ?
`${header}${sep}${managers}\n\n${body}`
: `${header}\n\n${body}`}
</CodeBlock>
);
}

function managersProp(managers: boolean) {
return managers ? ' managers={managers}' : '';
}

/** Matches prettier's import wrapping at printWidth 80 */
function importLine(names: string[], pkg: string): string[] {
if (!names.length) return [];
Expand Down