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

Next release - sequential target selectors, sequential action commands and nested loops #19

Closed bob2517 closed 4 years ago

bob2517 commented 4 years ago

In CSS it doesn't matter which commands are run in which sequence (mostly - the animation method is slightly different and has sequential commands). Active CSS was built on that premise of static declarations. Most of the time you can get away with commands not being sequential, and actually it does run things sequentially up to a point, but you can end up using a lot of "after stacks" to make sure of sequentiality, which can make the code seem a bit hacky.

With that in mind, and in the interests of keeping things simple, the solution is simply to make all target selector and action commands sequential. This decision still means that Active CSS can be incorporated into the browser, which is the point of the project, as all Active CSS declarations are labelled clearly by their event and so thus can be distinguished to have commands to run sequentially.

So the next minor release will introduce sequential target selectors and sequential action commands. It won't break what config works already, but will enable developers to write code in sequence. This will additionally quash any related consideration on Active CSS not being a "proper" programming language because the commands aren't currently sequential, not that anyone has done this yet.

(It's going to seriously break the extension for that particular release and introduce all sorts of issues with editing live config which will be tackled after the core has been handled.)

bob2517 commented 4 years ago

Note this ticket is only for sequential commands, and not for the future "} then {" syntax which should really handle all asynchronous results when used, whether success or failure. So that needs a bit more workout before implementing.

Note also that this ticket adds nothing to the syntax or the language itself, and is solely designed to reduce complexity when actually developing in Active CSS.

bob2517 commented 4 years ago

Example syntax it would need to parse in sequence for consideration in tackling - this isn't valid code, but it needs to be able to be parsed and iterated in sequence:

#feedbackForm:submit {
    add-class: .cheese;
    ajax-form-submit: post json;
    add-class: .potato;  // these duplicate add-class commands must not be grouped.
    #feedbackErr {
        add-class: .hide;
    }
    #feedbackErr {    // these duplicate target selectors must not be grouped.
        add-class: .hide;
    }
    @each field in errField {
        #{field} {
            add-class: .erredField;
        }
        console-log: "test";
        .erredField {
            remove-class: .erredField;
            console-log: "hello";
        }
        console-log: "hi";
    }
    var: success null, erred null;
    @each field in errField2 {
        console-log: "wazzup";
    }
    #fbSubmit {
        set-attribute: value "Sending...";
        set-property: disabled true;
    }
    console-log: "This is the end.";
}

Note: The automatic grouping of target selectors and action commands will be removed in the config parsing, but the ability to handle grouped target selectors and action commands in the config as written will be handled as usual, so there is no loss in functionality.

bob2517 commented 4 years ago

Note to self: currently the config target selectors are running alphabetically.

bob2517 commented 4 years ago

So the target is to have a fully parsed config which is in the config array in the correct order prior to the event loop. No sorting is needed in the parsing, as the lines are handling one at a time in sequence already. Also, no sorting is allowed in the event loop, as this would affect performance slightly, so its just the final positions in the parsing and the resultant config array that need establishing during the making of the config array so that they are in the correct order. Then once this has been established, all areas of the code where the config array is referenced need adjusting to handle any new indexing elements in the array.

bob2517 commented 4 years ago

The correct sequence to attack is first the initial parser, and secondly the assignment to the config array. The config array needs to have the top-level actions and the target selectors ordered correctly in the parsing array before analysing for the config array can commence.

bob2517 commented 4 years ago

As this will require revisiting the parsing and config setup, there's a nagging thought to consider also the config array structure needed for nested loops to save a bit of time and see if we can save two birds with one bowl of bird food. So gonna mull this over a bit more.

bob2517 commented 4 years ago

