Core: support codeblock tab meta without value

This commit is contained in:
Fuma Nama
2024-12-10 21:51:02 +08:00
parent 7ed38ed9bf
commit 050b326c41
4 changed files with 53 additions and 52 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': minor
---
Support codeblock `tab` meta without value
+4 -4
View File
@@ -270,11 +270,11 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs items={["Tab 1", "Tab 2"]}>
```ts tab="Tab 1"
```ts tab
console.log('A');
```
```ts tab="Tab 2"
```ts tab
console.log('B');
```
@@ -283,11 +283,11 @@ console.log('B');
<Tabs items={["Tab 1", "Tab 2"]}>
```ts tab="Tab 1"
```ts tab
console.log('A');
```
```ts tab="Tab 2"
```ts tab
console.log('B');
```
+22 -20
View File
@@ -41,6 +41,10 @@ const metaValues: MetaValue[] = [
name: 'tab',
regex: /tab="(?<value>[^"]+)"/,
},
{
name: 'tab',
regex: /tab/,
},
];
export const rehypeCodeDefaultOptions: RehypeCodeOptions = {
@@ -58,23 +62,17 @@ export const rehypeCodeDefaultOptions: RehypeCodeOptions = {
const map: Record<string, string> = {};
for (const value of metaValues) {
const result = value.regex.exec(meta);
meta = meta.replace(value.regex, (_, ...args) => {
const first = args.at(0);
map[value.name] = typeof first === 'string' ? first : '';
if (result) {
map[value.name] = result[1];
}
return '';
});
}
map.__parsed_raw = meta;
return map;
},
filterMetaString(meta) {
let replaced = meta;
for (const value of metaValues) {
replaced = replaced.replace(value.regex, '');
}
return replaced;
},
};
export type RehypeCodeOptions = RehypeShikiOptions & {
@@ -120,6 +118,11 @@ export function rehypeCode(
{
name: 'rehype-code:pre-process',
preprocess(code, { meta }) {
if (meta && '__parsed_raw' in meta) {
meta.__raw = meta.__parsed_raw;
delete meta.__parsed_raw;
}
if (meta && codeOptions.filterMetaString) {
meta.__raw = codeOptions.filterMetaString(meta.__raw ?? '');
}
@@ -163,9 +166,7 @@ export function rehypeCode(
);
return async (tree, file) => {
await (
await transformer
)(tree, file, () => {
(await transformer)(tree, file, () => {
// nothing
});
};
@@ -176,8 +177,8 @@ function transformerTab(): ShikiTransformer {
name: 'rehype-code:tab',
// @ts-expect-error -- types not compatible with MDX
root(root) {
const meta = this.options.meta;
if (typeof meta?.tab !== 'string') return root;
const value = this.options.meta?.tab;
if (typeof value !== 'string') return root;
return {
type: 'root',
@@ -188,9 +189,10 @@ function transformerTab(): ShikiTransformer {
data: {
_codeblock: true,
},
attributes: [
{ type: 'mdxJsxAttribute', name: 'value', value: meta.tab },
],
attributes:
value !== ''
? [{ type: 'mdxJsxAttribute', name: 'value', value }]
: [],
children: root.children,
} as MdxJsxFlowElement,
],
+22 -28
View File
@@ -7,7 +7,6 @@ import type {
import {
useMemo,
useState,
useCallback,
createContext,
useContext,
useRef,
@@ -115,34 +114,29 @@ export function Tabs({
}
}, [valueToIdMap]);
const onValueChange = useCallback(
(v: string) => {
if (updateAnchor) {
const id = valueToIdMap.get(v);
if (id) {
window.history.replaceState(null, '', `#${id}`);
}
}
if (groupId) {
listeners.get(groupId)?.forEach((item) => {
item(v);
});
if (persist) localStorage.setItem(groupId, v);
else sessionStorage.setItem(groupId, v);
} else {
setValue(v);
}
},
[valueToIdMap, groupId, persist, updateAnchor],
);
return (
<Primitive.Tabs
value={value}
onValueChange={onValueChange}
onValueChange={(v: string) => {
if (updateAnchor) {
const id = valueToIdMap.get(v);
if (id) {
window.history.replaceState(null, '', `#${id}`);
}
}
if (groupId) {
listeners.get(groupId)?.forEach((item) => {
item(v);
});
if (persist) localStorage.setItem(groupId, v);
else sessionStorage.setItem(groupId, v);
} else {
setValue(v);
}
}}
{...props}
className={cn('my-4', props.className)}
>
@@ -181,7 +175,7 @@ export function Tab({ value, className, ...props }: TabProps) {
const resolvedValue =
value ??
// eslint-disable-next-line react-hooks/rules-of-hooks -- `value` is not supposed to change
ctx?.items[useCollectionIndex()];
ctx?.items.at(useCollectionIndex());
if (!resolvedValue)
throw new Error(
'Failed to resolve tab `value`, please pass a `value` prop to the Tab component.',
@@ -237,7 +231,7 @@ function useCollectionIndex() {
}
useMemo(() => {
// re-order the item to the bottom if exists
// re-order the item to the bottom if registered
unregister();
register();
// eslint-disable-next-line -- register