Fork me on GitHub
#clojurescript
<
2022-09-12
>
Sturm03:09:07

Hi, how would I use wired-button in CLJS? https://www.npmjs.com/package/wired-elements

Sturm03:09:55

import { WiredButton, WiredInput } from "wired-elements"
<wired-button>Click Me</wired-button>

Sturm03:09:37

I've tried (:refer ["wired-elements" :refer (WiredButton)]) but I'm not clear how I get a reference to wired-button

Sturm03:09:56

The http://npmjs.com link above mentions "web components". Could that be the issues here?

thheller05:09:24

@ben735 you don't need a reference to wired-button, its just a web component. meaning it becomes a regular DOM element. just as you would create a div you create a wired-button

thheller05:09:41

so in case of reagent/re-frame [:wired-button ...]

Sturm05:09:14

oooohhh thanks @thheller - I'll give that a shot!

Sturm06:09:27

brilliant, thanks @thheller!

👍 1
puchacz19:09:11

hi, I am very new to this ecosystem, so I may have missed something obvious. I am trying to set up shadow-cljs server and use cider-connect-cljs to start experimenting. I can get REPL backed by a chrome tab, but the source file has status cider[not connected] and I cannot use M-. or C-z from it. I tried to jack-in-cljs instead (with shadow server down, to let emacs start it), and the effect is the same - I have REPL but source buffer is not "connected". I tried sesman-start first and then the usual cider-connect-cljs, but still no joy. I think the issue is similar to https://github.com/clojure-emacs/cider/issues/2939 I am also quite certain I had M-. working in sources when I was playing with figwheel-main a week ago.

Ben Lieberman21:09:31

For whatever it's worth, I had issues with CLJS tooling from emacs myself that was quite frustrating and I moved to VSCode and have since had no problems at all. Mine originated with figwheel and not shadow-cljs but in both cases they've been good from vscode

puchacz07:09:20

as a Common Lisp guy, I'd rather stay with emacs... I don't want too many tools.

valtteri16:09:18

#cider channel might be a good place to ask.

Fredrik Andersson20:09:38

does anyone have a good solution to find out changes between two vectors with objects? i would preferably need deleted, changed, added

Fredrik Andersson20:09:19

the objects is arbitrarily complex and might include arrays

p-himik20:09:40

By "objects" you mean plain JS objects? If so, there surely is a solution somewhere on NPM.

p-himik21:09:21

Those aren't suitable for JS objects though, AFAIK.

Fredrik Andersson22:09:49

I ended up using clojure.data/diff and simplified my use case

1
👍 1
Chase22:09:47

I randomly saw this small three.js demo on Twitter (https://twitter.com/KennethCassel/status/1569398632830435330) and decided to try out my clojurescript interop skills and see if I could quickly recreate it. I came up with this (https://gist.github.com/Chase-Lambert/9f4055ce8efcb7a1ae406cf1a8b5031d) and everything seems to be in working order (no shadow-cljs errors in the console at least) but nothing is happening in my browser. What else should I do? Any other interop advice?

Chase22:09:57

Looking at the three.js github repo I'm noticing the example they give there is an even simpler rotating cube. The above example was written by gpt-3 as an interesting tidbit. I'll try the repo example and see if I get anything.

p-himik22:09:18

In the JS case, animate is a plain function. In the CLJS case, animate is a higher order function that returns another function.

Chase22:09:06

Ok, I took out the (fn [] part from my animate function and a whole bunch of errors cascade down the console, including some infinite loop action lol.

Chase22:09:53

it's a Uncaught TypeError: Cannot set properties of undefined (setting 'x') error.

Chase22:09:25

If I take out the cube rotation stuff I get the same error but for (reading 'render') so I am definitely doing something fundamentally wrong here. Let me try the simpler repo example.

p-himik22:09:18

Yeah, trying a simpler case makes sense. I just took a very superficial look at those two code samples.

Chase22:09:53

This is the part that confuses me from the repo (https://github.com/mrdoob/three.js/) example:

renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );

// animation

function animation( time ) {

	mesh.rotation.x = time / 2000;
	mesh.rotation.y = time / 1000;

	renderer.render( scene, camera );

}
It has me call that animation function with no parameters but that function seems to take a time parameter.

Chase22:09:42

how do I even translate that top line to cljs? (.setAnimationLoop renderer #(animation))? Or without the #?

p-himik22:09:20

The latter, you just pass the function itself - by name or by value. But in CLJS, you gotta define that function before you pass it somewhere. Not sure whether just declaring it would do the trick - probably won't given how it's not a real name but rather a field within an NS object.

Chase22:09:48

And what would you advise about that whole time thing?

p-himik22:09:09

Hmm? It's just an argument to the function, no?

Chase22:09:09

maybe time is a field within the renderer object? Because I have to pass that renderer object to the animation function anyways so I can call that render method. I'll try that

p-himik22:09:28

(defn animation [time]
  ...)

(.setAnimationLoop renderer animation)

p-himik22:09:12

animation is a closure over renderer since you create renderer before (defn animation ...).

Chase22:09:22

Hmmm, ok. But that animation function needs to also see scene and camera objects I create earlier. It's all so imperative. It's frustrating getting this translated.

p-himik23:09:00

Heh, yeh. But sometimes it helps to use higher order functions that you can to create closures dynamically. Not sure whether it's worth it in this case, but you can experiment with that.

Chase23:09:30

Trying it as just a straight imperative translation gives me an: `three.js:19559 WARNING: Too many active WebGL contexts. Oldest context will be lost. three.js:19850 THREE.WebGLRenderer: Context Lost.` warning.

Chase23:09:23

Not being able to translate 16 simple lines of JS to CLJS does not bode well for my hoped for future as a full stack Clojure developer. lol! Interop was supposed to be seemless. haha

p-himik23:09:54

That warning sounds like an artifact of hot code reloading. Does it work? Are the warnings still there if you just refresh the page?

Chase23:09:38

I had a cube for a second and then made some changes and it's gone. Then my undo didn't work correctly and I can't get back to the old state. Can't figure out why. Here is the full code of the example:

import * as THREE from 'three';

// init

const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;

const scene = new THREE.Scene();

const geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
const material = new THREE.MeshNormalMaterial();

const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animation );
document.body.appendChild( renderer.domElement );

// animation

function animation( time ) {

	mesh.rotation.x = time / 2000;
	mesh.rotation.y = time / 1000;

	renderer.render( scene, camera );

}
Sorry to be taking up so much time, but how would you change my code to match that better: https://gist.github.com/Chase-Lambert/9f4055ce8efcb7a1ae406cf1a8b5031d

Chase23:09:00

I don't know what to return from my cube function now.

p-himik23:09:43

> and then made some changes and it's gone This means that the code is not hot-code-reloading-friendly. No idea how to make it friendly though - depends on the three.js library and might even be impossible.

p-himik23:09:25

Not sure what you mean by "what to return". It's perfectly fine not to return anything.

p-himik23:09:11

(.setAnimationLoop renderer (animation renderer mesh scene camera)) Here, you're calling the animation function and pass its result to .setAnimationLoop. In JS, the animation function itself is passed to that method - because that method calls it.

Chase23:09:32

Ahh ok. So I'll just pass in (.setAnimationLoop renderer animation) like you told me before. And then maybe at the end of cube (thanks for clarifying I don't need to return anything) I was thinking of doing the whole (animation renderer mesh scene camera) because I don't know how or when else to feed all those objects to the animation function.

Chase23:09:27

With that though I'm getting another uncaught typeerror, this time on "rotation" in my animation function.

p-himik23:09:10

In the JS code, the animation function accepts just one argument. In your variant, it accepts many.

p-himik23:09:48

That's what I meant by dynamically creating closures:

(defn make-animation-fn [renderer mesh scene camera]
  (fn animation [time]
    ...))

(.setAnimationLoop renderer (make-animation-fn renderer mesh scene camera))

Chase00:09:37

that worked!

👍 1
Chase00:09:01

interesting solution. I did not grasp what you had meant when you originally said "dynamically creating closures"

Chase00:09:44

I'm still not understanding how animation knows what time is. I don't know what time is. lol

p-himik00:09:29

It is passed as a parameter.

p-himik00:09:00

Something inside tree.js calls it as (animation current-time).

Chase00:09:55

Ok. Yeah, I'll admit I hadn't actually dug into the three.js docs yet. I didn't anticipate the short example kicking my butt so hard. Thanks again for your patience and help.

p-himik00:09:58

No problem. :) I don't know tree.js either. I'm just judging by that function animation( time ) { ... }

skylize02:09:10

Just looking at this one bit of your question:

renderer.setAnimationLoop( animation );
...
function animation( time ) { ... }
> It has me call that animation function with no parameters but that function seems to take a time parameter. This code does not "call" animation. It only passes a reference to that function into renderer.setAnimationLoop. Presumably this will be called repeatedly from within the loop, passing in time each time it does. Hope that might help some.

Chase02:09:42

Yes, thanks for that clarification. It took a while for my brain to wrap itself around what was happening there. I got frustrated but in hindsight the interop really was quite straightforward.

skylize02:09:43

And without looking into three.js code myself, I would predict that it uses requestAnimationFrame under the hood, which on each "frame" provides a DOMHighResTimeStamp, which it will then pass along to your animation function as time. https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

Chase02:09:26

I'll check this out, I love the MDN docs. Another thing I need to go review again is how to inspect these objects in the developer console. I was getting minimal error support or anything telling me where I was going wrong.

Ben Lieberman23:09:00

CLJS classpath Q: shadow-cljs is failing to build my app bc it says my core.clj file was found on the classpath but the namespace of that file cannot be found? I think I might've goofed with a require or something and don't know how to undo it. builds were working fine previously

p-himik23:09:09

What is the exact error?

Ben Lieberman23:09:21

The required namespace "traintime.core" is not available.
; "traintime/core.clj" was found on the classpath. Should this be a .cljs file?

Ben Lieberman23:09:08

I just found that namespace in manifest.edn is it ok to take it out? bc it's not in classpath.edn where I expected it might be from that message

p-himik23:09:05

What about this part of the error? Should this be a .cljs file?

p-himik23:09:29

Those .edn files are supposed to be autogenerated - no need to edit them by hand.

Ben Lieberman23:09:22

It shouldn't be. traintime.core is the namespace of one of my .clj files but its for backend stuff. but I don't think I can use Clojure namespaces in CLJS files right? and I did require said namespace bc I forgot about said (possible) restriction

p-himik23:09:28

> but I don't think I can use Clojure namespaces in CLJS files right? Right. You can, but only for macros. And that has to be handled in a somewhat special way.

Ben Lieberman23:09:24

Would that at least go some way toward explaining this error? And if so, how could I undo it?

p-himik23:09:10

Ehm, it could. And no clue without knowing what you're doing exactly. Seems like you're requiring traintime.core from a CLJS namespace, but traintime.core is a CLJ namespace. So just stop requiring it if you don't need it. Or make it into a .cljc file if you need that functionality to be available in both CLJ and CLJS contexts. Or require it as a macro namespace if you need just a macro from it - but better yet, read how to create .cljs and .clj files together for macros so that requiring macros becomes easier.

Ben Lieberman23:09:05

my shadow-cljs.edn had the incorrect namespace for the :init-fn key facepalm

p-himik23:09:55

Heheh, happens.