diff --git a/.gitignore b/.gitignore index 71f5ed9..556f930 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ index.html site/demo.js +site/democss.css target pom.xml .lein-repl-history diff --git a/bin/gen-site.js b/bin/gen-site.js index 2485c4e..f565b22 100755 --- a/bin/gen-site.js +++ b/bin/gen-site.js @@ -10,18 +10,22 @@ vm.runInThisContext(src, srcFile); console.log('Generating page'); var main = demo.genpage(); +var ts = '?' + Date.now(); + +var cssFiles = ['examples/todomvc/todos.css', + 'examples/todomvc/todosanim.css', + 'examples/simple/example.css', + 'site/demo.css']; + var head = ['', '', - 'This is Cloact', - '', - '', - '', - '', + 'Cloact: Minimalistic React for ClojureScript', + '', ''].join('\n'); var body = ['', main, - '', + '', '', @@ -32,4 +36,8 @@ var html = ['', '', head, body, ''].join('\n'); console.log('Writing site'); fs.writeFileSync("index.html", html); fs.writeFileSync("site/demo.js", src); +fs.writeFileSync("site/democss.css", + cssFiles.map(function (x) { + return fs.readFileSync(x); + }).join("\n")); console.log('Wrote site'); diff --git a/site/demo.css b/site/demo.css index 2c04060..b8fc84a 100644 --- a/site/demo.css +++ b/site/demo.css @@ -41,7 +41,7 @@ color: #555; } -.demo-text > p { +.demo-text > p, .demo-text > ul { font-family: georgia, serif; font-size: 18px; line-height: 27px; diff --git a/src/cloact/core.cljs b/src/cloact/core.cljs index bfea6c1..e805877 100644 --- a/src/cloact/core.cljs +++ b/src/cloact/core.cljs @@ -9,6 +9,7 @@ (def React tmpl/React) +(def is-client tmpl/isClient) (defn render-component ([comp container] diff --git a/test/demo.cljs b/test/demo.cljs index b006e46..594786a 100644 --- a/test/demo.cljs +++ b/test/demo.cljs @@ -146,53 +146,60 @@ [slider {:value bmi :min 10 :max 50 :param :bmi}]]])) (defn intro [] - [:div.demo-text - - [:h2 "Introduction to Cloact"] + (let [github {:href "https://github.com/holmsand/cloact"} + clojurescript {:href "https://github.com/clojure/clojurescript"} + react {:href "http://facebook.github.io/react/"} + hiccup {:href "https://github.com/weavejester/hiccup"}] + [:div.demo-text - [:p [:a {:href "https://github.com/holmsand/cloact"} "Cloact"] - " provides a minimalistic interface between " - [:a {:href "https://github.com/clojure/clojurescript"} "ClojureScript"] - " and " [:a {:href "http://facebook.github.io/react/"} "React"] - ". It allows you to define efficient React components using nothing but - plain ClojureScript functions, that describe your UI using a " [:a - {:href "https://github.com/weavejester/hiccup"} "Hiccup"] "-like - syntax."] + [:h2 "Introduction to Cloact"] - [:p "A very basic component may look something like this: "] - [demo-component {:comp simple-component - :defs [:simple-component]}] + [:p [:a github "Cloact"] " provides a minimalistic interface + between " [:a clojurescript "ClojureScript"] " and " [:a + react "React"] ". It allows you to define efficient React + components using nothing but plain ClojureScript functions and + data, that describe your UI using a " [:a hiccup "Hiccup"] "-like + syntax."] - [:p "You can build new components using other components as - building blocks. Like this:"] - [demo-component {:comp simple-parent - :defs [:simple-parent]}] + [:p "The goal of Cloact is to make it possible to define + arbitrarily complex UIs using just a couple of basic concepts, + and to be fast enough by default that you rarely have to care + about performance."] - [:p "Data is passed to child components using plain old Clojure - maps. For example, here is a component that shows items in a " - [:code "seq"] ":" ] + [:p "A very basic Cloact component may look something like this: "] + [demo-component {:comp simple-component + :defs [:simple-component]}] - [demo-component {:comp lister-user - :defs [:lister :lister-user]}] + [:p "You can build new components using other components as + building blocks. Like this:"] + [demo-component {:comp simple-parent + :defs [:simple-parent]}] - [:p [:strong "Note: "] - "The " [:code "{:key item}"] " part of the " [:code ":li"] " isn't - really necessary in this simple example, but passing a unique key - for every item in a dynamically generated list of components is - good practice, and helps React to improve performance for large - lists."]]) + [:p "Data is passed to child components using plain old Clojure + maps. For example, here is a component that shows items in a " + [:code "seq"] ":" ] + + [demo-component {:comp lister-user + :defs [:lister :lister-user]}] + + [:p [:strong "Note: "] + "The " [:code "{:key item}"] " part of the " [:code ":li"] " + isn’t really necessary in this simple example, but passing a + unique key for every item in a dynamically generated list of + components is good practice, and helps React to improve + performance for large lists."]])) (defn managing-state [] [:div.demo-text [:h2 "Managing state in Cloact"] - [:p "The easiest way to manage state in Cloact is to use Cloact's + [:p "The easiest way to manage state in Cloact is to use Cloact’s own version of " [:code "atom"] ". It works exactly like the one in clojure.core, except that it keeps track of every time it is - deref'ed. Any component that uses an " [:code "atom"]" is automagically + deref’ed. Any component that uses an " [:code "atom"]" is automagically re-rendered when its value changes."] - [:p "Let's demonstrate that with a simple example:"] + [:p "Let’s demonstrate that with a simple example:"] [demo-component {:comp counting-component :defs [:ns :click-count :counting-component]}] @@ -200,7 +207,7 @@ component. That is easy to do with an " [:code "atom"] " as well."] [:p "Here is an example of that, where we call " - [:code "setTimeout"] " every time the component is rendered to + [:code "setTimeout"] " every time the component is rendered to update a counter:"] [demo-component {:comp timer-component @@ -208,21 +215,28 @@ [:p "The previous example also uses another feature of Cloact: a component function can return another function, that is used to do the actual - rendering. It is called with the same arguments as any other - component function. This allows you to perform some setup of newly - created components, without resorting to React's lifecycle + rendering. This allows you to perform some setup of newly + created components, without resorting to React’s lifecycle events."] [:p "By simply passing atoms around you can share state management between components, like this:"] [demo-component {:comp shared-state - :defs [:ns :atom-input :shared-state]}]]) + :defs [:ns :atom-input :shared-state]}] + + [:p [:strong "Note: "] "Component functions (including the ones + returned by other component functions) are called with three + arguments: "] + [:ul + [:li [:code "props"] ": a map passed from a parent" ] + [:li [:code "children"] ": a vector of the children passed to the component"] + [:li [:code "this"] ": the actual React component"]]]) (defn essential-api [] [:div.demo-text [:h2 "Essential API"] - [:p "Cloact supports most of React's API, but there is really only + [:p "Cloact supports most of React’s API, but there is really only one entry-point that is necessary for most applications: " [:code "cloact.core/render-component"] "."] @@ -236,7 +250,45 @@ [:div.demo-text [:h2 "Performance"] - [:p "Something about performance..."]]) + [:p "React itself is very fast, and so is Cloact. In fact, Cloact + will be even faster than plain React a lot of the time, thanks to + optimizations made possible by ClojureScript."] + + [:p "Mounted components are only re-rendered when their parameters + have changed. The change could come from a deref’ed " + [:code "atom"] ", the arguments passed to the component (i.e the + ”props” map and children) or component state."] + + [:p "All of these are checked for changes with a simple " + [:code "identical?"] " which is basically only a pointer + comparison, so the overhead is very low (even if the components of + the props map are compared separately, and " + [:code ":style"] " attributes are handled specially). Even the + built-in React components are handled the same way."] + + [:p "All this means that you (hopefully) simply won’t have to care + about performance most of the time. Just define your UI however you like + – it will be fast enough."] + + [:p "There are a couple of situations that you might have to care + about, though. If you give Cloact big " [:code "seq"] "s of + components to render, you might have to supply all of them with a + unique " [:code ":key"] " attribute to speed up rendering. Also note + that anonymous functions are not, in general, equal to each other + even if they represent the same code and closure."] + + [:p "But again, in general you should just trust that React and + Cloact will be fast enough. This very page is composed of a single + Cloact component with thousands of child components (every single + parenthesis etc in the code examples is a separate component), and + yet the page can be updated many times every second without taxing + the browser the slightest."] + + [:p "Incidentally, this page also uses another React trick: the + entire page is pre-rendered using Node, and " + [:code "cloact/render-component-to-string"] ". When it is loaded + into the browser, React automatically attaches event-handlers to + the already present DOM tree."]]) (defn bmi-demo [] [:div.demo-text @@ -262,7 +314,7 @@ [:h2 "Complete demo"] [:p "Cloact comes with a couple of complete examples, with - Leiningen project files and everything. Here's one of them in + Leiningen project files and everything. Here’s one of them in action:"] [demo-component {:comp simpleexample/simple-example @@ -291,16 +343,16 @@ [:div.test-output-mini [runtests/test-output-mini]] [:div.cloact-demo - [:h1 "Cloact: Simple and fast UI for ClojureScript"] + [:h1 "Cloact: Minimalistic React for ClojureScript"] [intro] [managing-state] [essential-api] - [performance] [bmi-demo] + [performance] [test-results] [complete-simple-demo] - [todomvc-demo] - [github-badge]]]) + [todomvc-demo]] + [github-badge]]) (defn ^:export mountdemo [] (cloact/render-component [demo] (.-body js/document))) diff --git a/test/runtests.cljs b/test/runtests.cljs index 2a7f5d2..add607c 100644 --- a/test/runtests.cljs +++ b/test/runtests.cljs @@ -19,7 +19,7 @@ (println "-----------------------------------------") (reset! test-results (t/run-all-tests)) (println "-----------------------------------------")) - 1000) + (if cloact/is-client 1000 0)) (defn test-output [] (let [res @test-results]