Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use other tag inside Scrollycoding #478

Open
Windsooon opened this issue Nov 5, 2024 · 4 comments
Open

How to use other tag inside Scrollycoding #478

Windsooon opened this issue Nov 5, 2024 · 4 comments

Comments

@Windsooon
Copy link

I'm trying to use Tag <CodeWithTooltips> inside <Scrollycoding> like this:

<Scrollycoding>

## !!steps A Game of Thrones


A Game of Thrones is the first book in the A Song of Ice and Fire series by George R.R. Martin. Set in a world where seasons last for years, it introduces a complex plot and a wide cast of characters, ranging from noble families vying for the Iron Throne to the supernatural threats in the North.


<CodeWithTooltips>
```js ! george.js
const houses = [
  "Stark",
  "Lannister",
  "Baratheon",
  "Targaryen",
]

const winner =
  houses[
    Math.floor(
      Math.random() * houses.length,
    )
  ]

console.log(`Iron Throne: ${winner}`)
```

However, it tells me

Error: at 
## !!steps A Game of Thrones
Error for `code`: Required
{
  "expected": "object",
  "received": "undefined"
}

I guess because I use the example for CodeWithTooltips which return

return <Pre code={highlighted} handlers={[tooltip]} />

It's not a code object so <Scrollycoding> can not parse it. So I tried another way to update the Code function for scrollycoding.tsx which would work

async function Code({ codeblock, tooltips }: { codeblock: RawCode; tooltips?: Block[] }) {
  console.log(tooltips);
  const highlighted = await highlight(codeblock, "github-from-css")
  highlighted.annotations = highlighted.annotations.map((a) => {
    const tooltip = tooltips.find((t) => t.title === a.query);
    if (!tooltip) return a;
    return {
      ...a,
      data: { ...a.data, children: tooltip.children },

    }
  })
  return (
    <Pre
      code={highlighted}
      handlers={[tooltip, tokenTransitions, wordWrap]}
      className="min-h-[40rem]"
    />
  )
}

However, I also want to use other tags like <CodeSwitcher> inside <Scrollycoding>, Are there any better way to implement is?

@pomber
Copy link
Contributor

pomber commented Nov 5, 2024

One way is to change the Scrollycoding component to accept a Block instead of a CodeBlock, then you can render anything inside the sticky part.

<Scrollycoding>

## !!steps A Game of Thrones


A Game of Thrones is the first book in the A Song of Ice and Fire series by George R.R. Martin. Set in a world where seasons last for years, it introduces a complex plot and a wide cast of characters, ranging from noble families vying for the Iron Throne to the supernatural threats in the North.

### !sticker

<CodeWithTooltips>
...
</CodeWithTooltips>

## !!steps ...

...

@Windsooon
Copy link
Author

Windsooon commented Nov 14, 2024

@pomber Thank you for your reply, I try several ways in the past few days but still couldn't figure it out:

First Way

I create a Schema like this:

const Schema = Block.extend({
  steps: z.array(
    Block.extend({
        sticker: Block.extend({
            code: CodeBlock,
            tooltips: z.array(Block).optional(),
    })
  })),
})

And the MDX looks like:

<Scrollycoding>


## !!steps A Game of Thrones

A Game of Thrones is the first book in the A Song of Ice and Fire series by George R.R. Martin. Set in a world where seasons last for years, it introduces a complex plot and a wide cast of characters, ranging from noble families vying for the Iron Throne to the supernatural threats in the North.

The Fellowship of the Ring

### !sticker

<CodeWithTooltips>

  ~~~js !code
  // !tooltip[/lorem/] description
  function lorem(ipsum, dolor = 1) {
    const sit = ipsum == null ? 0 : ipsum.sit
    dolor = sit - amet(dolor)
    // !tooltip[/consectetur/] inspect
    return sit ? consectetur(ipsum) : []
  }
  ~~~

## !!tooltips description

### Hello world

Lorem ipsum **dolor** sit amet `consectetur`.

</CodeWithTooltips>
</Scrollycoding>

I still got the error from const {steps} = parseProps(props, Schema)

Error: at 
## !!steps A Game of Thrones
### !sticker
Error for `code`: Required
{
  "expected": "object",
  "received": "undefined"
}

I don't know why the Schema didn't work.

Second Way

I created a Schema:

const Schema = Block.extend({
  steps: z.array(
    Block.extend({
        sticker: Block
  })),
})

const ToolSchema = Block.extend({
  code: CodeBlock,
  tooltips: z.array(Block).optional(),
})

And try to access code and tooltips using

export function Scrollycoding(props: unknown) {
  const { steps } = parseProps(props, Schema)
  return (
    <SelectionProvider className="flex gap-4">
      <div className="flex-1 mt-32 mb-[90vh] ml-2 prose min-w-60">
        {steps.map((step, i) => (
          <Selectable
            key={i}
            index={i}
            selectOn={["click", "scroll"]}
            className="border-l-4 data-[selected=true]:border-blue-400 px-5 py-2 mb-24 rounded bg-card"
          >
            <h2 className="mt-4 text-xl">{step.title}</h2>
            <div>{step.children}</div>
          </Selectable>
        ))}
      </div>
      <div className="w-1/2 bg-card">
        <div className="top-16 sticky overflow-auto">
          <Selection
            from={steps.map((step) => (
              <CodeWithTooltips props={step.sticker} />
            ))}
          />
        </div>
      </div>
    </SelectionProvider>
  )
}

async function CodeWithTooltips(props: unknown) {
  const { code, tooltips = [] } = parseProps(props, ToolSchema)
  const highlighted = await highlight(code, "github-from-css")

  highlighted.annotations = highlighted.annotations.map((a) => {
    const tooltip = tooltips.find((t) => t.title === a.query)
    if (!tooltip) return a
    return {
      ...a,
      data: { ...a.data, children: tooltip.children },

    }
  })
  return <Pre code={highlighted} handlers={[tooltip]} />
}

I still got

Error: at root
Error for `code`: Required
{
  "expected": "object",
  "received": "undefined"
}

I wonder which part I missed, Thank you.

@pomber
Copy link
Contributor

pomber commented Nov 14, 2024

In your first way, make the sticker a bare Block:

const Schema = Block.extend({
  steps: z.array(Block.extend({sticker: Block})),
})

and then you render the children:

<Selection
  from={steps.map((step) => (
    step.sticker.children
  ))}
/>

@Windsooon
Copy link
Author

Thank you so much. It works well!
I also found that token-transitions.tsx would add display: "inline-block"

export const tokenTransitions: AnnotationHandler = {
  name: "token-transitions",
  PreWithRef: SmoothPre,
  Token: (props) => (
    <InnerToken merge={props} style={{ display: "inline-block" }} />
  ),
}

so the tooltip classunderline decoration-dashed didn't work as expected. I solved it by adding an extra style

export const tooltip: AnnotationHandler = {
  name: "tooltip",
  Inline: ({ children, annotation }) => {
    const { query, data } = annotation
    return (
      <TooltipProvider>
        <Tooltip>
          <TooltipTrigger
            className="underline decoration-dashed"
            style={{ borderBottom: "1px dashed black" }} // Add border and padding
          >
            {children}
          </TooltipTrigger>
          <TooltipContent align="start">
            {data?.children || query}
          </TooltipContent>
        </Tooltip>
      </TooltipProvider>
    )
  },
}

@pomber Do you think we should add an demo for using nested tag? I can create a PR using my current example if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants