Fork me on GitHub
#clojurescript
<
2020-02-07
>
metehan13:02:22

Hi I have a JS interlop problem I am trying to render a chart with apexcharts

metehan13:02:12

(defn history []
  (let [data {:chart {:type "bar"}
              :series
              [{:name "sales"
                :data [30 40 45 50 49 60 70 91 125]}]
              :xaxis
              {:categories
               [1991
                1992
                1993
                1994
                1995
                1996
                1997
                1998
                1999]}}
        ]
    [:div [:div#chart {:style {:border "red solid thin"}}]
     [:button {:on-click 
               (fn []
                 (let [chart (charts. (.getElementById 
                                       js/document 
                                       "chart")
                                      data)]
                   (.render chart)))}
      
      "Render chart"]
     ]
    ))

metehan13:02:24

this is the error i get

p-himik14:02:06

Your data is a CLJS data structure. The library probably expects a JS data structure.

metehan14:02:24

https://twitch.tv/elixirBeam could you check me for a second @p-himik if you have 1 minute

p-himik14:02:14

Seems like you got it working?

metehan14:02:39

yes but i have a problem

metehan14:02:00

normally this chart must run when i load this element

metehan14:02:18

but since i need to select element with #id

metehan14:02:28

i have attached this to cutton

metehan14:02:06

how can i run it when history element loads

p-himik14:02:26

This particular example is exactly what you're looking for: https://github.com/reagent-project/reagent-cookbook/tree/master/recipes/highcharts I suggest browsing through reagent-cookbook repo for useful ideas in general.

metehan14:02:53

ah thank you very much 🍺

metehan14:02:49

works like a charm

thheller14:02:41

:ref would be the more "modern" version

4
metehan14:02:35

now i'll try :ref method thank you . what is the main advantage of using ref

thheller14:02:44

> Integrating with third-party DOM libraries.

thheller14:02:55

with a ref you get direct access to the dom node. so you don't need to query the dom to get it via an id

p-himik14:02:46

The cookbook uses reagent/dom-node, but it's discouraged and deprecated, as stated in the docs linked by thheller.

metehan14:02:45

in cookbook i couldn't find sample for :ref usage, i tried like this but didn't work

(defn history []
  (let [data (clj->js {:chart {:type "bar"}
                    :series
                    [{:name "sales"
                      :data [30 40 45 50 49 60]}]
                    :xaxis
                    {:categories
                     [1991
                      1992
                      1993
                      1994
                      1995
                      1996]}})]
    [:div [:div {:ref (fn [elm] (charts. elm data))}]]))

p-himik14:02:16

:ref just lets you get the DOM node without relying on its ID. Otherwise, it's the same as ID. Meaning, you have to do all the work at some other place. The link provided by thheller has more information.

metehan14:02:32

so i should still use component-did-mount right

thheller14:02:43

@m373h4n in the ref example you posted you are missing the (.render chart) call you had in the other example. I guess it would work if you add that?

thheller14:02:14

(defn history []
  (let [data (clj->js {:chart {:type "bar"}
                       :series
                       [{:name "sales"
                         :data [30 40 45 50 49 60]}]
                       :xaxis
                       {:categories
                        [1991
                         1992
                         1993
                         1994
                         1995
                         1996]}})
        ref-fn
        (fn [elm]
          (-> (charts. elm data)
              (.render)))]
    
    [:div [:div {:ref ref-fn}]]))

thheller14:02:22

something like that

thheller14:02:57

but one thing to watch out for with :ref is that they get called twice

thheller14:02:16

once when they mount with the elm argument. once when they unmount with nil

thheller14:02:46

and if ref-fn is constructed that way in render itself it'll be called twice in each render because it first unmounts the old one and then the new one

thheller14:02:53

its a bit tricky to get right 😛

p-himik15:02:04

That's why ref-fn probably shouldn't be used for anything but storing the ref. :)

thheller15:02:45

