mirror of https://github.com/status-im/reagent.git
Reorganize build, tests and doc site
Tests and doc site can now run without node, using only "lein figwheel". Tests and site are then re-run automatically whenever a source file changes. The doc site is now generated into "outsite/public", and can be copied into the "gh-pages" branch with "make build-gh-pages". "make push-gh-pages" builds the doc site and pushes it upstream to the gh-pages branch there. Generation of html pages is now handled completely in ClojureScript, loaded from "bin/gen-site.js". Link handling is a bit simplified.
This commit is contained in:
parent
3bf98230ca
commit
bffbae231d
|
@ -6,3 +6,9 @@ pom.xml
|
|||
.lein-repl-history
|
||||
pom.xml.asc
|
||||
.nrepl-port
|
||||
demo/empty.cljs
|
||||
outsite/public/index.html
|
||||
outsite/public/js
|
||||
outsite/public/news
|
||||
outsite/public/css
|
||||
out
|
||||
|
|
110
Makefile
110
Makefile
|
@ -1,51 +1,87 @@
|
|||
PORT = 4562
|
||||
|
||||
PROF = dev
|
||||
# PROF = prod,srcmap
|
||||
# PROF = prod
|
||||
|
||||
CLJSBUILD = client
|
||||
CLJSDIRS = src test
|
||||
|
||||
VERSION = 0.4.3
|
||||
|
||||
REACT_VERSION = 0.12.1
|
||||
|
||||
all: buildrun
|
||||
PROF =
|
||||
PORT = 3449
|
||||
|
||||
run: openbrowser buildrun
|
||||
SITEDIR = outsite/public
|
||||
OUTPUTDIR = $(SITEDIR)/js/out
|
||||
|
||||
leinbuild: setup
|
||||
lein -o with-profile $(PROF) cljsbuild once $(CLJSBUILD)
|
||||
|
||||
openbrowser:
|
||||
(sleep 1 && open site/test.html) &
|
||||
# convenience shortcuts for continous building
|
||||
##############################################
|
||||
|
||||
buildrun: setup
|
||||
lein -o with-profile $(PROF) cljsbuild auto $(CLJSBUILD)
|
||||
# development build with auto-reloading
|
||||
run: figwheel
|
||||
|
||||
runtest:
|
||||
$(MAKE) run PROF=test,$(PROF)
|
||||
# development build with auto-reloading and site generation
|
||||
runsite:
|
||||
@$(MAKE) run PROF=dev,site,$(PROF)
|
||||
|
||||
runsite: setup
|
||||
(sleep 3 && open "http://127.0.0.1:$(PORT)/$$(basename $$PWD)") &
|
||||
# production build with auto-rebuild
|
||||
runprod: clean
|
||||
@$(MAKE) serve-site PROF=prod,$(PROF)
|
||||
|
||||
# production build with auto-rebuild and testing
|
||||
runprodtest: clean
|
||||
@$(MAKE) serve-site PROF=prod,test,$(PROF)
|
||||
|
||||
clean:
|
||||
lein clean
|
||||
|
||||
|
||||
## Subtargets
|
||||
|
||||
figwheel: trigger-build
|
||||
@echo "Will start figwheel server at:\nhttp://127.0.0.1:$(PORT)\n\n"
|
||||
lein with-profile $(PROF), figwheel
|
||||
|
||||
serve-site: trigger-build
|
||||
@echo "Starting site at:\nhttp://127.0.0.1:$(PORT)/public\n\n"
|
||||
( trap "kill 0" SIGINT SIGTERM EXIT; \
|
||||
( cd .. && python -m SimpleHTTPServer $(PORT) & ); \
|
||||
lein -o with-profile $(PROF),prod cljsbuild auto $(CLJSBUILD) )
|
||||
( cd $(SITEDIR)/.. && python -m SimpleHTTPServer $(PORT) & ); \
|
||||
lein with-profile $(PROF), cljsbuild auto )
|
||||
|
||||
install: leinbuild
|
||||
lein install
|
||||
trigger-build:
|
||||
# always trigger build to make sure page-generation works
|
||||
@echo "(ns empty.generated.ns)" > demo/empty.cljs
|
||||
cat examples/todomvc/todos.css examples/simple/example.css \
|
||||
> site/public/css/examples.css
|
||||
|
||||
preclean:
|
||||
rm -rf repl .repl target out
|
||||
mkdir -p vendor/reagent
|
||||
|
||||
clean: preclean
|
||||
rm -rf news assets
|
||||
lein -o clean
|
||||
|
||||
setup: preclean
|
||||
mkdir -p news assets
|
||||
## gh-pages support
|
||||
###################
|
||||
|
||||
# build site and push upstream to the gh-pages branch
|
||||
push-gh-pages: build-gh-pages
|
||||
git push origin gh-pages
|
||||
|
||||
build-gh-pages: gen-site gh-pages-add
|
||||
|
||||
gen-site: clean
|
||||
lein with-profile prod cljsbuild once
|
||||
|
||||
# copy contents of $(SITEDIR) to branch gh-pages
|
||||
gh-pages-add:
|
||||
# sanity check
|
||||
test -f $(SITEDIR)/index.html
|
||||
test ! -e $(OUTPUTDIR)
|
||||
# make sure gh-pages branch exists
|
||||
git show-branch gh-pages || true | git mktree | \
|
||||
xargs git commit-tree | xargs git branch gh-pages
|
||||
# clone gh-pages branch, and commit site to that
|
||||
cd $(SITEDIR) && \
|
||||
rm -rf .git tmp && \
|
||||
git clone ../.. -lnb gh-pages tmp && \
|
||||
mv tmp/.git . && \
|
||||
git add . && git commit -m "Updated" && \
|
||||
git push && rm -rf .git tmp
|
||||
|
||||
|
||||
## Misc utilities
|
||||
#################
|
||||
|
||||
show-outdated:
|
||||
lein ancient :all
|
||||
|
@ -56,14 +92,6 @@ download-react:
|
|||
curl -L "http://fb.me/react-$(REACT_VERSION).min.js" \
|
||||
-o vendor/reagent/react.min.js
|
||||
|
||||
gensite:
|
||||
node bin/gen-site.js
|
||||
|
||||
demobuild:
|
||||
$(MAKE) PROF=prod,demo leinbuild
|
||||
|
||||
buildsite: demobuild gensite
|
||||
|
||||
setversion:
|
||||
version=$(VERSION); \
|
||||
find . -name project.clj -o -name README.md | \
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
var fs = require("fs");
|
||||
var vm = require("vm");
|
||||
var path = require("path");
|
||||
|
||||
var loadSrc = function (mainFile, outputDir, devModule) {
|
||||
var src = fs.readFileSync(mainFile);
|
||||
var googDir = path.join(outputDir, "goog");
|
||||
var optNone = false;
|
||||
if (outputDir) {
|
||||
optNone = fs.existsSync(path.join(googDir, "deps.js"));
|
||||
}
|
||||
|
||||
if (optNone) {
|
||||
var cwd = process.cwd();
|
||||
if (!global.goog) global.goog = {};
|
||||
|
||||
global.CLOSURE_IMPORT_SCRIPT = function (src) {
|
||||
require(path.resolve(path.resolve(
|
||||
cwd, path.join(googDir, src))));
|
||||
return true;
|
||||
};
|
||||
|
||||
var f = path.join(googDir, "base.js");
|
||||
vm.runInThisContext(fs.readFileSync(f), f);
|
||||
require(path.resolve(cwd, mainFile));
|
||||
goog.require(devModule);
|
||||
} else {
|
||||
global.globalNodeRequire = require;
|
||||
|
||||
vm.runInThisContext("(function (require) {"
|
||||
+ src
|
||||
+ "\n})(globalNodeRequire);", mainFile);
|
||||
}
|
||||
return optNone;
|
||||
};
|
||||
|
||||
exports.load = loadSrc;
|
|
@ -1,53 +1,31 @@
|
|||
#! /usr/bin/env node
|
||||
|
||||
var fs = require("fs");
|
||||
var vm = require('vm');
|
||||
var cljsLoad = require("./cljs-load");
|
||||
|
||||
var cssFiles = ['examples/todomvc/todos.css',
|
||||
'examples/todomvc/todosanim.css',
|
||||
'examples/simple/example.css',
|
||||
'site/demo.css'];
|
||||
var srcFile = "outsite/public/js/main.js";
|
||||
var outputDirectory = "outsite/public/js/out/";
|
||||
var moduleName = "devsetup";
|
||||
|
||||
var srcFile = "target/cljs-client.js";
|
||||
var src = fs.readFileSync(srcFile);
|
||||
var beep = "\u0007";
|
||||
|
||||
var clj_genpages = function (profile) {
|
||||
if (typeof demo === 'undefined') {
|
||||
vm.runInThisContext(src, srcFile);
|
||||
}
|
||||
return demo.genpages(profile);
|
||||
var gensite = function () {
|
||||
console.log("Loading " + srcFile);
|
||||
var optNone = cljsLoad.load(srcFile, outputDirectory, moduleName);
|
||||
sitetools.genpages({"opt-none": optNone});
|
||||
}
|
||||
|
||||
var generate = function () {
|
||||
var pages = clj_genpages();
|
||||
Object.keys(pages).map(function (page) {
|
||||
fs.writeFileSync(page, pages[page]);
|
||||
});
|
||||
fs.writeFileSync("assets/demo.js", src);
|
||||
fs.writeFileSync("assets/demo.css",
|
||||
cssFiles.map(function (x) {
|
||||
return fs.readFileSync(x);
|
||||
}).join("\n"));
|
||||
console.log('Wrote site');
|
||||
};
|
||||
|
||||
var compileOk = function () {
|
||||
var msg = process.argv[2];
|
||||
if (msg && msg.match(/failed/)) {
|
||||
console.log("Compilation failed");
|
||||
// beep
|
||||
console.log('\u0007');
|
||||
return false;
|
||||
}
|
||||
var compileFail = function () {
|
||||
var msg = process.argv[process.argv.length - 1];
|
||||
if (msg && msg.match(/failed/)) {
|
||||
console.log("Compilation failed" + beep);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (compileOk()) {
|
||||
console.log('Writing site');
|
||||
try {
|
||||
generate();
|
||||
} catch (e) {
|
||||
console.log('\u0007');
|
||||
console.error(e.stack);
|
||||
}
|
||||
if (!compileFail()) {
|
||||
try {
|
||||
gensite();
|
||||
} catch (e) {
|
||||
console.log(e + beep);
|
||||
console.error(e.stack);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
(ns demo
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :as i :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :as i :refer-macros [.' .!]]
|
||||
[clojure.string :as string]
|
||||
[reagentdemo.page :as page :refer [page-map page link prefix]]
|
||||
[sitetools :as tools :refer [link]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[reagentdemo.intro :as intro]
|
||||
[reagentdemo.news :as news]
|
||||
|
@ -10,9 +10,7 @@
|
|||
|
||||
(i/import-react)
|
||||
|
||||
(swap! page-map assoc
|
||||
"index.html" (fvar intro/main)
|
||||
"news/index.html" (fvar news/main))
|
||||
(def test-results-comp (atom nil))
|
||||
|
||||
(def github {:href "https://github.com/reagent-project/reagent"})
|
||||
|
||||
|
@ -23,48 +21,26 @@
|
|||
:alt "Fork me on GitHub"
|
||||
:src "https://s3.amazonaws.com/github/ribbons/forkme_left_orange_ff7600.png"}]])
|
||||
|
||||
(def index-page "index.html")
|
||||
(def news-page "news/index.html")
|
||||
|
||||
(tools/register-page index-page (fn [] [intro/main]))
|
||||
(tools/register-page news-page (fn [] [news/main]))
|
||||
|
||||
(defn demo []
|
||||
(dbg "demo")
|
||||
[:div
|
||||
[:div.nav
|
||||
[:ul.nav
|
||||
[:li.brand [link {:href (fvar intro/main)} "Reagent:"]]
|
||||
[:li [link {:href (fvar intro/main)} "Intro"]]
|
||||
[:li [link {:href (fvar news/main)} "News"]]
|
||||
[:li.brand [link {:href index-page} "Reagent:"]]
|
||||
[:li [link {:href index-page} "Intro"]]
|
||||
[:li [link {:href news-page} "News"]]
|
||||
[:li [:a github "GitHub"]]]]
|
||||
(let [comp (get @page-map @page (fvar intro/main))]
|
||||
[comp])
|
||||
(when @test-results-comp [@test-results-comp])
|
||||
[tools/page-content]
|
||||
[github-badge]])
|
||||
|
||||
(defn ^:export mountdemo [p]
|
||||
(when p (page/set-start-page p))
|
||||
(reagent/render-component [demo] (.-body js/document)))
|
||||
|
||||
(defn gen-page [p timestamp]
|
||||
(reset! page p)
|
||||
(let [body (reagent/render-component-to-string [demo])
|
||||
title @page/title-atom
|
||||
load-page (case p "index.html" "" p)]
|
||||
(str "<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<title>" title "</title>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<link rel='stylesheet' href='" (prefix "assets/demo.css") timestamp "'>
|
||||
</head>
|
||||
<body>
|
||||
" body "
|
||||
<script type='text/javascript'
|
||||
src='" (prefix "assets/demo.js") timestamp "'></script>
|
||||
<script type='text/javascript'>
|
||||
setTimeout(function() {demo.mountdemo('" load-page "')}, 200);
|
||||
</script>
|
||||
</body>
|
||||
</html>")))
|
||||
|
||||
(defn ^:export genpages []
|
||||
(let [timestamp (str "?" (.now js/Date))]
|
||||
(->> (keys @page-map)
|
||||
(map #(vector % (gen-page % timestamp)))
|
||||
(into {})
|
||||
clj->js)))
|
||||
(defn start! [{:keys [test-results]}]
|
||||
(reset! test-results-comp test-results)
|
||||
(tools/start! {:body (fn [] [demo])}))
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[clojure.string :as string]
|
||||
[reagentdemo.page :as rpage]
|
||||
[reagentdemo.syntax :as syntax]))
|
||||
|
||||
(def syntaxify (memoize syntax/syntaxify))
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
(ns reagentdemo.intro
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[clojure.string :as string]
|
||||
[reagentdemo.syntax :refer-macros [get-source]]
|
||||
[reagentdemo.page :refer [link title]]
|
||||
[sitetools :refer [link title]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[simpleexample :as simple]
|
||||
[todomvc :as todo]))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
(ns reagentdemo.news
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[reagentdemo.page :refer [title link page-map]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[sitetools :as tools :refer [title link]]
|
||||
[reagentdemo.news.anyargs :as anyargs]
|
||||
[reagentdemo.news.async :as async]
|
||||
[reagentdemo.news.undodemo :as undodemo]
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
(ns reagentdemo.news.anyargs
|
||||
(:require [reagent.core :as r :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[reagentdemo.syntax :refer-macros [get-source]]
|
||||
[reagentdemo.page :refer [title link page-map]]
|
||||
[sitetools :as tools :refer [title link]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[geometry.core :as geometry]))
|
||||
|
||||
(def url "news/any-arguments.html")
|
||||
|
||||
(def funmap (-> ::this get-source common/fun-map))
|
||||
(def src-for (partial common/src-for funmap))
|
||||
|
||||
|
@ -35,7 +37,7 @@
|
|||
geometry {:href "https://github.com/reagent-project/reagent/tree/master/examples/geometry"}
|
||||
jonase {:href "https://github.com/jonase"}]
|
||||
[:div.reagent-demo
|
||||
[:h1 [link {:href (fvar main)} head]]
|
||||
[:h1 [link {:href url} head]]
|
||||
[title (str "Reagent 0.4.0: " head)]
|
||||
[:div.demo-text
|
||||
|
||||
|
@ -53,12 +55,11 @@
|
|||
them."]
|
||||
|
||||
(if summary
|
||||
[link {:href (fvar main)
|
||||
:class 'news-read-more} "Read more"]
|
||||
[link {:href url :class 'news-read-more} "Read more"]
|
||||
[:div.demo-text
|
||||
[:p "In other words, you can now do this:"]
|
||||
|
||||
[demo-component {:comp (fvar say-hello)
|
||||
[demo-component {:comp say-hello
|
||||
:src (src-for [:hello-component :say-hello])}]
|
||||
|
||||
[:p "In the above example, it wouldn’t make any difference at
|
||||
|
@ -77,7 +78,7 @@
|
|||
and " [:code "for"] " expressions, so it’s safest to always
|
||||
put the call at the top, as in " [:code "my-div"] " here:"]
|
||||
|
||||
[demo-component {:comp (fvar call-my-div)
|
||||
[demo-component {:comp call-my-div
|
||||
:src (src-for [:nsr :my-div :call-my-div])}]
|
||||
|
||||
[:p [:em "Note: "] [:code "r/props"] " and "
|
||||
|
@ -129,7 +130,6 @@
|
|||
use Reagent’s new calling convensions, and looks like
|
||||
this:"]
|
||||
|
||||
[demo-component {:comp (fvar geometry-example)}]])]]))
|
||||
[demo-component {:comp geometry-example}]])]]))
|
||||
|
||||
(swap! page-map assoc
|
||||
"news/any-arguments.html" (fvar main))
|
||||
(tools/register-page url (fn [] [main]))
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
(ns reagentdemo.news.async
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[reagentdemo.syntax :refer-macros [get-source]]
|
||||
[reagentdemo.page :refer [title link page-map]]
|
||||
[sitetools :as tools :refer [title link]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]))
|
||||
|
||||
(def url "news/reagent-is-async.html")
|
||||
|
||||
(def funmap (-> "reagentdemo/news/async.cljs" get-source common/fun-map))
|
||||
(def src-for (partial common/src-for funmap))
|
||||
|
||||
|
@ -92,7 +94,7 @@
|
|||
(let [om-article {:href "http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/"}]
|
||||
[:div.reagent-demo
|
||||
[title "Reagent: Faster by waiting"]
|
||||
[:h1 [link {:href (fvar main)} "Faster by waiting"]]
|
||||
[:h1 [link {:href url} "Faster by waiting"]]
|
||||
[:div.demo-text
|
||||
[:h2 "Reagent gets async rendering"]
|
||||
|
||||
|
@ -106,7 +108,7 @@
|
|||
changes are rendered in one single go."]
|
||||
|
||||
(if summary
|
||||
[link {:href (fvar main)
|
||||
[link {:href url
|
||||
:class 'news-read-more} "Read more"]
|
||||
[:div.demo-text
|
||||
|
||||
|
@ -194,5 +196,4 @@
|
|||
:reset-random-colors :color-choose :ncolors-choose
|
||||
:palette :color-demo])}]])]]))
|
||||
|
||||
(swap! page-map assoc
|
||||
"news/reagent-is-async.html" (fvar main))
|
||||
(tools/register-page url (fn [] [main]))
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
(ns reagentdemo.news.clockpost
|
||||
(:require [reagent.core :as r :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg]]
|
||||
[reagentdemo.syntax :refer-macros [get-source]]
|
||||
[reagentdemo.page :refer [title link page-map]]
|
||||
[sitetools :as tools :refer [title link]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[reagentdemo.news.binaryclock :as binaryclock]))
|
||||
|
||||
(def url "news/binary-clock.html")
|
||||
|
||||
(def funmap (-> "reagentdemo/news/binaryclock.cljs"
|
||||
get-source common/fun-map))
|
||||
(def src-for (partial common/src-for funmap))
|
||||
|
@ -24,7 +26,7 @@
|
|||
clocksrc {:href "https://github.com/reagent-project/reagent/blob/master/demo/reagentdemo/news/binaryclock.cljs"}]
|
||||
|
||||
[:div.reagent-demo
|
||||
[:h1 [link {:href (fvar main)} head]]
|
||||
[:h1 [link {:href url} head]]
|
||||
[title head]
|
||||
[:div.demo-text
|
||||
|
||||
|
@ -42,8 +44,8 @@
|
|||
[:p "So, without further ado, here is a binary clock using Reagent."]
|
||||
|
||||
(if summary
|
||||
[link {:href (fvar main)
|
||||
:class 'news-read-more} "Read more"]
|
||||
[link {:href url
|
||||
:class 'news-read-mode} "Read more"]
|
||||
[:div.demo-text
|
||||
|
||||
[fn-src :nsr]
|
||||
|
@ -119,5 +121,4 @@
|
|||
description that corresponds to those arguments, and leave it
|
||||
to React to actually display that UI."]])]]))
|
||||
|
||||
(swap! page-map assoc
|
||||
"news/binary-clock.html" (fvar main))
|
||||
(tools/register-page url (fn [] [main]))
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
(ns reagentdemo.news.undodemo
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[reagentdemo.syntax :refer-macros [get-source]]
|
||||
[reagentdemo.page :refer [title link page-map]]
|
||||
[sitetools :as tools :refer [title link]]
|
||||
[reagentdemo.common :as common :refer [demo-component]]
|
||||
[todomvc :as todomvc]))
|
||||
|
||||
(def url "news/cloact-reagent-undo-demo.html")
|
||||
|
||||
(def funmap (-> ::this get-source common/fun-map))
|
||||
(def src-for (partial common/src-for funmap))
|
||||
|
||||
|
@ -48,7 +50,7 @@
|
|||
(defn main [{:keys [summary]}]
|
||||
(let [head "Cloact becomes Reagent: Undo is trivial"]
|
||||
[:div.reagent-demo
|
||||
[:h1 [link {:href (fvar main)} head]]
|
||||
[:h1 [link {:href url} head]]
|
||||
[title head]
|
||||
[:div.demo-text
|
||||
[:h2 "(reset! cloact-name \"Reagent\")"]
|
||||
|
@ -65,7 +67,7 @@
|
|||
search-and-replace should suffice."]
|
||||
|
||||
(if summary
|
||||
[link {:href (fvar main)
|
||||
[link {:href url
|
||||
:class 'news-read-more} "Read more"]
|
||||
[:div.demo-text
|
||||
|
||||
|
@ -86,5 +88,4 @@
|
|||
|
||||
[undo-demo-cleanup]])]]))
|
||||
|
||||
(swap! page-map assoc
|
||||
"news/cloact-reagent-undo-demo.html" (fvar main))
|
||||
(tools/register-page url (fn [] [main]))
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
(ns reagentdemo.page
|
||||
(:require [reagent.core :as reagent :refer [atom partial]]
|
||||
[reagent.interop :refer-macros [.' .! fvar fvar?]]
|
||||
[reagent.debug :refer-macros [dbg]]
|
||||
[clojure.string :as string]
|
||||
[goog.events :as events]
|
||||
[goog.history.EventType :as hevt])
|
||||
(:import [goog History]
|
||||
[goog.history Html5History]))
|
||||
|
||||
(def page (atom ""))
|
||||
(def base-path (atom nil))
|
||||
(def html5-history false)
|
||||
|
||||
(defn create-history []
|
||||
(when reagent/is-client
|
||||
(let [proto (-> js/window .-location .-protocol)]
|
||||
(if (and (.isSupported Html5History)
|
||||
(case proto "http:" true "https:" true false))
|
||||
(do (set! html5-history true)
|
||||
(doto (Html5History.)
|
||||
(.setUseFragment false)))
|
||||
(History.)))))
|
||||
|
||||
(def history (create-history))
|
||||
|
||||
(defn setup-history []
|
||||
(when-let [h history]
|
||||
(events/listen h hevt/NAVIGATE
|
||||
(fn [e]
|
||||
(reset! page (subs (.-token e)
|
||||
(count @base-path)))
|
||||
(reagent/flush)))
|
||||
(add-watch page ::history (fn [_ _ oldp newp]
|
||||
(when-not (= oldp newp)
|
||||
(.setToken h (str @base-path newp)))))
|
||||
(.setEnabled h true)))
|
||||
|
||||
(js/setTimeout setup-history 100)
|
||||
|
||||
(defn set-start-page [p]
|
||||
(when html5-history
|
||||
;; Find base-path for html5 history
|
||||
(let [loc (-> js/window .-location .-pathname)
|
||||
split #".[^/]*"
|
||||
loc-parts (re-seq split loc)
|
||||
page-parts (re-seq split (case p "" "." p))
|
||||
base (str (apply str
|
||||
(drop-last (count page-parts) loc-parts))
|
||||
"/")]
|
||||
(reset! base-path (string/replace base #"^/" ""))))
|
||||
(reset! page p))
|
||||
|
||||
(def title-atom (atom ""))
|
||||
|
||||
(def page-map (atom nil))
|
||||
|
||||
(def reverse-page-map (atom nil))
|
||||
|
||||
(add-watch page-map ::page-map-watch
|
||||
(fn [_ _ _ new-map]
|
||||
(reset! reverse-page-map
|
||||
(into {} (for [[k v] new-map]
|
||||
[v k])))))
|
||||
|
||||
(defn prefix [href]
|
||||
(let [depth (-> #"/" (re-seq @page) count)]
|
||||
(str (->> "../" (repeat depth) (apply str)) href)))
|
||||
|
||||
(defn link [props child]
|
||||
(let [rpm @reverse-page-map
|
||||
href (-> props :href rpm)]
|
||||
(assert (string? href))
|
||||
[:a (assoc props
|
||||
:href (prefix href)
|
||||
:on-click (if history
|
||||
(fn [e]
|
||||
(.preventDefault e)
|
||||
(reset! page href)
|
||||
(reagent/next-tick
|
||||
#(set! (.-scrollTop (.-body js/document))
|
||||
0)))
|
||||
identity))
|
||||
child]))
|
||||
|
||||
(add-watch page ::title-watch
|
||||
(fn [_ _ _ p]
|
||||
;; First title on a page wins
|
||||
(reset! title-atom "")))
|
||||
|
||||
(defn title [name]
|
||||
(when (= @title-atom "")
|
||||
(if reagent/is-client
|
||||
(set! (.-title js/document) name))
|
||||
(reset! title-atom name))
|
||||
[:div])
|
|
@ -0,0 +1,259 @@
|
|||
(ns sitetools
|
||||
(:require [clojure.string :as string]
|
||||
[goog.events :as evt]
|
||||
[goog.history.EventType :as hevt]
|
||||
[reagent.core :as reagent :refer [atom partial]]
|
||||
[reagent.debug :refer-macros [dbg log dev?]]
|
||||
[reagent.interop :as i :refer-macros [.' .!]])
|
||||
(:import [goog History]
|
||||
[goog.history Html5History]
|
||||
[goog.net Jsonp]))
|
||||
|
||||
(when (exists? js/console)
|
||||
(enable-console-print!))
|
||||
|
||||
(declare page-content)
|
||||
(declare prefix)
|
||||
|
||||
|
||||
|
||||
;;; Configuration
|
||||
|
||||
(defonce config (atom {:page-map {"index.html"
|
||||
(fn [] [:div "Empty"])}
|
||||
:body (fn [] [:div (page-content)])
|
||||
:site-dir "outsite/public"
|
||||
:css-infiles ["site/public/css/main.css"]
|
||||
:css-file "css/built.css"
|
||||
:js-file "js/main.js"
|
||||
:js-dir "js/out"
|
||||
:default-title ""
|
||||
:allow-html5-history false}))
|
||||
|
||||
(defonce page (atom "index.html"))
|
||||
(defonce page-title (atom (:default-title @config)))
|
||||
(defonce page-state (atom {:has-history false}))
|
||||
|
||||
(defn register-page [pageurl comp]
|
||||
(assert (string? pageurl) (str "expected string, not " pageurl))
|
||||
(assert (fn? comp))
|
||||
(swap! config update-in [:page-map] assoc pageurl comp))
|
||||
|
||||
|
||||
|
||||
;;; Components
|
||||
|
||||
(defn link
|
||||
[props child]
|
||||
(let [p (:href props)
|
||||
f ((:page-map @config) p)]
|
||||
(assert (ifn? f) (str "couldn't resolve ppage " p))
|
||||
(assert (string? p))
|
||||
[:a (assoc props
|
||||
:href (prefix p)
|
||||
:on-click (if (:has-history @page-state)
|
||||
(fn [e]
|
||||
(.preventDefault e)
|
||||
(reset! page p)
|
||||
(reagent/next-tick
|
||||
#(set! (.-scrollTop (.-body js/document))
|
||||
0)))
|
||||
identity))
|
||||
child]))
|
||||
|
||||
(defn title [name]
|
||||
(when (= @page-title "")
|
||||
;; First title on a page wins
|
||||
(reset! page-title name)
|
||||
(when reagent/is-client
|
||||
(set! (.-title js/document) @page-title)))
|
||||
nil)
|
||||
|
||||
(defn page-content []
|
||||
[(get-in @config [:page-map @page]
|
||||
(get-in @config [:page-map "index.html"]))])
|
||||
|
||||
|
||||
|
||||
|
||||
;;; Implementation:
|
||||
|
||||
(defn default-content []
|
||||
[:div "Empty"])
|
||||
|
||||
(add-watch page ::title-watch
|
||||
(fn [_ _ _ p]
|
||||
;; First title on a page wins
|
||||
(reset! page-title "")))
|
||||
|
||||
;;; History
|
||||
|
||||
(defn use-html5-history []
|
||||
(when reagent/is-client
|
||||
(let [proto (.' js/window :location.protocol)]
|
||||
(and (:allow-html5-history @config)
|
||||
(.isSupported Html5History)
|
||||
(#{"http:" "https:"} proto)))))
|
||||
|
||||
(defn create-history []
|
||||
(if (use-html5-history)
|
||||
(doto (Html5History.)
|
||||
(.setUseFragment false))
|
||||
(History.)))
|
||||
|
||||
(def history nil)
|
||||
|
||||
(defn token-base []
|
||||
(if (use-html5-history)
|
||||
(:base-path @config)))
|
||||
|
||||
(defn setup-history []
|
||||
(when (nil? history)
|
||||
(set! history (create-history))
|
||||
(swap! page-state assoc :has-history (some? history))
|
||||
(when-let [h history]
|
||||
(evt/listen h hevt/NAVIGATE
|
||||
(fn [e]
|
||||
(let [t (.-token e)
|
||||
bp (token-base)]
|
||||
(reset! page (if (and bp (== 0 (.indexOf t bp)))
|
||||
(subs t (count bp))
|
||||
t)))
|
||||
(reagent/flush)))
|
||||
(add-watch page ::history
|
||||
(fn [_ _ oldp newp]
|
||||
(when-not (= oldp newp)
|
||||
(.setToken h (str (token-base) newp)))))
|
||||
(.setEnabled h true))))
|
||||
|
||||
(defn base-path [loc p]
|
||||
;; Find base-path for html5 history
|
||||
(let [split #".[^/]*"
|
||||
depth (->> (case p "" "." p) (re-seq split) count)
|
||||
base (->> loc (re-seq split) (drop-last depth) (apply str))]
|
||||
(string/replace (str base "/") #"^/" "")))
|
||||
|
||||
(defn set-start-page [p]
|
||||
(when (and (not (:base-path @config))
|
||||
(use-html5-history))
|
||||
(swap! config assoc :base-path
|
||||
(base-path (.' js/window -location.pathname) p)))
|
||||
(reset! page p))
|
||||
|
||||
(defn prefix [href]
|
||||
(let [depth (-> #"/" (re-seq @page) count)]
|
||||
(str (->> "../" (repeat depth) (apply str)) href)))
|
||||
|
||||
|
||||
;;; Static site generation
|
||||
|
||||
(defn body []
|
||||
(let [b (:body @config)]
|
||||
(assert (fn? b))
|
||||
[b]))
|
||||
|
||||
(defn danger [t s]
|
||||
[t {:dangerouslySetInnerHTML {:__html s}}])
|
||||
|
||||
(defn html-template [{:keys [title body timestamp page-conf
|
||||
opt-none req]}]
|
||||
(let [c @config
|
||||
base (prefix (str (:js-dir c) "/goog/base.js"))
|
||||
main (str (prefix (:js-file c)) timestamp)
|
||||
css-file (prefix (:css-file c))
|
||||
opt-none (:opt-none c)]
|
||||
(reagent/render-to-static-markup
|
||||
[:html
|
||||
[:head
|
||||
[:meta {:charset "utf-8"}]
|
||||
[:meta {:name 'viewport
|
||||
:content "width=device-width, initial-scale=1.0"}]
|
||||
[:link {:href (str css-file timestamp) :rel 'stylesheet}]
|
||||
[:title title]]
|
||||
[:body
|
||||
(danger :div body)
|
||||
(danger :script (str "var pageConfig = " (-> page-conf
|
||||
clj->js
|
||||
js/JSON.stringify)))
|
||||
(if opt-none
|
||||
[:script {:src base :type "text/javascript"}])
|
||||
[:script {:src main :type "text/javascript"}]
|
||||
(if opt-none
|
||||
(danger :script "goog.require('devsetup');"))]])))
|
||||
|
||||
(defn gen-page [page-name timestamp]
|
||||
(reset! page page-name)
|
||||
(let [b (reagent/render-component-to-string (body))]
|
||||
(str "<!doctype html>"
|
||||
(html-template {:title @page-title
|
||||
:body b
|
||||
:page-conf {:allow-html5-history true
|
||||
:page-name page-name}
|
||||
:timestamp timestamp}))))
|
||||
|
||||
(defn mkdirs [f]
|
||||
(let [fs (js/require "fs")
|
||||
path (js/require "path")
|
||||
items (as-> f _
|
||||
(.' path dirname _)
|
||||
(.' path normalize _)
|
||||
(string/split _ #"/"))
|
||||
parts (reductions #(str %1 "/" %2) items)]
|
||||
(doseq [d parts]
|
||||
(when-not (.' fs existsSync d)
|
||||
(.' fs mkdirSync d)))))
|
||||
|
||||
(defn write-file [f content]
|
||||
(let [fs (js/require "fs")]
|
||||
(mkdirs f)
|
||||
(.' fs writeFileSync f content)))
|
||||
|
||||
(defn read-file [f]
|
||||
(let [fs (js/require "fs")]
|
||||
(.' fs readFileSync f)))
|
||||
|
||||
(defn path-join [& paths]
|
||||
(let [path (js/require "path")]
|
||||
(apply (.' path :join) paths)))
|
||||
|
||||
(defn read-css []
|
||||
(clojure.string/join "\\n"
|
||||
(map read-file (:css-infiles @config))))
|
||||
|
||||
(defn write-resources [dir]
|
||||
(write-file (path-join dir (:css-file @config))
|
||||
(read-css)))
|
||||
|
||||
|
||||
;;; Main entry points
|
||||
|
||||
(defn ^:export genpages [opts]
|
||||
(log "Generating site")
|
||||
(swap! config merge (js->clj opts :keywordize-keys true))
|
||||
(let [dir (:site-dir @config)
|
||||
written (atom #{})
|
||||
timestamp (str "?" (.' js/Date now))
|
||||
one-page (fn [] (first (filter
|
||||
(fn [x] (nil? (@written x)))
|
||||
(keys (:page-map @config)))))]
|
||||
(loop [f (one-page)]
|
||||
(when f
|
||||
(swap! written conj f)
|
||||
(write-file (path-join dir f)
|
||||
(gen-page f timestamp))
|
||||
(recur (one-page))))
|
||||
(write-resources dir))
|
||||
(log "Wrote site"))
|
||||
|
||||
(defn start! [site-config]
|
||||
(swap! config merge site-config)
|
||||
(when reagent/is-client
|
||||
(let [conf (when (exists? js/pageConfig)
|
||||
(js->clj js/pageConfig :keywordize-keys true))
|
||||
page-name (:page-name conf)]
|
||||
(when page-name
|
||||
(set-start-page page-name))
|
||||
(swap! config merge conf)
|
||||
(setup-history)
|
||||
(reagent/render-component (body)
|
||||
(.' js/document :body)))))
|
|
@ -0,0 +1,22 @@
|
|||
(ns devsetup
|
||||
(:require
|
||||
[demo :as site]
|
||||
[runtests]
|
||||
[reagent.core :as r]
|
||||
[figwheel.client :as fw :include-macros true]))
|
||||
|
||||
(defn test! []
|
||||
(runtests/run-tests))
|
||||
|
||||
(defn on-update []
|
||||
(r/force-update-all)
|
||||
(test!))
|
||||
|
||||
(when r/is-client
|
||||
(fw/watch-and-reload
|
||||
:websocket-url "ws://localhost:3449/figwheel-ws"
|
||||
:jsload-callback #(on-update)))
|
||||
|
||||
(demo/start! {:test-results (fn []
|
||||
[runtests/test-output-mini])})
|
||||
(test!)
|
|
@ -0,0 +1,8 @@
|
|||
(ns envsetup
|
||||
(:require [mysite]))
|
||||
|
||||
(mysite/start!)
|
||||
|
||||
(when
|
||||
(exists? js/runtests)
|
||||
(js/runtests.main))
|
|
@ -1,3 +1,4 @@
|
|||
@charset "utf-8";
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
|
|
124
project.clj
124
project.clj
|
@ -3,39 +3,97 @@
|
|||
:url "http://github.com/reagent-project/reagent"
|
||||
:license {:name "MIT"}
|
||||
:description "A simple ClojureScript interface to React"
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.6.0"]
|
||||
[org.clojure/clojurescript "0.0-2342"]]
|
||||
:plugins [[lein-cljsbuild "1.0.3"]
|
||||
[com.cemerick/clojurescript.test "0.3.1"]]
|
||||
:profiles {:dev {:source-paths ["src" "demo"]}
|
||||
:prod {:cljsbuild
|
||||
{:builds
|
||||
{:client {:compiler
|
||||
{:optimizations :advanced
|
||||
:elide-asserts true
|
||||
:pretty-print false}}}}}
|
||||
:test {:cljsbuild
|
||||
{:builds
|
||||
{:client {:source-paths ^:replace
|
||||
["test" "src" "demo"
|
||||
"examples/todomvc/src"
|
||||
"examples/simple/src"
|
||||
"examples/geometry/src"]}}}}
|
||||
:srcmap {:cljsbuild
|
||||
{:builds
|
||||
{:client
|
||||
{:compiler
|
||||
{:source-map "target/cljs-client.js.map"
|
||||
:source-map-path "client"}}}}}}
|
||||
:source-paths ["src"]
|
||||
:plugins [[lein-cljsbuild "1.0.3"]]
|
||||
:resource-paths ["vendor"]
|
||||
:cljsbuild
|
||||
{:builds
|
||||
{:client {:source-paths ["src" "demo" "examples/todomvc/src"
|
||||
"examples/simple/src"
|
||||
"examples/geometry/src"]
|
||||
:notify-command ["node" "./bin/gen-site.js"]
|
||||
:compiler
|
||||
{:output-dir "target/client"
|
||||
:output-to "target/cljs-client.js"
|
||||
:pretty-print true}}}})
|
||||
:source-paths ["src"]
|
||||
|
||||
:profiles {:dev-base {:dependencies
|
||||
[[figwheel "0.1.5-SNAPSHOT"]]
|
||||
:plugins [[lein-figwheel "0.1.5-SNAPSHOT"]]
|
||||
:resource-paths ["site" "outsite"]
|
||||
:figwheel {:css-dirs ["site/public/css"]}
|
||||
:cljsbuild {:builds
|
||||
{:client
|
||||
{:source-paths ["env/dev"]
|
||||
:compiler {:source-map true
|
||||
:optimizations :none
|
||||
:output-dir
|
||||
"outsite/public/js/out"}}}}}
|
||||
|
||||
:site {:resource-paths ^:replace ["outsite"]
|
||||
:figwheel {:css-dirs ^:replace ["outsite/public/css"]}
|
||||
:cljsbuild {:builds
|
||||
{:client
|
||||
{:notify-command
|
||||
["node" "bin/gen-site.js"]}}}}
|
||||
|
||||
:prod [:base :site
|
||||
{:cljsbuild {:builds
|
||||
{:client
|
||||
{:source-paths ["env/prod"]
|
||||
:compiler {:optimizations :advanced
|
||||
:elide-asserts true
|
||||
:output-dir "target/client"}}}}}]
|
||||
|
||||
:test {:dependencies [[com.cemerick/clojurescript.test "0.3.1"]]
|
||||
:cljsbuild {:builds
|
||||
{:client {:source-paths ["test"]}}}}
|
||||
|
||||
:dev [:dev-base :test]
|
||||
:prod-test [:prod :test]}
|
||||
|
||||
:clean-targets ^{:protect false} [:target-path :compile-path
|
||||
"outsite/public/js"
|
||||
"outsite/public/site"
|
||||
"outsite/public/news"
|
||||
"outsite/public/index.html"
|
||||
"out"]
|
||||
|
||||
:cljsbuild {:builds
|
||||
{:client {:source-paths ["src"
|
||||
"demo"
|
||||
"examples/todomvc/src"
|
||||
"examples/simple/src"
|
||||
"examples/geometry/src"]
|
||||
:compiler
|
||||
{:output-to "outsite/public/js/main.js"}}}}
|
||||
|
||||
:figwheel {:http-server-root "public" ;; assumes "resources"
|
||||
:server-port 3449}
|
||||
|
||||
;-------------------
|
||||
|
||||
;; :profiles {:dev {:source-paths ["src" "demo"]}
|
||||
;; :prod {:cljsbuild
|
||||
;; {:builds
|
||||
;; {:client {:compiler
|
||||
;; {:optimizations :advanced
|
||||
;; :elide-asserts true
|
||||
;; :pretty-print false}}}}}
|
||||
;; :test {:cljsbuild
|
||||
;; {:builds
|
||||
;; {:client {:source-paths ^:replace
|
||||
;; ["test" "src" "demo"
|
||||
;; "examples/todomvc/src"
|
||||
;; "examples/simple/src"
|
||||
;; "examples/geometry/src"]}}}}
|
||||
;; :srcmap {:cljsbuild
|
||||
;; {:builds
|
||||
;; {:client
|
||||
;; {:compiler
|
||||
;; {:source-map "target/cljs-client.js.map"
|
||||
;; :source-map-path "client"}}}}}}
|
||||
;; :cljsbuild
|
||||
;; {:builds
|
||||
;; {:client {:source-paths ["src" "demo" "examples/todomvc/src"
|
||||
;; "examples/simple/src"
|
||||
;; "examples/geometry/src"]
|
||||
;; :notify-command ["node" "./bin/gen-site.js"]
|
||||
;; :compiler
|
||||
;; {:output-dir "target/client"
|
||||
;; :output-to "target/cljs-client.js"
|
||||
;; :pretty-print true}}}}
|
||||
)
|
||||
|
|
|
@ -0,0 +1,579 @@
|
|||
@charset "utf-8";
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
font-size: 100%;
|
||||
vertical-align: baseline;
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
-o-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
body {
|
||||
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
line-height: 1.4em;
|
||||
background: #eaeaea;
|
||||
/* background: #eaeaea url('bg.png'); */
|
||||
color: #4d4d4d;
|
||||
width: 550px;
|
||||
margin: 0 auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-font-smoothing: antialiased;
|
||||
-ms-font-smoothing: antialiased;
|
||||
-o-font-smoothing: antialiased;
|
||||
font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="checkbox"] {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#todoapp {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
margin: 130px 0 40px 0;
|
||||
border: 1px solid #ccc;
|
||||
position: relative;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
|
||||
0 25px 50px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
#todoapp:before {
|
||||
content: '';
|
||||
border-left: 1px solid #f5d6d6;
|
||||
border-right: 1px solid #f5d6d6;
|
||||
width: 2px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 40px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#todoapp input::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
#todoapp input::-moz-placeholder {
|
||||
font-style: italic;
|
||||
color: #a9a9a9;
|
||||
}
|
||||
|
||||
#todoapp h1 {
|
||||
position: absolute;
|
||||
top: -120px;
|
||||
width: 100%;
|
||||
font-size: 70px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: #b3b3b3;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
|
||||
-webkit-text-rendering: optimizeLegibility;
|
||||
-moz-text-rendering: optimizeLegibility;
|
||||
-ms-text-rendering: optimizeLegibility;
|
||||
-o-text-rendering: optimizeLegibility;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
#header {
|
||||
padding-top: 15px;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
#header:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 15px;
|
||||
z-index: 2;
|
||||
border-bottom: 1px solid #6c615c;
|
||||
background: #8d7d77;
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
|
||||
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
|
||||
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
|
||||
border-top-left-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
}
|
||||
|
||||
#new-todo,
|
||||
.edit {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
font-size: 24px;
|
||||
font-family: inherit;
|
||||
line-height: 1.4em;
|
||||
border: 0;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
padding: 6px;
|
||||
border: 1px solid #999;
|
||||
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-font-smoothing: antialiased;
|
||||
-ms-font-smoothing: antialiased;
|
||||
-o-font-smoothing: antialiased;
|
||||
font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#new-todo {
|
||||
padding: 16px 16px 16px 60px;
|
||||
border: none;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
z-index: 2;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#main {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
border-top: 1px dotted #adadad;
|
||||
}
|
||||
|
||||
label[for='toggle-all'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toggle-all {
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
left: -4px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
/* Mobile Safari */
|
||||
border: none;
|
||||
}
|
||||
|
||||
#toggle-all:before {
|
||||
content: '»';
|
||||
font-size: 28px;
|
||||
color: #d9d9d9;
|
||||
padding: 0 25px 7px;
|
||||
}
|
||||
|
||||
#toggle-all:checked:before {
|
||||
color: #737373;
|
||||
}
|
||||
|
||||
#todo-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#todo-list li {
|
||||
position: relative;
|
||||
font-size: 24px;
|
||||
border-bottom: 1px dotted #ccc;
|
||||
}
|
||||
|
||||
#todo-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#todo-list li.editing {
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#todo-list li.editing .edit {
|
||||
display: block;
|
||||
width: 506px;
|
||||
padding: 13px 17px 12px 17px;
|
||||
margin: 0 0 0 43px;
|
||||
}
|
||||
|
||||
#todo-list li.editing .view {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#todo-list li .toggle {
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
/* auto, since non-WebKit browsers doesn't support input styling */
|
||||
height: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto 0;
|
||||
/* Mobile Safari */
|
||||
border: none;
|
||||
-webkit-appearance: none;
|
||||
-ms-appearance: none;
|
||||
-o-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
#todo-list li .toggle:after {
|
||||
content: '✔';
|
||||
/* 40 + a couple of pixels visual adjustment */
|
||||
line-height: 43px;
|
||||
font-size: 20px;
|
||||
color: #d9d9d9;
|
||||
text-shadow: 0 -1px 0 #bfbfbf;
|
||||
}
|
||||
|
||||
#todo-list li .toggle:checked:after {
|
||||
color: #85ada7;
|
||||
text-shadow: 0 1px 0 #669991;
|
||||
bottom: 1px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#todo-list li label {
|
||||
white-space: pre;
|
||||
word-break: break-word;
|
||||
padding: 15px 60px 15px 15px;
|
||||
margin-left: 45px;
|
||||
display: block;
|
||||
line-height: 1.2;
|
||||
-webkit-transition: color 0.4s;
|
||||
transition: color 0.4s;
|
||||
}
|
||||
|
||||
#todo-list li.completed label {
|
||||
color: #a9a9a9;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
#todo-list li .destroy {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
bottom: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: auto 0;
|
||||
font-size: 22px;
|
||||
color: #a88a8a;
|
||||
-webkit-transition: all 0.2s;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
#todo-list li .destroy:hover {
|
||||
text-shadow: 0 0 1px #000,
|
||||
0 0 10px rgba(199, 107, 107, 0.8);
|
||||
-webkit-transform: scale(1.3);
|
||||
-ms-transform: scale(1.3);
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
#todo-list li .destroy:after {
|
||||
content: '✖';
|
||||
}
|
||||
|
||||
#todo-list li:hover .destroy {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#todo-list li .edit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#todo-list li.editing:last-child {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
color: #777;
|
||||
padding: 0 15px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -31px;
|
||||
left: 0;
|
||||
height: 20px;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#footer:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 31px;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
z-index: -1;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
|
||||
0 6px 0 -3px rgba(255, 255, 255, 0.8),
|
||||
0 7px 1px -3px rgba(0, 0, 0, 0.3),
|
||||
0 43px 0 -6px rgba(255, 255, 255, 0.8),
|
||||
0 44px 2px -6px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#todo-count {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#filters {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#filters li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#filters li a {
|
||||
color: #83756f;
|
||||
margin: 2px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#filters li a.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#clear-completed {
|
||||
float: right;
|
||||
position: relative;
|
||||
line-height: 20px;
|
||||
text-decoration: none;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
font-size: 11px;
|
||||
padding: 0 10px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#clear-completed:hover {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#info {
|
||||
margin: 65px auto 0;
|
||||
color: #a6a6a6;
|
||||
font-size: 12px;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#info a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Hack to remove background from Mobile Safari.
|
||||
Can't use it globally since it destroys checkboxes in Firefox and Opera
|
||||
*/
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
#toggle-all,
|
||||
#todo-list li .toggle {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#todo-list li .toggle {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#toggle-all {
|
||||
top: -56px;
|
||||
left: -15px;
|
||||
width: 65px;
|
||||
height: 41px;
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 20px 0;
|
||||
border: 0;
|
||||
border-top: 1px dashed #C5C5C5;
|
||||
border-bottom: 1px dashed #F7F7F7;
|
||||
}
|
||||
|
||||
.learn a {
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
color: #b83f45;
|
||||
}
|
||||
|
||||
.learn a:hover {
|
||||
text-decoration: underline;
|
||||
color: #787e7e;
|
||||
}
|
||||
|
||||
.learn h3,
|
||||
.learn h4,
|
||||
.learn h5 {
|
||||
margin: 10px 0;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.learn h3 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.learn h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.learn h5 {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.learn ul {
|
||||
padding: 0;
|
||||
margin: 0 0 30px 25px;
|
||||
}
|
||||
|
||||
.learn li {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.learn p {
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
line-height: 1.3;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.quote {
|
||||
border: none;
|
||||
margin: 20px 0 60px 0;
|
||||
}
|
||||
|
||||
.quote p {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.quote p:before {
|
||||
content: '“';
|
||||
font-size: 50px;
|
||||
opacity: .15;
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
.quote p:after {
|
||||
content: '”';
|
||||
font-size: 50px;
|
||||
opacity: .15;
|
||||
position: absolute;
|
||||
bottom: -42px;
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
.quote footer {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.quote footer img {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.quote footer a {
|
||||
margin-left: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.speech-bubble {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, .04);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.speech-bubble:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 30px;
|
||||
border: 13px solid transparent;
|
||||
border-top-color: rgba(0, 0, 0, .04);
|
||||
}
|
||||
|
||||
.learn-bar > .learn {
|
||||
position: absolute;
|
||||
width: 272px;
|
||||
top: 8px;
|
||||
left: -300px;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(255, 255, 255, .6);
|
||||
-webkit-transition-property: left;
|
||||
transition-property: left;
|
||||
-webkit-transition-duration: 500ms;
|
||||
transition-duration: 500ms;
|
||||
}
|
||||
|
||||
@media (min-width: 899px) {
|
||||
.learn-bar {
|
||||
width: auto;
|
||||
margin: 0 0 0 300px;
|
||||
}
|
||||
|
||||
.learn-bar > .learn {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.learn-bar #todoapp {
|
||||
width: 550px;
|
||||
margin: 130px auto 40px auto;
|
||||
}
|
||||
}
|
||||
div, h1, input {
|
||||
font-family: HelveticaNeue, Helvetica;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.example-clock {
|
||||
font-size: 128px;
|
||||
line-height: 1.2em;
|
||||
font-family: HelveticaNeue-UltraLight, Helvetica;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.example-clock {
|
||||
font-size: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.color-input, .color-input input {
|
||||
font-size: 24px;
|
||||
line-height: 1.5em;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testing reagent</title>
|
||||
<link rel="stylesheet" href="../examples/todomvc/todos.css">
|
||||
<link rel="stylesheet" href="../examples/todomvc/todosanim.css">
|
||||
<link rel="stylesheet" href="../examples/simple/example.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<style type="text/css">
|
||||
.runtests { margin-bottom: 400px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>This will become an example when compiled</h1>
|
||||
<script type="text/javascript" src="../target/cljs-client.js"></script>
|
||||
<script type="text/javascript">
|
||||
if (typeof runtests !== 'undefined') {
|
||||
runtests.mounttests();
|
||||
} else {
|
||||
demo.mountdemo();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,18 +1,26 @@
|
|||
|
||||
(ns runtests
|
||||
(:require [reagent.core :as reagent :refer [atom]]
|
||||
[reagent.interop :refer-macros [.' .! fvar]]
|
||||
[reagent.interop :refer-macros [.' .!]]
|
||||
[reagent.debug :refer-macros [dbg println]]
|
||||
[demo :as demo]
|
||||
[cemerick.cljs.test :as t]))
|
||||
[cemerick.cljs.test :as t]
|
||||
[testreagent]
|
||||
[testcursor]
|
||||
[testinterop]
|
||||
[testratom]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
(def test-results (atom nil))
|
||||
|
||||
(def test-box {:position 'absolute
|
||||
:margin-left -35
|
||||
:color :#aaa})
|
||||
|
||||
(defn test-output []
|
||||
(let [res @test-results]
|
||||
[:div {:style {:margin-top "40px"}}
|
||||
[:div {:style test-box}
|
||||
(if-not res
|
||||
[:div "waiting for tests to run"]
|
||||
[:div
|
||||
|
@ -25,9 +33,10 @@
|
|||
(let [res @test-results]
|
||||
(if res
|
||||
(if (zero? (+ (:fail res) (:error res)))
|
||||
[:div "Tests ok"]
|
||||
[:div {:style test-box}
|
||||
"All tests ok"]
|
||||
[test-output])
|
||||
[:div "."])))
|
||||
[:div {:style test-box} "testing"])))
|
||||
|
||||
(defn test-demo []
|
||||
[:div
|
||||
|
@ -48,8 +57,9 @@
|
|||
(reset! test-results {:error e}))))
|
||||
(println "-----------------------------------------"))
|
||||
|
||||
(if reagent/is-client
|
||||
(do
|
||||
(reset! test-results nil)
|
||||
(js/setTimeout run-all-tests 1000))
|
||||
(run-all-tests))
|
||||
(defn run-tests []
|
||||
(if reagent/is-client
|
||||
(do
|
||||
(reset! test-results nil)
|
||||
(js/setTimeout run-all-tests 100))
|
||||
(run-all-tests)))
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
(ns simpletest
|
||||
(:require-macros [cemerick.cljs.test
|
||||
:refer (is deftest with-test run-tests testing)])
|
||||
(:require [cemerick.cljs.test :as t]))
|
||||
|
||||
;; (deftest somewhat-less-wat
|
||||
;; (is (= "{}[]" (+ {} []))))
|
||||
|
||||
(deftest javascript-allows-div0
|
||||
(is (= js/Infinity (/ 1 0) (/ (int 1) (int 0)))))
|
||||
|
||||
;; (with-test
|
||||
;; (defn pennies->dollar-string
|
||||
;; [pennies]
|
||||
;; {:pre [(integer? pennies)]}
|
||||
;; (str "$" (int (/ pennies 100)) "." (mod pennies 100)))
|
||||
;; (testing "assertions are nice"
|
||||
;; (is (thrown-with-msg? js/Error #"integer?" (pennies->dollar-string 564.2)))))
|
Loading…
Reference in New Issue