squint

Brandon Stubbs 2024-05-24T14:16:46.812029Z

Question on html tag 🧵

borkdude 2024-05-27T09:22:21.422139Z

@kuzmin_m I added support for :& now (also documented). Bonus: the library also works in ClojureScript now

👍 1
borkdude 2024-05-27T09:23:38.195429Z

as for html5: I guess we could default to html5 and make up a way to alter the behavior globally or so.

borkdude 2024-05-27T14:39:37.380429Z

@kuzmin_m what do you mean with hourglass?

kuzmin_m 2024-05-27T14:58:23.819069Z

In my company we mark saved for later messages with UPD: https://github.com/borkdude/html/issues/4

borkdude 2024-05-27T14:59:53.405139Z

👍

Brandon Stubbs 2024-05-24T14:17:56.070959Z

What do you think about having the ability to merge props?

npx squint-cljs -e '(println #html [:h1 {:id "heading"} "Hello"])'
;; => <h1 id="heading">Hello</h1>
npx squint-cljs -e '(println #html [:h1 (merge {:id "heading"} {:class "h1"}) "Hello"])'
;; => <h1>[object Object]Hello</h1>

borkdude 2024-05-24T14:40:09.399069Z

With JSX that works like this:

$ npx squint-cljs -e '(println (let [m {:id :heading}] #jsx [:h1 {:& m :class "h1"}"Hello"]))'
file:///private/tmp/sq/.tmp2PrN7M/squint.mjs:4
return <h1 {...m1} class="h1">Hello</h1>;

borkdude 2024-05-24T14:40:32.635769Z

Obviously that doesn't work with html:

$ npx squint-cljs -e '(println (let [m {:id :heading}] #html [:h1 {:& m :class "h1"}"Hello"]))'
<h1 {...m1} class="h1">Hello</h1>

borkdude 2024-05-24T14:40:44.052679Z

since HTML isn't dynamic

borkdude 2024-05-24T14:41:25.299339Z

but since there is a dynamic tag function involved, perhaps it's possible to hack something like that

borkdude 2024-05-24T14:45:15.526459Z

Perhaps given

return squint_html.tag`<h1 {...m1} class="h1">${squint_core.str(m1)}</h1>`;
squint_html.tag could do a string interpolation here on the keys and vals of the map at runtime and replace that

Brandon Stubbs 2024-05-24T14:49:00.272889Z

Yeah maybe squint shouldn’t manage it then. I am just trying to get as far as I can without writing my own vdom. I do plan to write a vdom for squint at some point

borkdude 2024-05-24T14:49:27.568259Z

> Yeah maybe squint shouldn’t manage it then It could, given the above idea

borkdude 2024-05-24T14:49:58.025919Z

Feel free to post an issue, I'm now on the other thing which I am now being distracted from :)

Brandon Stubbs 2024-05-24T14:50:09.161069Z

Ok great 🙂

borkdude 2024-05-24T15:14:27.128529Z

I think I got it already:

$ ./node_cli.js --show -e '(println (let [m {:id :heading}] #html [:h1 {:& m :class "h1"} (:id m)]))'
import * as squint_core from 'squint-cljs/core.js';
import * as squint_html from 'squint-cljs/src/squint/html.js';
squint_core.println((() => {
const m1 = ({ "id": "heading" });
return squint_html.tag`<h1 ${m1} class="h1">${squint_core.get(m1, "id")}</h1>`;
})());

<h1 id="heading" class="h1">heading</h1>

borkdude 2024-05-24T15:14:39.115499Z

I can commit this to master for you to try it out

borkdude 2024-05-24T15:16:01.515969Z

pushed

👀 1
Brandon Stubbs 2024-05-24T15:16:40.740149Z

Oh wow thanks, was just grabbing a will try in a sec

Brandon Stubbs 2024-05-24T15:29:14.824409Z

Yeah working great!

./node_cli.js --show -e '(defn my-button [props] #html [:button {:id "button" :& props}]) (println (my-button {:class "danger"}))'

Brandon Stubbs 2024-05-24T15:31:10.622689Z

Yeah edge on nil props

./node_cli.js --show -e '(defn my-button [props] #html [:button {:id "button" :& props}]) (println (my-button) (my-button {:class "danger"}))'

borkdude 2024-05-24T15:31:35.868359Z

please make issue, gotta go afk for a bit

👍 1
borkdude 2024-05-24T15:59:16.332299Z

why would you write a literal nil with :& though?

Brandon Stubbs 2024-05-24T16:03:00.253779Z

Yeah sorry more the runtime nil

Brandon Stubbs 2024-05-24T16:13:17.776679Z

I would have expected the latter to work in terms of following the rules of spread in JS:

let x = {a: 2};
{a: 1, ...x}; // {a: 2}
{...x, a: 1}; // {a: 1}

borkdude 2024-05-24T16:14:08.512229Z

I pushed some more improvements for rendering css now as well, I'll take a look at your latest comment

borkdude 2024-05-24T16:16:00.960389Z

ah yes, the order, this sucks because CLJS maps are unordered. There's a workaround for that, yeah, this is a problem

borkdude 2024-05-24T16:17:37.054539Z

o wait, it hasn't got to do with that even

borkdude 2024-05-24T16:17:45.727579Z

The rendered HTML is:

<a style="color: red" href="" style="color: green">Clojure rules</a>

borkdude 2024-05-24T16:17:56.757669Z

I guess this is just a browser problem rather than a squint problem

Brandon Stubbs 2024-05-24T16:34:31.345769Z

I wonder if :& attributes should always overwrite the other attributes in squints case? As having two style attributes doesn’t really make sense. The use case would be do have the defaults setup and then use the spread to overwrite the defaults

borkdude 2024-05-24T17:01:37.969009Z

Yes we know the keys statically so we could pass those to the attr function

borkdude 2024-05-24T17:01:46.990149Z

I’ll have a look after dinner

borkdude 2024-05-24T17:36:03.851349Z

so in squint's case:

{:a 1 :& x}
and {:& x :a 1} would do the same?

Brandon Stubbs 2024-05-24T17:46:57.031749Z

Yeah I think that would be the most common thing people would want

borkdude 2024-05-24T17:55:52.834819Z

$ ./node_cli.js --show -e '(defn my-button [props] #html [:button {:id "button" :& props}]) (println (my-button {:id 2}))'
import * as squint_core from 'squint-cljs/core.js';
import * as squint_html from 'squint-cljs/src/squint/html.js';
var my_button = function (props) {
return squint_html.tag`<button id="button" ${squint_html.attrs(props,new Set(["id"]))}></button>`;
};
squint_core.println(my_button(({ "id": 2 })));

export { my_button }

<button id="button" ></button>

borkdude 2024-05-24T17:56:47.875269Z

pushed

borkdude 2024-05-24T17:58:10.395249Z

This also works for choosing the prop from the map instead:

(defn my-button [props] #html [:button {:id (:id props "button") :& props}])

borkdude 2024-05-24T18:10:55.567709Z

published 0.7.109 with these changes

borkdude 2024-05-24T18:12:24.297379Z

I guess I'll have a look at those vite plugin things later next week hopefully :) This was also fun though

Brandon Stubbs 2024-05-24T18:30:34.004299Z

In the above example, both end up with the green styling

borkdude 2024-05-24T18:32:26.366859Z

yes, as discussed

borkdude 2024-05-24T18:32:33.810409Z

the explicit property always overrides

borkdude 2024-05-24T18:32:44.587489Z

you replied with "this is the most common use case"

Brandon Stubbs 2024-05-24T18:33:28.993119Z

Ah sorry, I meant as in the outcomes would be the same, that the spread is always treated as spread onto, (merge m :&)

borkdude 2024-05-24T18:35:24.643689Z

I don't understand. I asked here if the position doesn't matter:

{:& x :a 1} imo means that :a 1 always wins, right

borkdude 2024-05-24T18:35:33.697239Z

but you preferred the other one?

borkdude 2024-05-24T18:35:42.071209Z

as the only behavior?

Brandon Stubbs 2024-05-24T18:37:06.555039Z

Yeah sorry I thought:

{:a 1 :& x} // x keys would override 
{:& x :a 1} // x keys would override

borkdude 2024-05-24T18:37:25.979329Z

I see ok

borkdude 2024-05-24T18:37:41.825099Z

we can do that

Brandon Stubbs 2024-05-24T18:38:10.508779Z

Sorry about that, I misunderstood

borkdude 2024-05-24T18:38:39.815879Z

Yeah, I guess I misunderstood too :) I think this requires a little more work and I'm done for today, if you can make an issue I'll fix that next time

👍 1
Brandon Stubbs 2024-05-24T18:48:05.931139Z

You’re too fast 😂

borkdude 2024-05-24T18:48:13.640939Z

ah what the heck, already on npm ;)

Brandon Stubbs 2024-05-24T18:48:37.643249Z

Definitely owe you a few 🍻

❤️ 1
borkdude 2024-05-24T20:09:00.870859Z

yeah I guess this behaves the same as JSX now, pretty sweet

Brandon Stubbs 2024-05-24T21:45:03.775409Z

Yeah it’s really awesome!

kuzmin_m 2024-05-26T14:48:58.346109Z

Will it be possible to use #html in JVM Clojure?

borkdude 2024-05-26T14:50:48.782119Z

Why not, have you tried it? It might already work?

borkdude 2024-05-26T14:57:33.395969Z

I assume that your question means: does JS produced with squint on the JVM also support #html: I wouldn't see why not

borkdude 2024-05-26T14:58:31.125479Z

$ clj
Clojure 1.12.0-alpha7
user=> (require '[squint.compiler :as sc])
nil
user=> (sc/compile-string "#html [:a {:color :blue}]")
"import * as squint_core from 'squint-cljs/core.js';\n`<a color=\"blue\"></a>`;\n"

kuzmin_m 2024-05-26T15:01:17.081509Z

No. I expect it produces a string like hiccup.

borkdude 2024-05-26T15:02:10.393639Z

it does produce a string (inside of the JavaScript)

kuzmin_m 2024-05-26T15:02:39.139789Z

Without js 🙂

borkdude 2024-05-26T15:02:56.903659Z

why not just use hiccup then

kuzmin_m 2024-05-26T15:03:44.681199Z

Security, supportability and other things

borkdude 2024-05-26T15:06:12.373859Z

why would hiccup be more unsafe, unsupportable and other things than squint's #html?

kuzmin_m 2024-05-26T15:13:06.242019Z

Hiccup is in alpha stages for years. It has still inconsistent API: helpers use unsafe hiccup-v1. #html is very simple, it doesn’t do some magic with for and if macros. And I think it could be a standard way to render html in different platforms.

borkdude 2024-05-26T15:16:45.207049Z

what magic with for and if are you referring to?

kuzmin_m 2024-05-26T15:17:57.672609Z

> If the body of the element is a seq, its contents will be expanded out into the element body. This makes working with forms like map and for more convenient: > > user=> (str (h/html [:ul > (for [x (range 1 4)] > [:li x])])) > "<ul><li>1</li><li>2</li><li>3</li></ul>"

borkdude 2024-05-26T15:19:51.498559Z

lazy sequences (and arrays) are also handled the same way in squint #html: https://x.com/borkdude/status/1794349007076241663/photo/1

borkdude 2024-05-26T15:23:10.331269Z

what hiccup tries to do there is optimize stuff it can do at compile time so it can skip work at run time

kuzmin_m 2024-05-26T15:24:13.238929Z

In Squint we should add #html inside for, if, when, let etc. And it is right. I wonder about simple html/xml rendering in jvm clojure, and #html could be the best way to do it

borkdude 2024-05-26T15:25:04.957769Z

ah you mean you should repeat the #html ok

borkdude 2024-05-26T15:25:59.329659Z

you mean, like this right?

(h/html [:ul (for [i [1 2 3]] (h/html [:li i]))])

kuzmin_m 2024-05-26T15:26:30.515579Z

#html instead of h/html

borkdude 2024-05-26T15:26:47.692439Z

yeah but would you have preferred that hiccup worked like that?

kuzmin_m 2024-05-26T15:29:12.426889Z

I’d prefer don’t use hiccup 🙂 And use #html like I could in squint/cherry

borkdude 2024-05-26T15:41:42.119319Z

I'll have a look at what's possible

👍 1
🚀 1
1
borkdude 2024-05-26T16:02:57.473309Z

ok, try this: https://github.com/borkdude/html experimental, but with your feedback it can improve

kuzmin_m 2024-05-26T16:22:28.762069Z

You are very fast! 🙂 1. Is it possible to use #html instead of h/html? 2. Add escaping and raw strings 3. #html [:div {:& props}] Also there is the most confusing part about HTML5 vs *X*HTML5. HTML5 uses https://developer.mozilla.org/en-US/docs/Glossary/Void_element. But it’s seems that XHTML5 does not have void elements. So using XHTML5 we will use simpler rules. Could you also add a note about doctype and HTML5 vs XHTML5?

borkdude 2024-05-26T16:25:02.530199Z

escape strings: yes of course, PR welcome, I'm afk for dinner now

👍 1
borkdude 2024-05-26T16:25:09.165349Z

posting issues also welcome

Brandon Stubbs 2024-05-25T12:34:35.104489Z

metal