From d04e374231d266ab664775f01cc927f913880f00 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 12 Mar 2024 19:21:01 -0600 Subject: [PATCH] ul done and dusted --- app/globals.css | 38 +++-- components/ttcmd/index.tsx | 282 +++++++---------------------------- lib/tcmd/TokenIdentifiers.ts | 35 ++++- lib/tcmd/index.ts | 131 +++++++++++++++- 4 files changed, 237 insertions(+), 249 deletions(-) diff --git a/app/globals.css b/app/globals.css index cab3c9d..dda59e5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,51 +4,63 @@ @layer base { * { - @apply text-white box-border + @apply text-white box-border; } body { - @apply dark:bg-mixed-100 bg-primary-600 + @apply dark:bg-mixed-100 bg-primary-600; } input { - @apply py-2 px-4 rounded-full dark:bg-mixed-200 bg-mixed-600 placeholder:text-dark-500 + @apply py-2 px-4 rounded-full dark:bg-mixed-200 bg-mixed-600 placeholder:text-dark-500; } - h1,h2,h3,h4,h5,h6 { - @apply font-bold + h1, + h2, + h3, + h4, + h5, + h6 { + @apply font-bold; } p { - @apply py-1 + @apply py-1; } } @layer components { .strapline { - @apply dark:text-primary-500 text-primary-100 uppercase font-bold mb-2 text-lg + @apply dark:text-primary-500 text-primary-100 uppercase font-bold mb-2 text-lg; } .heading { @apply pb-8 border-b border-b-primary-500 dark:border-b-dark-500 min-w-full; } .heading h1 { - @apply text-5xl font-bold + @apply text-5xl font-bold; } .card { @apply dark:bg-mixed-200 bg-primary-500 rounded-3xl p-6 shadow-2xl /* mix-blend-luminosity */ - /* @apply bg-mixed-200 rounded-3xl p-6 shadow-2xl */ + /* @apply bg-mixed-200 rounded-3xl p-6 shadow-2xl */; } .btn-primary { - @apply dark:bg-primary-500 bg-primary-100 py-4 px-6 dark:text-mixed-100 text-white rounded-full font-bold text-lg + @apply dark:bg-primary-500 bg-primary-100 py-4 px-6 dark:text-mixed-100 text-white rounded-full font-bold text-lg; } .btn-secondary { - @apply dark:text-primary-500 text-primary-100 py-4 px-6 font-bold text-lg + @apply dark:text-primary-500 text-primary-100 py-4 px-6 font-bold text-lg; } .p { - @apply py-1 + @apply py-1; } .poppable { - @apply card dark:bg-mixed-300 bg-primary-600 p-2 rounded-lg transition-opacity data-[visible=true]:z-10 data-[visible=true]:opacity-100 data-[visible=false]:opacity-0 -z-10 max-w-[400px] absolute + @apply card dark:bg-mixed-300 bg-primary-600 p-2 rounded-lg transition-opacity data-[visible=true]:z-10 data-[visible=true]:opacity-100 data-[visible=false]:opacity-0 -z-10 max-w-[400px] absolute; + } +} + +@layer utilities { + .list-1 { + /* @apply list-[square]; */ + list-style: square; } } diff --git a/components/ttcmd/index.tsx b/components/ttcmd/index.tsx index f094a86..972040f 100644 --- a/components/ttcmd/index.tsx +++ b/components/ttcmd/index.tsx @@ -12,6 +12,12 @@ import StaticGenerationSearchParamsBailoutProvider from "next/dist/client/compon export const TTCMD: FC<{ body: Promise }> = ({ body }) => { const text = use(body); const [elements, tabSpacing] = useMemo(() => createElements(text), [text]); + const tada = useMemo( + () => ( + <>{renderer(elements.filter((e) => !e.parent).map((e) => e.token!))} + ), + [elements], + ); return (
@@ -24,7 +30,7 @@ export const TTCMD: FC<{ body: Promise }> = ({ body }) => {
{/* {elements.map((e, i) => {render(e)})} */} - {renderer(elements.map((e) => e.token!), tabSpacing)} + {tada}
//
// {/*
{JSON.stringify(elements,null,2)}
*/} @@ -32,14 +38,14 @@ export const TTCMD: FC<{ body: Promise }> = ({ body }) => { ); }; -const renderer = (tokens: Token[], tabSpacing: number) => { +function renderer(tokens: Token[]) { const usedIds: string[] = []; return tokens.map((t) => ( -
{render(t, usedIds, tabSpacing)}
+
{render(t, usedIds)}
)); -}; +} -const render = (token: Token, usedIds: string[], tabSpacing: number) => { +function render(token: Token, usedIds: string[]) { switch (token.type) { case "heading": return ( @@ -66,7 +72,7 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => { > {token.children?.map((c, i) => (
- {render(c, usedIds, tabSpacing)} + {render(c, usedIds)}
))}
@@ -82,7 +88,7 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => {
{token.children?.map((e) => ( - {render(e, usedIds, tabSpacing)} + {render(e, usedIds)} ))}
@@ -123,7 +129,7 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => { case "popover": return ( render(c, usedIds, tabSpacing)) || + content={token.children?.map((c) => render(c, usedIds)) || token.content} preferredAlign="centered" preferredEdge="bottom" @@ -141,7 +147,7 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => {
{token.children?.map((e, i) => ( - {render(e, usedIds, tabSpacing)} + {render(e, usedIds)} ))}
@@ -155,7 +161,7 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => { {token.children?.map((e, i) => ( - {render(e, usedIds, tabSpacing)} + {render(e, usedIds)} ))} @@ -174,6 +180,37 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => { {token.content} ); + case "list": + const items = token.children || []; + return ( + <> +
    + {items.map((c) => { + return ( +
  • + {c.children?.map((c) => render(c, usedIds))} +
  • + ); + })} +
+ + ); + case "list-item": + // This probably doesn't need to exist, but I'm leaving it anyway + return ( +
  • + {token.children?.map((c) => render(c, usedIds))} +
  • + ); default: return (
    @@ -181,94 +218,9 @@ const render = (token: Token, usedIds: string[], tabSpacing: number) => {
    ); } -}; +} -// const renderBlock = ( -// block: BlockChildren, -// usedIds: string[] = [], -// ): ReactNode => { -// usedIds = usedIds || []; -// switch (block.type) { -// case "block": -// return block.children.map((e, i) => ( -// {renderBlock(e, usedIds)} -// )); -// case "grid": -// return ( -//
    -// {block.children.map((c, i) => ( -//
    -// {renderBlock(c, usedIds)} -//
    -// ))} -//
    -// ); -// case "card": -// return ( -//
    -// {block.children.map((e, i) => ( -// -// {renderBlock(e, usedIds)} -// -// ))} -//
    -// ); -// case "accordion": -// return ( -//
    -// -// -// {block.children.map((e, i) => ( -// -// {renderBlock(e, usedIds)} -// -// ))} -// -// -//
    -// ); -// default: -// return ( -// renderParagraph(block as ParagraphToken, usedIds) -// ); -// } -// }; - -// const renderParagraph = (p: ParagraphToken, usedIds: string[]) => { -// switch (p.type) { -// case "p": -// return ( -//
    -// {p.content.map((e, i) => ( -// -// {renderToken(e, usedIds)} -// -// ))} -//
    -// ); -// case "code": -// return ( -//
    -//           {p.content.map((c) => c.line.toString()).join("\n\n")}
    -//         
    -// ); -// default: -// return ( -//
    -// Block or paragraph missing implementation: {p.type} -//
    -// ); -// } -// }; - -const generateId = (t: string, usedIds: string[]) => { +function generateId(t: string, usedIds: string[]) { let id = t.toLowerCase().replace(/[^a-z\s]/ig, "").trim().replaceAll( " ", "-", @@ -280,138 +232,4 @@ const generateId = (t: string, usedIds: string[]) => { idNum++; } return id; -}; - -// const renderToken = (t: Token, usedIds: string[]) => { -// switch (t.type) { -// case "h1": { -// return ( -//
    -// {renderInlineToken(t.line)} -//
    -// ); -// } -// case "h2": { -// return ( -//
    -// {renderInlineToken(t.line)} -//
    -// ); -// } -// case "h3": { -// return ( -//
    -// {renderInlineToken(t.line)} -//
    -// ); -// } -// case "p": -// return ( -//
    -// {t.lines.map((e, i) => ( -// -// {renderInlineToken(e.line)} -// -// ))} -//
    -// ); -// case "text": -// return ( -// <> -// {renderInlineToken(t.line)} -//   -// -// ); -// case "list1": -// return
  • {renderInlineToken(t.line)}
  • ; -// case "list2": -// return
  • {renderInlineToken(t.line)}
  • ; -// default: -// return ( -//
    -// Missing implementation for tcMD element `{(t as { type: string }) -// .type}` -//
    -// ); -// } -// }; - -// const renderInlineToken = (l: Line) => { -// if (typeof l === "string") return l; - -// return l.map((token) => ( -// -// {(() => { -// switch (token.type) { -// case "text": -// return {token.content}; -// case "bold": -// return {token.content}; -// case "italic": -// return {token.content}; -// case "anchor": -// return ( -// -// {token.content} -// -// ); -// case "image": { -// token.data.src = token.data.src as string; -// if (token.data.src.startsWith(" -// -// ); -// } -// // eslint-disable-next-line @next/next/no-img-element -// return {token.content}; -// } -// case "popover": -// return ( -// -// -// {token.content} -// -// -// ); -// case "inline-code": -// return ( -// -// {token.content} -// -// ); -// default: -// return ( -// -// Inline element not implemented: {token.type} -// -// ); -// } -// })()} -// -// )); -// }; +} diff --git a/lib/tcmd/TokenIdentifiers.ts b/lib/tcmd/TokenIdentifiers.ts index 3a1072a..db76afd 100644 --- a/lib/tcmd/TokenIdentifiers.ts +++ b/lib/tcmd/TokenIdentifiers.ts @@ -21,7 +21,7 @@ export const TokenIdentifiers = new Map !m.parent); - // return markers; + // return markers.filter((m) => !m.parent); + return markers; } +// function establishClosestParent(blocks: TokenMarker[]): void { +// blocks.sort((a, b) => a.start - b.start); // Sort blocks by start position + +// for (let i = 0; i < blocks.length; i++) { +// const block = blocks[i]; +// if (block.parent) continue; // Skip blocks that already have a parent + +// let closestParent: TokenMarker | undefined = undefined; +// let minDistance = Number.MAX_SAFE_INTEGER; + +// // Find the closest parent block for each block +// for (let j = 0; j < i; j++) { +// const otherBlock = blocks[j]; +// if (otherBlock.end >= block.start && otherBlock.start <= block.start) { +// const distance = block.start - otherBlock.start; +// if (distance < minDistance) { +// minDistance = distance; +// closestParent = otherBlock; +// } +// } +// } + +// if (closestParent) { +// block.parent = closestParent; // Assign the closest parent block +// } +// } +// } + function establishClosestParent(blocks: TokenMarker[]): void { blocks.sort((a, b) => a.start - b.start); // Sort blocks by start position @@ -84,7 +112,10 @@ function establishClosestParent(blocks: TokenMarker[]): void { const otherBlock = blocks[j]; if (otherBlock.end >= block.start && otherBlock.start <= block.start) { const distance = block.start - otherBlock.start; - if (distance < minDistance) { + if ( + distance < minDistance && + isAcceptableChild(otherBlock.type, block.type) + ) { minDistance = distance; closestParent = otherBlock; } @@ -97,6 +128,20 @@ function establishClosestParent(blocks: TokenMarker[]): void { } } +type ParentChildMap = { + [parentType: string]: string[]; // Map parent types to an array of acceptable child types +}; + +const parentChildMap: ParentChildMap = { + "list": ["list-item"], + // Add more mappings as needed... +}; + +function isAcceptableChild(parentType: string, childType: string): boolean { + const acceptableChildren = parentChildMap[parentType]; + return acceptableChildren ? acceptableChildren.includes(childType) : true; +} + function filterOverlappingPBlocks(blocks: TokenMarker[]): TokenMarker[] { return blocks.filter((block) => { if (block.type !== "p") { @@ -122,6 +167,10 @@ function filterOverlappingPBlocks(blocks: TokenMarker[]): TokenMarker[] { const contentToChildren = (token: Token) => { let content = token.content; + const wasSpecialCase = handleSpecial(token); + + if (wasSpecialCase) return; + const splitMarker = "{{^^}}"; for (const child of token.children || []) { content = content.replace(child.raw, splitMarker); @@ -139,3 +188,79 @@ const contentToChildren = (token: Token) => { token.children || [], ).filter((c) => c.children?.length || (c.rendersContentOnly && c.content)); }; + +function handleSpecial(token: Token) { + switch (token.type) { + case "list": { + const chunks = splitByDepth(token.children!); + const items = processChunks(chunks); + token.children = items.flat(); + return token.children; + } + default: + return; + } +} + +function splitByDepth(items: Token[]) { + const chunks: Token[][] = []; + let currentDepth = -1; + let chunk: Token[] = []; + + if (!items) return chunks; + + for (const item of items) { + const depth = Number(item.metadata.initialDepth); + if (depth === currentDepth) { + chunk.push(item); + } else { + if (chunk.length > 0) { + chunks.push(chunk); + } + chunk = [item]; + currentDepth = depth; + } + } + + if (chunk.length > 0) { + chunks.push(chunk); + } + + return chunks; +} + +function processChunks(chunks: Token[][]) { + const mergedChunks: Token[][] = []; + for (let i = 1; i < chunks.length; i++) { + const currentChunk = chunks[i]; + let j = i - 1; + + // Find the first chunk with a lower depth + while (j >= 0) { + const prevChunk = chunks[j]; + const prevDepth = Number(prevChunk[0].metadata.initialDepth); + + if (prevDepth < Number(currentChunk[0].metadata.initialDepth)) { + // Append the current chunk to the children of the found chunk + const lastPrev = prevChunk[prevChunk.length - 1]; + lastPrev.children = lastPrev.children || []; + lastPrev.children.push({ + type: "list", + content: "", + raw: "", + metadata: { initialDepth: currentChunk[0].metadata.initialDepth }, + uuid: crypto.randomUUID(), + children: currentChunk, + }); + mergedChunks.push(currentChunk); + break; + } + + j--; + } + } + + // console.log(chunks); + // Filter out chunks that were merged into others + return chunks.filter((c) => !mergedChunks.find((c2) => c === c2)); +}