max-mapper / yo-yo

A tiny library for building modular UI components using DOM diffing and ES6 tagged template literals

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

composition and yo-yo discussion

kristoferjoseph opened this issue · comments

commented

One thing I see myself missing when using yo-yo is the ability to compose elements in the template string.
How should I be thinking about composing multiple reusable components?
I would really like to be able to treat them similarly to custom elements like so:

/* list-item.js */
var yo = require('yo-yo')
var Icon = require('./components/icon')
module.exports = function(item) {
    return yo`<li>
         <Icon src=${item.icon}>
         <span>${item.label}
   </li>`
}
/* composite-component.js */
var yo = require('yo-yo')
var ListItem = require('./components/ListItem')

var el = list([
  {
    icon:'grizzly.svg'
    label: 'grizzly'
 },
   {
    icon:'polar.svg'
    label: 'polar'
 },
   {
    icon:'brown.svg'
    label: 'brown'
 }
])

function list (items) {
  return yo`<ul>
    ${items.map(function (item) {
      return yo`<ListItem>${item}</ListItem>`
    })}
  </ul>`
}

document.body.appendChild(el)
commented

So in my mind custom elements are nothing but named groups of DOM elements with some behavior attached. To get the same in JS we should have 1. a way of naming elements and 2. a way of grouping DOM. Functions seem to do that pretty well!

So as an example:

/* list-item.js */
var yo = require('yo-yo')
var Icon = require('./components/icon')
module.exports = function(item) {
  return yo`<li>
     ${Icon(item.icon)}
     <span>${item.label}
  </li>`
}
/* composite-component.js */
var yo = require('yo-yo')
var ListItem = require('./components/ListItem')

var el = list([
  {
    icon:'grizzly.svg'
    label: 'grizzly'
 },
   {
    icon:'polar.svg'
    label: 'polar'
 },
   {
    icon:'brown.svg'
    label: 'brown'
 }
])

function list (items) {
  return yo`<ul>
    ${items.map(ListItem)}
  </ul>`
}

document.body.appendChild(el)

I don't think we need to fit everything into XML to work well with it. Does this work for you?

commented

Seems legit. Will hammer on this a bit and see.
Ends up looking like some weird bastard child of handlebars doesn't it? ;)

/* ...other required components */
function Component(state) {
   return yo`<div>
        ${Header(state)}
        ${ScrollArea(state)}
        ${Footer(state)}
    </div>`
}
commented

Hahaha, but using yo-yoify the perf overhead is infinitely times smaller - because there isn't any ^__^

Also mentioned here, we could create a transform that would accomplish the original proposed composition. But I too have gotten used to ${Icon(item.icon)} so haven't tried implementing it yet.

commented

@shama I'd be down to get this working, mainly because I feel it would aid adoption/support. What level do you feel this transform should be at? Is it a bel concern or a yo-yo level transform?

I agree that it would be nice to be able to use our "elements" like we use native elements. Isn't that the idea behind custom elements in the web components spec? A few days ago I was toying around with using custom elements in choo/yo-yo: demo (type in your github username). You'll notice that it uses <user-profile> in the <body> too - not just via JS.

I know that browsers other than recent chrome require a polyfill, and I've heard there are still performance issues with custom elements (though I don't know what they are). I'm still excited about them and thought they might be at least tangentially related to this thread.

commented

I mean, if I am going to use a transform anyways for bundling I'm thinking a transform to expand imported templates wouldn't be too bad ( says the guy who doesn't want to have to write babelscript anymore )

@kristoferjoseph I think of yo-yoify as an [optional] optimization for your production code, just as you'd minify/uglify it, as opposed to babel/JSX transpiling, which is necessary to even run it.

commented

@timwis I was mainly joking. ;)
Would be nice to not even have to browserify, but bundling is a necessary thing so ¯_(ツ)_/¯

Oh yeah, for custom components, I forgot about this project: https://github.com/chromakode/diablo but haven't got to play around with it too much yet.

My primary issue with registering elements to use as <user-profile> is all the components share the same space. So if I registers a <x-button> and another module registers a <x-button> whose button wins? Maybe there is a part of the web component spec that has thought about this already though?

I think a library for an application to map up modules to registered elements would be cool though, but only at the app level.


The transform idea is similar to yo-yoify, but it reads the JS and catalogs the variables on the same scope as template literals. Then when pre-parsing the template literals, maps the custom elements to the variable names on the scope.

Then the author can use the syntax they prefer but the published element would still be just a native element for the rest of us to consume.

commented

@shama daaaang diablo

Custom element collisions are an interesting topic, not sure how concerned I am. Seems like importing a set of custom elements is an explicit user action, same with defining a namespace for them... pretty sure you get a warning on collision, but I stopped following all of that since they are just a working draft still.

I am happy with just expanding to templates with a transform like you outlined. "Real" custom elements may be interesting to chase later on, but definitely in the imaginary dragons realm of the web currently.

commented

Closing this for now as people seem content with ${Custom(state)} for now. Will work on a template expanding transform in parallel.