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

Variables revisited - the final (yeah, right) piece of the puzzle #269

Closed bob2517 closed 1 year ago

bob2517 commented 2 years ago

When I started ACSS, I had no idea it would turn into a full fledged programming language, and it has evolved naturally rather than writing the whole spec first, which would obviously have been the preferable route to take, but almost an impossible thing to achieve at the very beginning of the project. That wasn't the initial goal of the project.

With that in mind I've slowly been disliking more and more the way variables have evolved over time in the core. There are certain things you can't do very easily, like get access to JavaScript prototype functions on variables, and html escaping issues, stuff like that. I've generally worked around them, but those were annoyances nonetheless.

To those familiar only with CSS, these abilities seem way beyond anything that would even be considered in CSS, but after working with ACSS for a few years, you get used to expecting ACSS to be able to do that sort of impossible, and it's a bit annoying when it can't.

Over the last week I've been putting together a new syntax which will work in tandem with the existing variable structure, but with the view of deprecating the older syntax where possible. This will also make variables simpler to handle in the core, which will make my brain a bit calmer. It's a bit like SCSS, but with support for JS syntax rather than trying to design something new, as I don't see the point.

Syntax examples follow:

$myVar: 1;
$myVar++;
$myVar: [];
$myVar[]; 2;         (this adds to the end of an existing array)
$myVar: { key: value };
$myVar: 1;
$myVar: targetSelector;
$myVar: eventSelector;
$myVar: new Date();
$myVar: $anotherVar + new Date() + $someOtherVar.replace(/,/g, '<br />');
etc. basically anything you can do with a var statement in JS.

As you can see, every variable type gets lumped into one syntax. Currently this dollar variable is the "HTML variable" in ACSS, which is the most permissive variable there is, because it allows raw HTML. Worse case scenario HTML variables will have to get immediately deprecated and removed and replaced with this solution in existing code.

Then they will be used like this, anywhere in any command or conditional, inside the existing {= =} syntax:

render: "kljshdkjsdf{= $myStringVar =}sdfkjhsdfkjh";
render: "kljshdkjsdf{= htmlentity($myStringVar) =}sdfkjhsdfkjh";
render: "kljshdkjsdf{= unhtmlentity($myStringVar) =}sdfkjhsdfkjh";
render: "kljshdkjsdf{= $myStringVar.replace(/,/g, '<br>') =}sdfkjhsdfkjh";
...
html {
  <div>{= $a + $b / $c =}</div>
}

It will also make possible the possibility of reactive expressions, with the use of double-dollars, which would be cool:

render: "Changing value: {= $$myNumber + 10 =}";
...
html {
  <div>{= $greeting $$name =}</div>
  <div>{= $a + $b / $$c =}</div>
}

When a double-dollar variable changes, the whole expression inside the {= =} would re-render.

This immediately adds the full power of JavaScript to ACSS variables and should hopefully provide the flexibility that ACSS has been lacking in terms of handling variables. There may be a few helper functions bundled in there too which I'll do, like the escaping and the unescaping of html.

And I think that syntax should be a lot easier to remember. And if you're coming from PHP it will be familiar, as PHP variables all start with a dollar. It's a simpler approach, and one which I think sits better with existing CSS syntax, as it becomes very clear which are programming variables and which are not. You can't mistake the {= =} for anything else.

I'm going to implement this, convert all the docs example to use the new syntax and see what works, what doesn't, and what if anything is a bit annoying about it. it shouldn't be a massive thing to implement. We've already got the {= =} syntax, so it will be a case of slapping in the dollar variables and the double-dollar vars.

This hasn't been an easy thing to decide upon - I've been mulling it over since variables started getting introduced, which was a long time ago, and I think a move like this is long overdue. It could even be a real game-changer in terms of what's possible.

bob2517 commented 2 years ago

This is about a third complete. I'm trying to implement this and keep it backward-compatible rather than destroying what is there already so I can deprecate the older methods. Looking good so far and it definitely feels like the right move, config looks cleaner with what I've done so far.

bob2517 commented 2 years ago

This is now about 75% complete, maybe more than that. It's looking like it's going to be a backward-compatible upgrade at this point, and should work with the existing variable implementation. The variables are easier to spot, and IMO the code is cleaner with this new syntax.

Working syntax examples so far:

$myVar: 1;
$myVar++;
$myVar: [];
$myVar: { key: value };
$myVar: 1;
$myVar: true;
$myVar: "hi there";
$myVar: new Date();
$myVar: $anotherVar + new Date() + $someOtherVar.replace(/,/g, '<br />');
render: "An in-string variable: {$clock}";
render: "A reactive variable: {{$clock}}";
render: "A calculated variable: {= $numA + 'hi there' + $numB =}";
@if $gameState[$r] == "" {
$nextMove: $squaresRemaining[ Math.floor(Math.random() * $squaresRemaining.length) ];

So it's basically $variableName, unless it's being used in double-quotes, in which case it's {$variableName}. And if it's a reactive variable then it's {{$variableName}}. JavaScript expressions go into {= =} in any action command except the var command, where {= =} isn't needed at all.

I think that should cover all the issues that I keep running into, but I'm going to convert the docs site and see if anything else can be added to make it easier to do things.

Still these to do, which are brand new concepts for ACSS:

$myVar[]; 2;         (this adds to the end of an existing array - syntax borrowed from PHP. It's nicer than push and saves having to do a run command.)
$myVar: targetSelector;
$myVar: eventSelector;

Plus any helper functions.

I may or may not do those last bits for the next release. It depend how it goes on converting the docs site. Once it's done, I'll set up a separate page for the new syntax and deprecate the old variable methods for full removal in version 3 of ACSS.

bob2517 commented 2 years ago

The clock example is now officially a thing of beauty:

@component basic-clock {
  &:beforeComponentOpen {
    $clockTime: new Date().toLocaleTimeString() every 1s;
  }
  html {
    Time: {{$clockTime}}
  }
}
<basic-clock></basic-clock>

(Of course, the other methods of components still apply, like HTML in template tags or external files, if HTML inside ACSS feels weird.)

bob2517 commented 2 years ago

This is going really well and I hopefully should have it on the branch by the end of this coming weekend. I've done about 70% of the docs and fixed loads of things, including a few bugs that I didn't know were there.

Helper functions so far are escapeHTML() and unEscapeHTML(), which converts the key parts of HTML strings to their HTML entity equivalent and back again. Like "\<" becomes "\<", etc. These can be used in anything that is a JavaScript expression, including the at-rules:

Eg.

$safeVar: escapeHTML($aStringWithRawHTML);
$htmlVar: unEscapeHTML($aStringWithAlreadyEscapedHTML);

Doing it this way rather than having an automatic way, like it does currently, is weirdly easier to work with, because you have more control over all the scenarios.

I was going to call the functions esc() and unEsc(), but it didn't seem right to have command names that didn't explain what they did, so I plumped with the self-describing ones. html_entities or entityHTML seemed a bit too technical.

bob2517 commented 2 years ago

I think this is about done for the moment. Elements can be assigned to dollar variables now too. All the docs examples are converted, including the code editor. There are other improvements that can be made that I spotted while doing this, but in terms of setting up a better syntax to replace the var command, I think it's there. The main improvement that could be done is getting a syntax for exercising javascript prototype function that are assigned to a variable, like $el.focus(). That can only be done via the run command at the moment, which is fine for now.

Committing in a mo - just need to do a bit of clean-up - there's a gazillion files with commented out console.log calls.

bob2517 commented 1 year ago

This has been battle-tested, with the docs site examples re-done with dollar variables and for other production projects.

Still to do, which would be nice:

/* add to array */
$myArr[]: "something";

The existing var-delete command can be used for now to delete a property, and I don't have a replacement syntax for that yet.

That addition should wrap this issue up for release.

bob2517 commented 1 year ago

Also check/add window.$globalVar: "something"; syntax, as I think that is the last thing hanging up the var command from getting deprecated, but verify this. Also re-check $var.prop: "something"; and $var.prop[$varB]: "something";.

bob2517 commented 1 year ago

Reactive expressions won't be part of this release, but the syntax design is set up to support it later on when there is a use case for it.

bob2517 commented 1 year ago

$myArr[]: "something"; works offline and does the same thing as JS $myArr.push("something");

It should work for session/local storage but needs a test.

Looking at setting up a window.$myVar: "something"; syntax. Should be quick to do.

bob2517 commented 1 year ago

Verified these as working offline in or out of local-storage/session-storage:

$arr[]: "test";
$arr2.dave[]: "test";
$var.prop: "something";
$arr[$dave]: "there";
$dave: { prop: "hi" };
$arr[$dave.dave]: "there";

Just the "window." syntax to add - currently that has to be done via the var command, but this would be handier.

bob2517 commented 1 year ago

Correction: "window.$var: val;" is being added only, for now at least.

bob2517 commented 1 year ago

Now on branch. I think that's it - will review next.

bob2517 commented 1 year ago

Closing in preparation for release.