1) The nested loops will need to handled in a recursive function calling stylee to drill further into the array sort of fashion, otherwise it will all get weird. This will mean that the config will no longer will work as a fixed element length array, so the array usage needs to be reviewed to see if that's going to be ok to do. Should be fine - just need to estimate the amount of work. Check the extension too. It does raise massive implications on the UI for the extension, but there's not much we can do about that. Nested loop syntax, for the existing @each and the future @for loop, and anything else like this, needs to be able to done with a nested syntax. The extension has to work with the core - not the other way around. This is going to delay the release of the extension in terms of coding weeks or possibly even longer depending on how much new UI functionality is needed to go along with the upgrade. 2) The index counter for the ordering needs to work on 2 levels: a) the target selector level (which resets for each new event or loop), and b) the action command level (which I think is already indexed and should resolve once the automatic grouping of the same action command in the same target selector is removed, but need to confim this). This can be done independently of 1 so may create a new issue for 1 and just do 2 this time around.

bob2517 commented 4 years ago

Note: neither of these upgrades will degrade performance in any way. The parsing shouldn't take any longer, and in fact for the runtime itself there might possibly be a notable speed increase in some scenarios. This is because "after stack" forces the core out of the event loop each time with a settimeout. With sequential commands, the event loop can just keep looping.

bob2517 commented 4 years ago

One other point on this worth mentioning. Sequential commands will by default allow async actions to happen in the background, so they will effectively be initialised and departed from in the command flow, to have the usual "after" event happen as it does currently. This is the current default behaviour, and it doesn't need to change.

There was a thought of a possible idea to make this nicer for the developer, and that would be to add "sync" parameters to async commands to force the sequential queue to wait until the async command is successful before continuing the command flow. Sounds nice in theory, but not sure it's worth doing though. Failed async actions need to be able to be handled regardless, so a simple "sync" parameter may not cut the cheese on a syntax level. Could just keep the failed "after" events in reserve and have async "sync" commands only continue the flow if there is a success. It would be nicer to code with, slightly. If a "sync" parameter was added to commands though, it would need to be done at a generic core level anyway, and also ideally so that other people could use the parameter in their custom commands. May be over-ambitious though. The "after" events as they stand are quite an elegant solution on a syntax level, but don't handle multiple async instances using the same command. It's the same mess for any language though, handling chained async callbacks. Maybe labels used with failed after events is the solution? Dunno yet - these are just thoughts. The "sync" command does seem nice if there was only the success event to deal with though - variables could be auto-generated from ajax results and used in the next line, which would be nice. Also, being able to optionally wait until an delay on an action command has happened before moving onto the next command would be a nice touch.

bob2517 commented 4 years ago

Target selectors and action commands now look like they should be working in sequence in the core, from looking at the final config array after initialisation. Needs more testing. But aside from that, the looping structure needs to be revisited in order for the loops to work in sequence along with the target selectors. Currently all the loops get handled at a separate time to the non-looped target selectors, which is pants. Dunno what I was thinking. I was probably thinking that it didn't matter, because it actually didn't matter at the time.

So I think the most sensible thing to do at this point is to revisit the looping, get it working sensibly so that they can be nested at the same time, as that's the major unknown quantity about the whole thing, and design it all so that everything can be sequential.

A good start though. If there wasn't an @each command already, that would have been a wrap.

There's already a significant performance increase on startup, as each action command no longer needs to be grouped up, so that code ending up working all by itself by removing the grouping section of the config array setup. The action commands were already number indexed.

bob2517 commented 4 years ago

Back on this - had to leave it alone a do something else for a few days. Afterajax commands don't seem to be working correctly - fixing this so core is stable then onto the other stuff. There's an issue with iterating items with the same primSel and event, so the change from grouping isn't fully sorted out. Should be just a case of adjusting something in the event loop, as the config looks ok.

bob2517 commented 4 years ago

It's only running the first item in each declaration. Doh. Need another iteration loop.

bob2517 commented 4 years ago

Fixed offline. Now gonna get nested loops working. This should be a bit more interesting. First up, get the config array nesting recursively before the event loop starts.

bob2517 commented 4 years ago

Almost there - config looks good, just wrapping up the event loop.

bob2517 commented 4 years ago

I'm on the final stretch now. Onto the innards of the each loop. Event loop handling is complete - just handling the actual variable usage within the loops themselves, which is the fun bit. There's going to be loops allowed around action commands too, although I haven't exactly found a use for that yet - need to do an example. Gonna focus on getting it working tomorrow with minimal distractions and hopefully have it wrapped then. Then the plan is to update the docs on Wednesday and do a release. The good thing with this release is that there are no additional syntax changes to the language at all, which is cool. Just performance improvements on start-up and potential optimizations (juggling action commands around and removing "after stack") if the developer wants to even go there.

