Giter Club home page Giter Club logo

Comments (14)

Odie avatar Odie commented on August 20, 2024 11

Ran into this today...
extracted the relevant parts from cljs-react-material-ui and it worked like a charm.

(ns app.core
  (:require [sablono.util]))

(defn adapt-class [react-class]
  (fn [& args]
    (let [[opts children] (if (map? (first args))
                            [(first args) (rest args)]
                            [{} args])
          type# (first children)]
      (let [new-children (if (vector? type#)
                           [(sablono.interpreter/interpret children)]
                           children)]

        (apply js/React.createElement react-class
               (clj->js (sablono.util/html-to-dom-attrs opts)) new-children)))))

Would really appreciate it if an utility like this can be added to rum. It seems like a natural thing to want to do.

from rum.

priornix avatar priornix commented on August 20, 2024 7

Thanks for this, I adapted this for use within my Antizer library, but also added a few things:

  • Changed (vector?) to (sequential?) to handle cases where child elements can be a list, eg: from a (for)
  • Check properties hash-map values for any valid HTML element tags and convert them to a React element
  • Sablono's html-to-dom-attrs function does not work for nested hash-maps, so I replaced it with a function I wrote map-keys->camel-case.
(require '[clojure.string :as string])
(require '[clojure.walk :as w])

(defn kebab-case->camel-case
  "Converts from kebab case to camel case, eg: on-click to onClick"
  [input]
  (let [words (string/split input #"-")
        capitalize (->> (rest words)
                        (map #(apply str (string/upper-case (first %)) (rest %))))]
    (apply str (first words) capitalize)))

(defn map-keys->camel-case 
  "Stringifys all the keys of a cljs hashmap and converts them
   from kebab case to camel case. If :html-props option is specified, 
   then rename the html properties values to their dom equivalent
   before conversion"
  [data & {:keys [html-props]}]
  (let [convert-to-camel (fn [[key value]]
                           [(kebab-case->camel-case (name key)) value])]
    (w/postwalk (fn [x]
                  (if (map? x)
                    (let [new-map (if html-props
                                    (rename-keys x {:class :className :for :htmlFor})
                                    x)]
                      (into {} (map convert-to-camel new-map)))
                    x))
      data)))

(defn adapt-class [react-class & args]
  (let [[opts children] (if (map? (first args))
                          [(first args) (rest args)]
                          [{} args])
        type# (first children)
        ;; we have to make sure to check if the children is sequential 
        ;; as a list can be returned, eg: from a (for)
        new-children (if (sequential? type#)
                       [(sablono.interpreter/interpret children)]
                       children)
        ;; convert any options key value to a react element, if
        ;; a valid html element tag is used, using sablono
        vector->react-elems (fn [[key val]]
                              (if (sequential? val)
                                [key (sablono.interpreter/interpret val)]
                                [key val]))
        new-options (into {} (map vector->react-elems opts))]
    (apply js/React.createElement react-class
      ;; sablono html-to-dom-attrs does not work for nested hash-maps
      (clj->js (map-keys->camel-case new-options :html-props true)) 
      new-children)))

from rum.

piranha avatar piranha commented on August 20, 2024 2

Maybe... I'm not a fan of that cryptic stuff (hard to search for, hard to understand if you're seeing it for the first time). I'd prefer something like (def Comp (rum/pour js/Component)) (adapt-class I mean :)).

from rum.

tonsky avatar tonsky commented on August 20, 2024 1

@abhishiv have you solved this?

from rum.

cldwalker avatar cldwalker commented on August 20, 2024 1

I had no problem using a react-treeview component with rum. May be worth mentioning that rum is compatible w/ react components. I know that quiescent does

from rum.

roman01la avatar roman01la commented on August 20, 2024 1

Actually, since we are going to fork Sablono I'm more into introducing [:> js/Component props & children] syntax, similar to Reagent

from rum.

roman01la avatar roman01la commented on August 20, 2024 1

Fixed in 3938ac1

from rum.

tonsky avatar tonsky commented on August 20, 2024

When you write [(.-Route js/ReactRouter) {:name "home"} ] sablono tries to parse it as tag, something of form [:div attrs & children].

Just create component with interop syntax, e.g. (js/ReactRouter.Route #js {:name "home"}). Do not put in a vector as first element (but as child β€” in sablono terms β€” it’s ok)

from rum.

taylorSando avatar taylorSando commented on August 20, 2024

Is there an easy way to compose external react components, like say js/ReactBootstrap.ModalDialog?

I've only been able to create external react components with a (js/React.createElement js/ReactBootstrap.Button (clj->js args) content) command, which is not composable. I know in Reagent, you can just say [ReactBootstrap.Button args "label"]

from rum.

sabbaticaldev avatar sabbaticaldev commented on August 20, 2024

I think this could be clear in docs. Reagent has a function to adapt class, and I saw something similar in a cljs-material-ui wrapper, "adapt-rum-class" (https://github.com/madvas/cljs-react-material-ui/blob/f66e8047b556e41dce45b1391cae9ca13f25242f/src/cljs_react_material_ui/core.clj). Is it needed?

from rum.

sooheon avatar sooheon commented on August 20, 2024

@priornix 's solution above has the problem where any data prop you pass in to the component, if it's in the form of a hash-map, will get keys mangled.

from rum.

roman01la avatar roman01la commented on August 20, 2024

We'll probably introduce something like this. @piranha @martinklepsch Do you have anything like that in your code?

from rum.

piranha avatar piranha commented on August 20, 2024

We don't have a lot of external React components and for those we have integration is just some custom code. We used antizer in Suppliers UI though and I didn't touch that for a while, but I think it worked well. :) And we have more external deps there.

from rum.

roman01la avatar roman01la commented on August 20, 2024

I'm perhaps biased since I'm using Reagent now and it feels good to be able to use JS components inline, w/o making another def using class adapter. But I'm also fine with going both ways.

from rum.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.