TEIC / CETEIcean

TEI in HTML5 Custom Elements
BSD 2-Clause "Simplified" License
164 stars 36 forks source link

Behavior-functions are not triggered #65

Closed FransJoakim closed 1 year ago

FransJoakim commented 1 year ago

I have a reoccurring issue with behaviors that are added to CETEI. I've been trying different things, but none of the behaviors get activated. Nothing is logged to the console, and no header gets changed. It actually worked for a short while, then stopped again.

  const ct = new CETEI()
  let behaviors = {
  tei: {
      div: function (e) {
          console.log("div")
          return e
      },
      head: function (e) {
          let result = document.createElement("h" + e.dataset.level)
          for (let n of Array.from(e.childNodes)) {
              result.appendChild(n.cloneNode())
          }
          return result
      },
  },
  }
  ct.addBehaviors(behaviors)
  const html = await ct.makeHTML5(data)

In your tutorial the keys are shown as strings, but JS simply change these to a normal object-key, like above.

Is there anything I've done wrong?

Thank you for providing an otherwise great library !

FransJoakim commented 1 year ago

It turns out these functions don't run (or don't seem to run) if these nodes later are altered when mounted into react using the functions from this library: https://github.com/pfefferniels/react-teirouter/tree/master

I don't understand why, but it doesn't seem to be a problem with you're code

hcayless commented 1 year ago

@raffazizzi has code for working with CETEIcean in react. I think https://github.com/raffazizzi/react-teirouter is the right repo, but I couldn't swear to it. My sense though is that behaviors don't necessarily play well with react. I think Raff mostly just uses react components instead.

raffazizzi commented 1 year ago

Hi @FransJoakim, with react-teirouter, I would advise replacing CETEIcean behaviors with React components. You can attach a custom React component to tei-head and handle it natively with React methods, state management, etc.

You probably already know from the react-teirouter readme that you can route CETEIcean elements to React components, e.g.:

const domData // a DOM object, e.g. generated with CETEIcean.

<TEIRender data={domData}>
  <TEIRoute el='tei-head' component={Head}/>
</TEIRender>

and the component (I'm trying to emulate your code above, but I didn't test this):

const Head = (props) => {
 // you're determining the element to create dynamically so I can't use jsx here, but we can use React.createElement
  return (
    React.createElement(
     "h" + props.teiNode.dataset.level, // element name
     null, // no attributes needed
     <TEINodes teiNodes={props.teiNode.childNodes} {...props} />} // content (I can use JSX again and this will append the content of tei-head)
   )
  )
}

(NB the main repo for tei-reactrouter is actually the one you're using, maintained by @pfefferniels; my fork is used only to send PRs to that repo).

Also note that tei-reactrouter is a generic library to map React components to CETEIcean elements, so it won't include CETEIcean's default behaviors. However, I recreated most of them in React for a Gatsby theme. You can find the code here and you may be able to import it and use it even outside of a Gatsby enviroment; if you try it let me know how it goes! You should be able to install it via npm npm install gatsby-theme-ceteicean.

It comes with default behaviors (modeled after the ones in vanilla CETEIcean) and you can Route them through react-teirouter, eg:

import { TeiHeader } from "gatsby-theme-ceteicean/src/components/DefaultBehaviors"

// ...

<TEIRender data={domData}>
  <TEIRoute el='tei-teiHeader' component={TeiHeader}/>
  <TEIRoute el='tei-head' component={Head}/>
</TEIRender>

Alternatively, you don't need to use react-teirouter, just use vanilla CETEIcean with React.useLayoutEffect(), something like this, but it's less efficient and you lose the opportunity to use React with CETEIcean elements:

React.useLayoutEffect(() => {
  // CETEI code to run after rendering
}, []) // <- note the empty array here