Skip to content

Commit

Permalink
style: full ssr for code blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
asmyshlyaev177 committed Nov 27, 2024
1 parent ace908e commit e43db31
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 88 deletions.
2 changes: 0 additions & 2 deletions packages/example-nextjs14/src/app/(demo)/CodeBlocksNext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import { File } from '../components/File';
import { CodeBlockState } from './CodeBlockState';
import { tooltips } from './tooltips';
Expand Down
11 changes: 1 addition & 10 deletions packages/example-nextjs14/src/app/(demo)/page.tsx
Original file line number Diff line number Diff line change
@@ -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: () => <div className="codeBlock-wrapper codeBlock-loader"></div>,
},
);
import { CodeBlocks } from './CodeBlocksNext';

export default async function Home({ searchParams }: { searchParams: object }) {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import { File } from '../../components/File';
import { CodeBlockState } from '../CodeBlockState';
import { tooltips } from '../tooltips';
Expand Down
12 changes: 2 additions & 10 deletions packages/example-nextjs14/src/app/(demo)/react-router/page.tsx
Original file line number Diff line number Diff line change
@@ -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: () => <div className="codeBlock-wrapper codeBlock-loader"></div>,
},
);
import { CodeBlocksRR } from './CodeBlocksRR';

export default async function Home({ searchParams }: { searchParams: object }) {
return (
Expand All @@ -20,7 +12,7 @@ export default async function Home({ searchParams }: { searchParams: object }) {
<TabsBlock />

<section className="codeBlock-wrapper">
<CodeBlocks />
<CodeBlocksRR />
</section>

<Description/ >
Expand Down
21 changes: 9 additions & 12 deletions packages/example-nextjs14/src/app/components/Code.tsx
Original file line number Diff line number Diff line change
@@ -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 ?
<div dangerouslySetInnerHTML={{ __html: text }} {...rest}></div>
: <div {...rest}>{content.split('\n').map((el, ind) => (<div key={ind}>{el || '_'}</div>))}</div>
return <div id={id}>{text ?
<div dangerouslySetInnerHTML={{ __html: text }} {...rest}></div>
: <div {...rest}>{content.split('\n')
.map((el, ind) => (<div key={ind}>{el || '_'}</div>))}
</div>}
</div>
}
74 changes: 74 additions & 0 deletions packages/example-nextjs14/src/app/components/FakeTypes.tsx
Original file line number Diff line number Diff line change
@@ -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 ? <div
style={floatingStyles}
ref={refs.setFloating}
className="text-[12px] p-4 absolute bg-slate-800 rounded-md max-w-[600px] transition-none border border-slate-500"
>
{tooltip.nodes.map((node, ind) => (
<CodeClient content={node.text} lang={node.lang} key={ind} />
))}
</div> : 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 <div dangerouslySetInnerHTML={{ __html: text || "." }} />
}
67 changes: 15 additions & 52 deletions packages/example-nextjs14/src/app/components/File.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 (
<div
Expand All @@ -67,21 +42,9 @@ export const File = ({

</div>

{tooltip.nodes.length ? <div
style={floatingStyles}
ref={refs.setFloating}
className="text-[12px] p-4 absolute bg-slate-800 rounded-md max-w-[600px] transition-none border border-slate-500"
>
{tooltip.nodes.map((node, ind) => (
<Code content={node.text} lang={node.lang} key={ind} />
))}
</div> : null}

<div onMouseMove={matchTooltips}>
<FakeTypes matchers={matchers} id={id}/>

<Code content={content} className="bg-gray-800 max-sm:text-[0.7rem] max-sm:p-2 font-mono p-5 text-current" />

</div>
<Code content={content} id={id} className="bg-gray-800 max-sm:text-[0.7rem] max-sm:p-2 font-mono p-5 text-current" />

</div>
);
Expand Down
1 change: 1 addition & 0 deletions packages/example-nextjs14/src/app/highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ function getHighlighter() {
})
}

const _warmUp = getHighlighter()
13 changes: 13 additions & 0 deletions packages/example-nextjs14/src/app/utils.ts
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit e43db31

Please sign in to comment.