bob2517 commented 4 years ago

Mostly working now - on final tests to make sure it works in all expected places.

Here is the example of sequential target selectors, action commands and nested loops that I'm working on:

HTML:

<button id="showVillage">Show people</button>
<p id="myVillage"></p>

Active CSS:

#showVillage:click {
    var: villagePeople {
        Dave: ['Awesome beard', 'Great brain', 'Great teeth'],
        Jane: ['Awesome hat', 'Great nose', 'Great ear'],
        Fred: ['Awesome nose', 'Great big hat', 'Great Scott!']
    };
    set-property: disabled true;
    background-color: lightgray;
    render-after-end: "<p>Below we have a list of some people who live in the village.<br>";
    @each person in villagePeople {
        #showVillage + p {        /* (this refers to the p tag just added above) */
            render-before-end: "We got {person}<br>";
        }
    }
    #showVillage + p {
        render-before-end: "</p><p>That will do...</p>";
    }
    #myVillage {
        render: "Ok. So about these people.<br>";
    }
    @each person, properties in villagePeople {
        #myVillage {
            render-before-end: "{person}:<br>";
        }
        @each characteristic in properties {
            #myVillage {
                render-before-end: "{characteristic}<br>";
            }
        }
    }
}

Currently produces:

Below we have a list of some people who live in the village.
We got Dave
We got Jane
We got Fred

That will do...

Ok. So about these people.
Dave:
Awesome beard
Great brain
Great teeth
Jane:
Awesome hat
Great nose
Great ear
Fred:
Awesome nose
Great big hat
Great Scott!
bob2517 commented 4 years ago

Note to self: Data-binding for loops is going to have to work differently than the existing method. Data-binding on the same variables should already work as it stands, but really the loop should completely re-render if the number of elements change, and only the variable content at that. This was a separate item on the roadmap and won't be covered with this release.

bob2517 commented 4 years ago

Loop not working in components for some reason. Need to do more tests before publishing. [Edit] was a typo thankfully, rather than a logic error. But I've found another bug to do with rendering @each looped components in components with events in the sub-component, so will fix that before continuing on this ticket. Docs will need a completely new version now that we don't need after stack so much, so will be splitting up the docs site into different versions before publishing which may take a few days, depending on work and other distractions. [Edit] The above "event in component" bug is now fixed and was kinda related to the @each loop as that highlighted some clean-up action that was occurring to prevent multiple event-scoped component renders in the same host, but this is only relevant for shadow DOMs to prevent multiple shadow DOMs with the same host, so the fix was just to limit it to shadow DOMs.

bob2517 commented 4 years ago

All known bugs to do with this upgrade or otherwise are now fixed. Might do some more tests before publishing, but it's looking good so far.

Next, gonna work on the website docs versioning system before publishing, as this is the first version that changes how the language can be addressed as a developer - with sequential commands. The latest release technically doesn't actually have to change the developer's viewpoint of how to program in Active CSS, ie. Active CSS can still be viewed upon as a static language and it will work the same. Strange but true. But this upgrade does shift the potential somewhat.

So the current 2.2.4 website will be accessible via a different URL as specified on the core download page, and the main site will default to 2.3 orientation, where the orientation will be on Active CSS as a sequential language. Searching on the docs page will be limited to the version being browsed.

bob2517 commented 4 years ago

Note: There is a visible performance improvement when "after stack" is removed and the action command is placed at the end of the flow instead. So that's cool. 👍

bob2517 commented 4 years ago

I've changed my mind on the docs versioning system. No point getting confusing about it - I'm just going to amend the existing docs and examples. If have people already used "after stack" a lot, they will still be able to find it in the docs. Everything will still be there - there aren't any syntax changes anyway. So will hopefully get 2.3.0 published some point tomorrow or Tuesday.

bob2517 commented 4 years ago

Live in 2.3.0. Closing.