well, it should be created when the component mounts but I don't know how to do that with reagent 😛

thheller15:02:27

or not even mounted. when it is created, dunno if that is even accessible in reagent

thheller15:02:32

in JS it would be in the constructor

p-himik16:02:08

You just save the ref in a regular atom in a form-2 component, and then use it in the :component-did-mount handler. Or did I misunderstand something?

lilactown16:02:10

or just (when elm …

lilactown19:02:09

is there a way to infer the return type of a function in a macro?

bfabry19:02:09

you could resolve the var for a symbol passed in to a macro, and get any metadata that's defined on the var. like a return type hint or a spec. if that's what you mean

lilactown19:02:04

I don’t think it’s that simple in CLJS. There’s no var to look up AFAICT

lilactown19:02:33

I have a function that can infer the type of a symbol based on the analyzer info

bfabry19:02:11

oh I missed that I was in the clojurescript channel

thheller20:02:39

you can look at :ret-tag in the analyzer data. dunno how reliable that ultimately is though.

lilactown21:02:41

I’m looking at the analyzer data, and all I see is :tag - is it in some nested bits in :info or :env?

thheller21:02:30

where are you looking?

thheller21:02:38

it'll only be available if the function return value is actually known

lilactown21:02:02

I’m calling (ana-api/no-warn (ana-api/analyze env x)) and looking at the result

thheller21:02:11

[:cljs.analyzer/namespaces 'foo.bar :defs 'a :ret-tag]

thheller21:02:40

thought you resolved symbols?

thheller21:02:12

what was the x?

lilactown21:02:25

in this case I’m just passing in a symbol for x

thheller21:02:40

if you just want to resolve the symbol don't use analyzer

thheller21:02:52

use (cljs.analyzer/resolve-var env the-sym)

lilactown21:02:07

I have a macro that emits a (def ~name …) which contains a value that is a function. is there a way to add the ret-tag to that var?

lilactown21:02:33

I am also annotating the fn that I pass in but there’s a couple steps of indirection, which I’m guess is obscuring it from the analyzer

thheller22:02:20

it makes things a billion times easier to follow if you just paste what the macro actually emits 😛

thheller22:02:50

(def ~(with-meta name {:tag 'function}) ...) might do it

thheller22:02:16

but I don't really know what you are doing

thheller22:02:36

sounds a bit like you are trying to do a little too much magic 😛

lilactown22:02:20

(linked to relevant part)

thheller22:02:44

so you have (def something-wrapped ...) and then (def something something-wrapped)? whats the point of that? ::P

lilactown22:02:34

ehhh historical reasons 😛 yes I can clean that up

lilactown22:02:21

the problem I’m trying to solve is: my code base at work has a mix of helix + reagent components. I’d like the $ macro (https://github.com/Lokeh/helix/blob/master/src/helix/core.clj#L7) to detect if it’s passed a reagent component or a regular React component, and warn if it’s a reagent component

thheller22:02:11

whats a reagent component and whats a regular react component

lilactown22:02:36

reagent component returns vectors, regular react component returns react elements

thheller22:02:36

($ Thing {:foo "bar"}) and you want to know what Thing is?

lilactown22:02:47

yep, or specifically the return value

lilactown22:02:54

I was hoping to leverage the return type of the function to see if it’s a vector or react element. but i’m not sure I can get good enough fidelity with this approach even if I get the annotation of the helix component correct

lilactown22:02:02

it’s an experiment, to say the least

thheller22:02:24

simple way would be to just resolve-var it and see if it has analyzer data

thheller22:02:30

ie. if its written in CLJS

thheller22:02:07

(:require ["some-npm-dep" :refer (Thing)]) won't have analyzer data

thheller22:02:17

while (defn Thing ...) will

lilactown22:02:42

I count helix components also as “react components”, which are defed in CLJS as well

lilactown22:02:59

so I have 3 cases: • helix component: returns React elements • JS component: no analyzer data • anything else

thheller22:02:47

I'd never try to solve that via a macro

thheller22:02:36

for anything else I'd just have a (this-is-something-else foo {:bar 1})

thheller22:02:47

(this-is-js Thing {:bar 1})

thheller22:02:06

just like reagent has [:> SomeJSThing ...]

lilactown22:02:38

yeah that’s not really what I’m trying to solve

lilactown22:02:54

$ works with any kind of React component. helix, something off of NPM, etc.

thheller22:02:25

but don't you need a special cast for JS things and CLJS things anyways?

thheller22:02:51

just for the props? or do you always convert to JS anyways?

lilactown22:02:05

helix’s $ macro emits props as JS

lilactown22:02:26

the little corner case I’m trying to solve is when someone passes a reagent component to $ (since we have a lot of them still)

thheller22:02:26

yes but that seems dumb if you have a CLJS component that wants a clojure map?

thheller22:02:01

dunno really ... I'm so done with react and happy that I don't have to care about details like that anymore 😛

lilactown22:02:21

the component body gets the props wrapped as a bean. it has a certain elegance since there’s no difference between interop and using a CLJS component in terms of how you create a component. it also can be a little faster as long as you’re writing your props map literally

yedi19:02:37

hey yall, quick survey: what libs are you using for managing CSS in your cljs apps?

lilactown19:02:14

we use a wrapper around the CSS-in-JS library emotion

👍 4
isak20:02:53

tailwind css + purgecss, so just utility classes, very little custom css

👍 8
Drew Verlee21:02:05

I feel like I just need a processor from clojure to css, everything else I see css solutions doing is /seems trivel. Like tailwinds utility classes seem mainly useful for cross browser support but the way that information is expressed is akward. E.d pl5 pl6 a class for each property seems like an idea taken to an extreme.

lilactown21:02:33

tailwind is really nice for building things like component libraries, or whipping up a prototype

lilactown21:02:45

but I wouldn’t build an application with just tailwind

lilactown21:02:32

our emotion wrapper is nice for ad-hoc CSS, but again, the goal is to use it for our component library with minor styling in our actual application

lilactown21:02:12

emotion allows you to ad-hoc do things like:

(def my-styles
  (css {:color "green"
        "&:hover" {:color "red"}}))
;; => "auto-generated-class-name-asdf1234"
and then you can refer my-styles wherever you would pass in a class name

shaun-mahood21:02:59

"I feel like I just need a processor from clojure to css" - isn't this exactly what Garden does, or am I misreading your statement?

isak21:02:59

For tailwind, you can also create components out of common patterns. For example:

.btn {
    @apply p-2 font-semibold text-base border rounded-sm;
}

.btn-blue {
  @apply bg-blue-700 text-white border-blue-900;
}

isak21:02:58

In general yes it can look a little verbose sometimes, but I just don't think the alternative is better

Brandon Olivier21:02:32

I'm trying to pull my graphql queries out of my cljs files and into their own files. How do I read them back in from the cljs file?

dpsutton21:02:06

brand new shadow feature for this if you're using it

Chris Lester21:02:12

Read them in as edn. Look at the Lacinia tutorial for some good examples of loading a schema from edn.

thheller21:02:49

hehe brand new 🙂 added exactly one year ago 😛

dpsutton21:02:03

it popped up on my radar the other day and i saw feb so i thought it was brand new lol

dpsutton21:02:11

but now i see the '19

fabrao22:02:35

Hello all, how do I convert this to clojure? Main part is component

<ChatBot
  steps={[
    {
      id: '1',
      message: 'You can add custom components',
      trigger: '2',
    },
    {
      id: '2',
      component: (
        <div> This is an example component </div>
      ),
      end: true,
    },
  ]}
/>

fabrao23:02:52

Anyway, use (reagent/as-element [:div {} "This is an example component"])

lilactown23:02:50

FWIW it wasn’t clear that you were using Reagent vs. some other lib (like rum), so it was hard to answer your question easily