Active-CSS / active-css

The epic event-driven browser language for UI with functionality in one-liner CSS. Over 100 incredible CSS commands for DOM manipulation, ajax, reactive variables, single-page application routing, and lots more. Could CSS be the JavaScript framework of the future?
https://activecss.org
Other
42 stars 7 forks source link

Allow @ statements in HTML block rendering in components #330

Closed bob2517 closed 1 year ago

bob2517 commented 1 year ago

Possible syntax - this came up in a conversation a while back and it should get an issue:

@component something {
  html {
      <i class="{:
         @if (localStorage.getItem("lampQuarto") == "acessa") {
            render: "fa-solid text-success";
         } @else {
            render: "fa-regular text-danger";
         }
     :} fa-lightbulb fa-2x" id="lampada_quarto"></i>
  }
}

The render target "selector" would be the html string block itself, and the target selector for everything else would be the component host. The scope would be determined by the component. Some commands wouldn't make sense in there. But if I tie it into the general event flow it could in theory run most if not all the commands that would make sense to run, such as loops, etc. Will ponder this one while I'm doing the pre-rendered component upgrade.

Note to self: Would be easy enough to extract the code within the {: :}, including multiple instances. Just slap a placeholder there at parse time, put the events in the config under a pseudonym, then when rendering the HTML, break off into the sub-events, render at the end of the dummy "target selector" that is actually the half-finished component HTML, and then get back in to the component to finish off the rest of the html block. Probably would be a good idea to disallow the pause command in there, as I suspect that's asking for trouble, and that goes for any delays, like "after stack". It needs to be a smooth run through on the thread or it will get weird. Need to get some more use cases with loops before starting this.

bob2517 commented 1 year ago

Actually, it should be easier than that to implement. Extract out the {: :} and create an internal mid-way event between beforeComponentOpen and componentOpen. If the render command runs in that event, the result gets stored in the HTML block placeholder prior to render, unless rendering from inside a target selector, as I think we can get additional target selectors in there too. So the top-level render in the HTML block will treat the HTML block as the "event selector". Something like that. No delays allowed though - not even await. Not for first implementation anyway.

bob2517 commented 1 year ago

This is pretty much done offline, just need to test the component scoping, looping, targets and nested components to make sure they work as expected and should be able to get it onto the branch today if all goes well.

Format will be as above, example:

@component hello-world {
    html {
        <div>
        {:
        @if ($okToSayHello) {
            render: "Hello world.";
        }
        body {
            render-before-end: "This text is outside the component.";
        }
        :}
        </div>
    }
}
dragontheory commented 1 year ago

At a glance, this looks easy to read and thus that much less effort to understand.

bob2517 commented 1 year ago

Yes, it's another missing piece of the puzzle I think. A good bonus is the ability to loop an array of strings in there and render the concatenated results - hopefully that will work as expected.

bob2517 commented 1 year ago

Shadow DOM components work, and loops too, example:

@component hello-world shadow {
    html {
        <ul>
            {:
            @for n from 1 to 10 {
                render: "<li>{n} Hello world.</li>";
            }
            :}
        </ul>
    }
}

All render commands are concatenated into one result before finally rendering on the page, which is something that has been missing in the core up to now.

dragontheory commented 1 year ago

Very nice!

bob2517 commented 1 year ago

Yes indeed. Multiple blocks are working too, example:

@component hello-world shadow {
    html {
        <ul>
            {:
            @for n from 1 to 10 {
                render: "<li>{n} Hello world.</li>";
            }
            :}
        </ul>
        <ul>
            {:
            @for n from 1 to 10 {
                render: "<li>{n} Something else.</li>";
            }
            :}
        </ul>
    }
}
bob2517 commented 1 year ago

There's a bug with passing variables into nested components - this doesn't work yet - the attribute variable isn't being recognised inside the nested component:

@component hello-world strictlyPrivate {
    &:beforeComponentOpen {
        $arr: [ 'Bob', 'Geoff', 'Tracey' ];
    }
    html {
        <ul>
        {:
        @each $person in $arr {
            render: "<li><a-person name='{$person}'></a-person></li>";
        }
        :}
        </ul>
    }
}

@component a-person {
    html {
        Hello {@host:name}.
    }
}

I'm going to commit the current state of things to the branch anyway, as the component scoping for events and variables and general nesting seems to be working. Will fix this bug though before release.

bob2517 commented 1 year ago

Fix for attribute references using {@host:... for nested components is now on the branch.

bob2517 commented 1 year ago

Closing pending release.