diff --git a/packages/example-nextjs14/src/app/(demo)/CodeBlocksNext.tsx b/packages/example-nextjs14/src/app/(demo)/CodeBlocksNext.tsx index 62a8814..ecddc70 100644 --- a/packages/example-nextjs14/src/app/(demo)/CodeBlocksNext.tsx +++ b/packages/example-nextjs14/src/app/(demo)/CodeBlocksNext.tsx @@ -1,5 +1,3 @@ -'use client'; - import { File } from '../components/File'; import { CodeBlockState } from './CodeBlockState'; import { tooltips } from './tooltips'; diff --git a/packages/example-nextjs14/src/app/(demo)/page.tsx b/packages/example-nextjs14/src/app/(demo)/page.tsx index bcee7a7..dce15df 100644 --- a/packages/example-nextjs14/src/app/(demo)/page.tsx +++ b/packages/example-nextjs14/src/app/(demo)/page.tsx @@ -1,17 +1,8 @@ -import dynamicImport from 'next/dynamic'; -import React from 'react'; import { DemoPart } from '../DemoPart'; import { Description } from '../components/Description'; import { TabsBlock } from './TabsBlock'; - -const CodeBlocks = dynamicImport( - () => import('./CodeBlocksNext') - .then((mod) => mod.CodeBlocks), - { - loading: () =>
, - }, -); +import { CodeBlocks } from './CodeBlocksNext'; export default async function Home({ searchParams }: { searchParams: object }) { return ( diff --git a/packages/example-nextjs14/src/app/(demo)/react-router/CodeBlocksRR.tsx b/packages/example-nextjs14/src/app/(demo)/react-router/CodeBlocksRR.tsx index 136dcdb..5d8efe7 100644 --- a/packages/example-nextjs14/src/app/(demo)/react-router/CodeBlocksRR.tsx +++ b/packages/example-nextjs14/src/app/(demo)/react-router/CodeBlocksRR.tsx @@ -1,5 +1,3 @@ -'use client'; - import { File } from '../../components/File'; import { CodeBlockState } from '../CodeBlockState'; import { tooltips } from '../tooltips'; diff --git a/packages/example-nextjs14/src/app/(demo)/react-router/page.tsx b/packages/example-nextjs14/src/app/(demo)/react-router/page.tsx index a77b8f8..d228315 100644 --- a/packages/example-nextjs14/src/app/(demo)/react-router/page.tsx +++ b/packages/example-nextjs14/src/app/(demo)/react-router/page.tsx @@ -1,16 +1,8 @@ -import dynamicImport from 'next/dynamic'; -import React from 'react'; import { DemoPart } from '../../DemoPart'; import { Description } from '../../components/Description'; import { TabsBlock } from '../TabsBlock'; - -const CodeBlocks = dynamicImport( - () => import('./CodeBlocksRR').then((mod) => mod.CodeBlocksRR), - { - loading: () =>
, - }, -); +import { CodeBlocksRR } from './CodeBlocksRR'; export default async function Home({ searchParams }: { searchParams: object }) { return ( @@ -20,7 +12,7 @@ export default async function Home({ searchParams }: { searchParams: object }) {
- +
diff --git a/packages/example-nextjs14/src/app/components/Code.tsx b/packages/example-nextjs14/src/app/components/Code.tsx index 9713ccb..a62e74f 100644 --- a/packages/example-nextjs14/src/app/components/Code.tsx +++ b/packages/example-nextjs14/src/app/components/Code.tsx @@ -1,18 +1,15 @@ -'use client' -import React from 'react'; +'use server' import { highlight } from '../highlighter'; import { Langs } from '../types'; -export const Code = ({ content, lang, ...rest }: { content: string, lang?: Langs } & React.ComponentPropsWithoutRef<'div'>) => { +export const Code = async ({ content, lang, id, ...rest }: { content: string, lang?: Langs, id?: string } & React.ComponentPropsWithoutRef<'div'>) => { + const text = await highlight(content, { lang }) - const [text, setText] = React.useState(''); - - React.useEffect(() => { - highlight(content, { lang }).then(setText) - }, [content, lang]) - - return text ? -
- :
{content.split('\n').map((el, ind) => (
{el || '_'}
))}
+ return
{text ? +
+ :
{content.split('\n') + .map((el, ind) => (
{el || '_'}
))} +
} +
} diff --git a/packages/example-nextjs14/src/app/components/FakeTypes.tsx b/packages/example-nextjs14/src/app/components/FakeTypes.tsx new file mode 100644 index 0000000..5e4f776 --- /dev/null +++ b/packages/example-nextjs14/src/app/components/FakeTypes.tsx @@ -0,0 +1,74 @@ +'use client'; + +import React from 'react'; + +import { useFloating, autoUpdate, useClientPoint, offset } from '@floating-ui/react'; + +import { type Matcher, type Tooltip, type Langs } from '../types'; +import { highlight } from '../highlighter'; + +export const FakeTypes = ({ matchers, id }: { matchers?: Matcher[], id: string }) => { + const [tooltip, setTooltip] = React.useState<{ x: number, y: number, nodes: Tooltip[] }>({ nodes: [], x: 0, y: 0 }); + + const { refs, floatingStyles, context } = useFloating({ + whileElementsMounted: autoUpdate, + placement: 'top', + middleware: [ + offset({ mainAxis: 20, crossAxis: 50 }) + ] + }); + useClientPoint(context, + { x: tooltip.x, y: tooltip.y } + ) + + React.useEffect(() => { + const codeBlock = document.querySelector(`[id="${id}"]`) + + if (codeBlock && !(codeBlock as HTMLDivElement).onmouseover) { + const matchTooltips = (ev: MouseEvent) => { + // @ts-expect-error fots + const text = (ev?.target?.textContent || '').trim(); + // @ts-expect-error fots + const next = (ev?.target?.nextSibling?.textContent || '').trim() + + // if (text?.length < 12) { + // console.log(`${text}${next}`, { ev, context }) + // } + + const match = matchers?.find(el => el[0] === `${text}${next}`) + + if (match) { + if (match[1] !== tooltip.nodes) { + setTooltip({ nodes: match[1], x: ev.clientX, y: ev.clientY }) + } + } else if (tooltip.nodes.length) { + setTooltip(curr => ({ ...curr, nodes: [] })) + } + } + + (codeBlock as HTMLDivElement).onmousemove = matchTooltips; + (codeBlock as HTMLDivElement).onmouseleave = () => setTooltip(curr => ({...curr, nodes: []})); + + } + }, [id, tooltip.nodes, matchers]) + + return tooltip.nodes.length ?
+ {tooltip.nodes.map((node, ind) => ( + + ))} +
: null +} + +const CodeClient = ({ content, lang }: { content: string, lang?: Langs, id?: string }) => { + const [text, setText] = React.useState(''); + + React.useEffect(() => { + highlight(content, { lang }).then(setText) + }, [content, lang]) + + return
+} diff --git a/packages/example-nextjs14/src/app/components/File.tsx b/packages/example-nextjs14/src/app/components/File.tsx index 546ce66..3644c59 100644 --- a/packages/example-nextjs14/src/app/components/File.tsx +++ b/packages/example-nextjs14/src/app/components/File.tsx @@ -1,10 +1,17 @@ -'use client' -import React from 'react'; - -import { useFloating, autoUpdate, useClientPoint, offset } from '@floating-ui/react'; +import dynamicImport from 'next/dynamic'; import { Code } from './Code'; -import { type Matcher, type Tooltip } from '../types'; +import { type Matcher } from '../types'; + +import { stringToHash } from '../utils'; + +const FakeTypes = dynamicImport( + () => import('./FakeTypes') + .then((mod) => mod.FakeTypes), + { + loading: () => null, + }, +); export const File = ({ name, @@ -15,39 +22,7 @@ export const File = ({ content: string; matchers?: Matcher[] }) => { - const [tooltip, setTooltip] = React.useState<{ x: number, y: number, nodes: Tooltip[] }>({ nodes: [], x: 0, y: 0 }); - - const { refs, floatingStyles, context } = useFloating({ - whileElementsMounted: autoUpdate, - placement: 'top', - middleware: [ - offset({ mainAxis: 20, crossAxis: 50 }) - ] - }); - useClientPoint(context, - { x: tooltip.x, y: tooltip.y } - ) - - const matchTooltips = (ev: React.MouseEvent) => { - // @ts-expect-error fots - const text = (ev?.target?.textContent || '').trim(); - // @ts-expect-error fots - const next = (ev?.target?.nextSibling?.textContent || '').trim() - - // if (text?.length < 12) { - // console.log(`${text}${next}`, { ev, context }) - // } - - const match = matchers?.find(el => el[0] === `${text}${next}`) - - if (match) { - if (match[1] !== tooltip.nodes) { - setTooltip({ nodes: match[1], x: ev.clientX, y: ev.clientY }) - } - } else if (tooltip.nodes.length) { - setTooltip(curr => ({ ...curr, nodes: [] })) - } - } + const id = stringToHash(content); return (
- {tooltip.nodes.length ?
- {tooltip.nodes.map((node, ind) => ( - - ))} -
: null} - -
+ - - -
+
); diff --git a/packages/example-nextjs14/src/app/highlighter.ts b/packages/example-nextjs14/src/app/highlighter.ts index 6c12adc..84790e7 100644 --- a/packages/example-nextjs14/src/app/highlighter.ts +++ b/packages/example-nextjs14/src/app/highlighter.ts @@ -65,3 +65,4 @@ function getHighlighter() { }) } +const _warmUp = getHighlighter() diff --git a/packages/example-nextjs14/src/app/utils.ts b/packages/example-nextjs14/src/app/utils.ts new file mode 100644 index 0000000..390c6cc --- /dev/null +++ b/packages/example-nextjs14/src/app/utils.ts @@ -0,0 +1,13 @@ +export function stringToHash(text: string) { + let hash = 0; + + if (text.length == 0) return '0'; + + for (let i = 0; i < text.length; i++) { + const char = text.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; + } + + return String(hash); +}