Skip to content

Commit 52a8979

Browse files
committed
docs: Add vue/react selector for component docs
1 parent b03fa99 commit 52a8979

File tree

10 files changed

+625
-0
lines changed

10 files changed

+625
-0
lines changed

docs/core/api/useSuspense.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ description: High performance async data rendering without overfetching. useSusp
1111
import Tabs from '@theme/Tabs';
1212
import TabItem from '@theme/TabItem';
1313
import GenericsTabs from '@site/src/components/GenericsTabs';
14+
import FrameworkTabs, { TabItem as FrameworkTabItem } from '@site/src/components/FrameworkTabs';
1415
import ConditionalDependencies from '../shared/\_conditional_dependencies.mdx';
1516
import PaginationDemo from '../shared/\_pagination.mdx';
1617
import HooksPlayground from '@site/src/components/HooksPlayground';
@@ -41,6 +42,9 @@ values={[
4142
]}>
4243
<TabItem value="rest">
4344

45+
<FrameworkTabs>
46+
<FrameworkTabItem value="react">
47+
4448
<HooksPlayground fixtures={detailFixtures} row>
4549

4650
```typescript title="ProfileResource" collapsed
@@ -82,6 +86,53 @@ render(<ProfileDetail />);
8286

8387
</HooksPlayground>
8488

89+
</FrameworkTabItem>
90+
<FrameworkTabItem value="vue">
91+
92+
<TypeScriptEditor>
93+
94+
```typescript title="ProfileResource" collapsed
95+
import { Entity, resource } from '@data-client/rest';
96+
97+
export class Profile extends Entity {
98+
id: number | undefined = undefined;
99+
avatar = '';
100+
fullName = '';
101+
bio = '';
102+
103+
static key = 'Profile';
104+
}
105+
106+
export const ProfileResource = resource({
107+
path: '/profiles/:id',
108+
schema: Profile,
109+
});
110+
```
111+
112+
```html title="ProfileDetail.vue"
113+
<script setup lang="ts">
114+
import { useSuspense } from '@data-client/vue';
115+
import { ProfileResource } from './ProfileResource';
116+
117+
const profile = await useSuspense(ProfileResource.get, { id: 1 });
118+
</script>
119+
120+
<template>
121+
<div class="listItem">
122+
<Avatar :src="profile.avatar" />
123+
<div>
124+
<h4>{{ profile.fullName }}</h4>
125+
<p>{{ profile.bio }}</p>
126+
</div>
127+
</div>
128+
</template>
129+
```
130+
131+
</TypeScriptEditor>
132+
133+
</FrameworkTabItem>
134+
</FrameworkTabs>
135+
85136
</TabItem>
86137
<TabItem value="other">
87138

website/plan-crumbs.mdc

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
Use a wrapped `DocBreadcrumbs` theme component. That’s the smallest, most upgrade-friendly hook that’s used on every docs MDX page.
2+
3+
### 1. Create your custom “right of breadcrumbs” component
4+
5+
For example:
6+
7+
`src/components/BreadcrumbRightControls.tsx`:
8+
9+
```tsx
10+
import React from 'react';
11+
12+
export default function BreadcrumbRightControls() {
13+
// Replace with your real UI: framework selector, version dropdown, etc.
14+
return (
15+
<div>
16+
{/* Example content */}
17+
<button type="button">Do something</button>
18+
</div>
19+
);
20+
}
21+
```
22+
23+
### 2. Swizzle `DocBreadcrumbs` with `--wrap`
24+
25+
Docusaurus exposes docs breadcrumbs through the `@theme/DocBreadcrumbs` component, which is used inside the docs layout ([alanwang.site][1]). Swizzling with `--wrap` lets you enhance it without copying the whole implementation ([Docusaurus][2]).
26+
27+
From your project root:
28+
29+
```bash
30+
npx docusaurus swizzle @docusaurus/theme-classic DocBreadcrumbs -- --wrap
31+
# or: yarn swizzle @docusaurus/theme-classic DocBreadcrumbs --wrap
32+
# or: pnpm docusaurus swizzle @docusaurus/theme-classic DocBreadcrumbs -- --wrap
33+
```
34+
35+
This creates:
36+
37+
```text
38+
src/theme/DocBreadcrumbs/index.(js|tsx)
39+
```
40+
41+
### 3. Implement the wrapper that adds your control on the right
42+
43+
Replace the generated file with something like this (TypeScript version; JS is identical minus types):
44+
45+
`src/theme/DocBreadcrumbs/index.tsx`:
46+
47+
```tsx
48+
import React from 'react';
49+
import clsx from 'clsx';
50+
51+
// Original theme implementation
52+
import DocBreadcrumbs from '@theme-original/DocBreadcrumbs';
53+
import type { Props } from '@theme/DocBreadcrumbs';
54+
55+
// Your custom UI
56+
import BreadcrumbRightControls from '@site/src/components/BreadcrumbRightControls';
57+
58+
// Optional CSS module for layout
59+
import styles from './styles.module.css';
60+
61+
export default function DocBreadcrumbsWrapper(
62+
props: Props,
63+
): JSX.Element | null {
64+
const breadcrumbs = <DocBreadcrumbs {...props} />;
65+
66+
// If breadcrumbs are disabled (config/front matter), don't render anything
67+
if (!breadcrumbs) {
68+
return null;
69+
}
70+
71+
return (
72+
<div className={clsx(styles.wrapper)}>
73+
<div className={styles.left}>{breadcrumbs}</div>
74+
<div className={styles.right}>
75+
<BreadcrumbRightControls />
76+
</div>
77+
</div>
78+
);
79+
}
80+
```
81+
82+
`src/theme/DocBreadcrumbs/styles.module.css`:
83+
84+
```css
85+
.wrapper {
86+
display: flex;
87+
align-items: center; /* baseline-ish alignment with default breadcrumbs */
88+
justify-content: space-between;
89+
gap: 0.75rem;
90+
}
91+
92+
/* Left side: native Docusaurus breadcrumbs */
93+
.left {
94+
min-width: 0; /* keep ellipsis / wrapping sane */
95+
}
96+
97+
/* Right side: your custom controls */
98+
.right {
99+
display: inline-flex;
100+
align-items: center;
101+
gap: 0.5rem;
102+
}
103+
```
104+
105+
Notes:
106+
107+
* Using `@theme-original/DocBreadcrumbs` means you keep all the default markup and schema.org breadcrumb behavior while just wrapping it in a flex container ([Docusaurus][2]).
108+
* `DocBreadcrumbs` is only used on docs pages via `DocItem/Layout`, so this affects every MDX docs page and nothing else (no blog, no plain MDX pages) ([alanwang.site][1]).
109+
* The wrapper layout is independent of Docusaurus internals: if they change the internal breadcrumb markup, you still just receive whatever `DocBreadcrumbs` returns and render it on the left.
110+
111+
That’s it: you’ve extended Docusaurus 3 with a single wrapped theme component, and your custom controls will appear aligned to the right of the breadcrumbs on every docs MDX page.
112+
113+
[1]: https://www.alanwang.site/en/posts/blog-guides/docusaurus-comment "Adding Comment Functionality to Docusaurus | Alan Wang"
114+
[2]: https://docusaurus.io/docs/swizzling?utm_source=chatgpt.com "Swizzling"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
.container {
2+
position: relative;
3+
display: inline-flex;
4+
align-items: center;
5+
}
6+
7+
.logo {
8+
width: 16px;
9+
height: 16px;
10+
flex-shrink: 0;
11+
}
12+
13+
.chevron {
14+
width: 12px;
15+
height: 12px;
16+
flex-shrink: 0;
17+
transition: transform 0.2s;
18+
}
19+
20+
.trigger {
21+
display: inline-flex;
22+
align-items: center;
23+
gap: 0.4rem;
24+
appearance: none;
25+
background-color: var(--ifm-background-surface-color);
26+
border: 1px solid var(--ifm-color-emphasis-300);
27+
border-radius: var(--ifm-global-radius);
28+
color: var(--ifm-font-color-base);
29+
cursor: pointer;
30+
font-family: var(--ifm-font-family-base);
31+
font-size: 0.875rem;
32+
font-weight: 500;
33+
padding: 0.35rem 0.5rem 0.35rem 0.6rem;
34+
transition:
35+
border-color 0.2s,
36+
box-shadow 0.2s;
37+
}
38+
39+
.trigger:hover {
40+
border-color: var(--ifm-color-primary);
41+
}
42+
43+
.trigger:focus {
44+
outline: none;
45+
border-color: var(--ifm-color-primary);
46+
box-shadow: 0 0 0 2px var(--ifm-color-primary-light);
47+
}
48+
49+
.trigger[aria-expanded='true'] .chevron {
50+
transform: rotate(180deg);
51+
}
52+
53+
.dropdown {
54+
position: absolute;
55+
top: calc(100% + 4px);
56+
left: 0;
57+
z-index: 100;
58+
min-width: 100%;
59+
margin: 0;
60+
padding: 0.25rem;
61+
list-style: none;
62+
background-color: var(--ifm-background-surface-color);
63+
border: 1px solid var(--ifm-color-emphasis-300);
64+
border-radius: var(--ifm-global-radius);
65+
box-shadow:
66+
0 4px 6px -1px rgb(0 0 0 / 0.1),
67+
0 2px 4px -2px rgb(0 0 0 / 0.1);
68+
}
69+
70+
.option {
71+
display: flex;
72+
align-items: center;
73+
gap: 0.5rem;
74+
width: 100%;
75+
padding: 0.4rem 0.6rem;
76+
border: none;
77+
border-radius: calc(var(--ifm-global-radius) - 2px);
78+
background: transparent;
79+
color: var(--ifm-font-color-base);
80+
font-family: var(--ifm-font-family-base);
81+
font-size: 0.875rem;
82+
font-weight: 500;
83+
cursor: pointer;
84+
transition: background-color 0.15s;
85+
}
86+
87+
.option:hover {
88+
background-color: var(--ifm-color-emphasis-100);
89+
}
90+
91+
.option.selected {
92+
background-color: var(--ifm-color-primary-lightest);
93+
color: var(--ifm-color-primary-darkest);
94+
}
95+
96+
html[data-theme='dark'] .option.selected {
97+
background-color: var(--ifm-color-primary-darkest);
98+
color: var(--ifm-color-primary-lightest);
99+
}
100+
101+
html[data-theme='dark'] .dropdown {
102+
box-shadow:
103+
0 4px 6px -1px rgb(0 0 0 / 0.3),
104+
0 2px 4px -2px rgb(0 0 0 / 0.3);
105+
}

0 commit comments

Comments
 (0)