mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core: support codeblock tab meta without value
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': minor
|
||||
---
|
||||
|
||||
Support codeblock `tab` meta without value
|
||||
@@ -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');
|
||||
```
|
||||
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user