choojs / choo

:steam_locomotive::train: - sturdy 4kb frontend framework
https://choo.io/
MIT License
6.78k stars 595 forks source link

Widgets with choo #250

Closed callum closed 8 years ago

callum commented 8 years ago

I'm trying to create a reusable element that implements Quill but I can't figure out how to do it. The issue I'm having is that on re-render the Quill editor gets wiped; I don't know how to maintain the state of it.

The most basic example:

const html = require('choo/html')
const onload = require('on-load')
const Quill = require('quill')

module.exports = richtext

function richtext (state, prev, send) {
  const div = html`<div></div>`
  onload(div, (el) => {
    const quill = new Quill(el)
    quill.on('text-change', () => {
      send() // triggers re-render and DOM state is lost
    })
  })
  return div
}

I used virtual-widget a while back - something like that would be ideal for this.

reminyborg commented 8 years ago

If you only need a single instance of quill then i would do domething like this

The gist is that you create an element that you hook the Quill instance to. And then you can easily just drop it into your choo dom tree.

If you need multiple instances running try tenants Still needs some work but it works really well for my usecases.

callum commented 8 years ago

Excellent @reminyborg - I will give that a try. Thanks

callum commented 8 years ago

Just seen the D3 example and it's not clear to me how you would create multiple instances in my case. Can you give an example?

reminyborg commented 8 years ago

I created a short little gist with quill for you. Its a really nice editor.

You still need to hook up send ++ but it should get you started.

Edit: You have to add: <link href="http://cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"> to the head. Requirebin doesnt save it for some reason...

timwis commented 8 years ago

Wow, interesting approach @reminyborg, i'm surprised that works. We talked about this in patrick-steele-idem/morphdom#77, and the latest PR on morphdom should make memoizing possible. Though it looks like that's what you're doing... have you played around with onload with your approach?

reminyborg commented 8 years ago

@timwis thanks. I have used this approach quite a lot in other frameworks and while it might seem a bit strange at first it gives you a lot of control.

Ive used the onload on my little leaflet gist. It is used to invalidate the leaflet size so it rerenders the map once it knows its size in the dom. In that gist you can also se the awesomeness of getting the state and prev values to do diffing based "mutations" of the leaflet map.

yoshuawuyts commented 8 years ago

waiting on https://github.com/patrick-steele-idem/morphdom/issues/77 to be published; made a demo on how to create widgets with the isSameNode() API here: https://github.com/shama/on-load/issues/10#issuecomment-246118634 (e.g. thunking!)

This should provide exactly the experience we intended to have from the start :tada:

yoshuawuyts commented 8 years ago

Yay, all the PRs have been merged and it should all be working. As per #1 we're now building out cache-element/widget which should allow for super clean widgetization of elements :sparkles: The API looks like this:

const widget = require('cache-element/widget')
const html = require('bel')

const renderEl = widget(function (update) {
  let name = null
  let age = null

  update(onupdate)

  return html`
    <p onload=${onload} onunload=${onunload}>
      Name is ${name} and age is ${age}
    </p>
  `

  function onupdate (newName, newAge) {
    name = newName
    age = newAge
  }

  function onload () {
    console.log('added to DOM')
  }

  function onunload () {
    name = null
    age = null
    console.log('removed from DOM')
  }
})

let el = renderEl('Tubi', 12) // creates new element
let el = renderEl('Tubi', 12) // returns cached element (proxy)
let el = renderEl('Babs', 25) // returns cached element (proxy)

I think this should answer your question; consider watching / contributing to cache-element to get this out faster, but in essence all the lower level components now work and exist :grin: