bigskysoftware / _hyperscript

a small scripting language for the web
BSD 2-Clause "Simplified" License
3.14k stars 149 forks source link

On variable change events #194

Open mikemissing opened 2 years ago

mikemissing commented 2 years ago

Hej, still loving the good work! I had an idea which I want to bounce here: acting on variable changes (reactivity)

Motivation

Having the ability to react to variable changes reduces the need for extra vanilla js or 3rd party libs, like alpinejs.

POC Setup

I have a table of products, they all have an unique id, I want to be able to select a subset of the products and update a link somewhere reflecting the selection.

Code example

I am not 100% sure on code correctness but I hope the idea is clear. Also, toggle would make more sense than just add but I wanted to use append just as it's there ...

<table _="init set @ids to [] end">
...   
<tr _="on click append 'id1' to @ids"> ... </tr>
<tr _="on click append 'id2' to @ids"> ... </tr>
...
</table>

And then somewhere else I would put

<a href="#" _="on change of @ids set [href=@ids.join(',')]">link</a>

Alternative example would be

<div _="on change of @ids set my.innerHtml to "Products selected" + @ids.length">No products selected</div>

Questions

This boils down to two questions I guess:

  1. does it make sense for hyperscript to handle such change events
  2. can the set sommand handle arbitrary js code to execute as the attribute value (having js code there would probably make most sense, one could get away by having something else)?

Summary

It's just an idea but if present would completely eliminate the need of something else that can handle reactivity, and ... the moral dilemma is, if I need to pull in another thing, why have two dependencies then.

dz4k commented 2 years ago

A few thoughts:

Here's how I would implement a table with multi-select items:

on click -- this is in the table code
  tell closest <tr/> to target
    toggle @aria-selected
    add .selected
    -- and so on... --
  end
  trigger itemsChanged
on itemsChanged from #the-table -- this is in the link code
  get @data-id of <[aria-selected=true]/> in #items-table
  set #link.href to result.join(',')

More code would be needed to implement a proper listbox, @1cg maybe we should have examples of WAI-ARIA patterns, they are useful and in JS, harder than they seem to implement properly.

<ul role="listbox" _="install Listbox">
  <li role="option" aria-selected="false">Option 1</li>
  ...
</ul>
mikemissing commented 2 years ago

I mean, once I understood what your code did I was mind blown, AMAZING!!! I guess if you spend too much time in vue everything starts to look like vue ...

I guess you can write hyperscript or you can WRITE hyperscript. And the use of attributes ... OMG!!

I literally just replaced all of existing ugly, repetitive vanilla js with what I think is the most beautiful thing I've written in a while, all hyperscript.

Thank for the help, original idea makes no sense anymore.

PS: not whining but the examples do not really cover the full potential of the language, or I really read them badly.

bencroker commented 2 years ago

@mikemissing can you share what you ended up with, to help better inform us of useful examples that could be added to the site?

mikemissing commented 2 years ago

Ah yes, of course.

The table gets the following code:

<table>
    ...
    <tbody
        class="product-selector"
         _="on click
        tell closest <tr/> to target
            toggle .selected-product
        end
        send productSelectionChanged to #navigation"
    >
        <tr data-id="id1">...</tr>
        <tr data-id="id2">...</tr>
    ....
</table>

The navigation panel gets the following code:

<div 
    id="navigation"
    _="on productSelectionChanged
        get @data-id of .selected-product in <tbody.product-selector/>
        if result is null then set ids to '' else set ids to result.join(',') end
        for e in <a.reflects-product-selection/>
            get @data-url of e
            set base to it
            set e.href to base + '?&ids=' + ids
        end
    ">
        <a href="/home">home</a>
        <a href="#" data-url="/compare" class="reflects-product-selection">compare</a>
    ...
</div>

The tricky part was on the navigation side, @dz4k did a very good job explaining how the selection could be written more hyperscripty. I also omitted here the CSS styling of rows for hover, select, clear etc.

I mean, it is not much, it could also be written better (also, not a direct c/p) but when I realised that I can do meaningful reactive element manipulation without reverting to vanilla js, that was the big AHA! moment.

bencroker commented 2 years ago

Very cool!