├── doc
├── common
│ ├── locale-1.adoc
│ ├── component-1.adoc
│ ├── form-4.adoc
│ ├── form-2.adoc
│ ├── locale-3.adoc
│ ├── data-props.adoc
│ ├── return-values.adoc
│ ├── form-1.adoc
│ ├── locale-2.adoc
│ └── form-3.adoc
├── Makefile
└── content.adoc
├── .gitignore
├── test
└── antizer
│ └── core_test.clj
├── examples
├── src
│ └── antizer_examples
│ │ ├── app.cljs
│ │ ├── common.cljs
│ │ ├── rum.cljs
│ │ └── reagent.cljs
└── resources
│ ├── rum.html
│ ├── reagent.html
│ └── css
│ └── style.css
├── project.clj
├── profiles.clj
├── src
└── antizer
│ ├── reagent.cljs
│ ├── core.cljs
│ ├── macros.clj
│ ├── rum.cljs
│ └── antd.clj
├── README.md
└── LICENSE
/doc/common/locale-1.adoc:
--------------------------------------------------------------------------------
1 | Antizer supports the use of locale provider to provide localization / i18n support.
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /doc/dist/
11 | examples/resources/js
12 | .hgignore
13 | .hg/
14 | .DS_Store
--------------------------------------------------------------------------------
/doc/common/component-1.adoc:
--------------------------------------------------------------------------------
1 | All the properties that are passed in can be in kebab-case.
2 |
3 | include::data-props.adoc[tags=warning1;text;warning2]
4 |
5 | include::return-values.adoc[tags=note1;text;note2]
6 |
--------------------------------------------------------------------------------
/test/antizer/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns antizer.core-test
2 | (:require [clojure.test :refer :all]
3 | [antizer.core :refer :all]))
4 |
5 | (deftest a-test
6 | (testing "FIXME, I fail."
7 | (is (= 0 1))))
8 |
--------------------------------------------------------------------------------
/doc/common/form-4.adoc:
--------------------------------------------------------------------------------
1 | The form functions (eg: `reset-fields`, `validate-fields`) correspond to the form functions which
2 | can be found https://ant.design/components/form/#Form.create(options)[here].
3 |
4 | include::return-values.adoc[]
5 |
--------------------------------------------------------------------------------
/doc/common/form-2.adoc:
--------------------------------------------------------------------------------
1 | The initial step involves calling `(create-form)` with the form component
2 | to be rendered.
3 |
4 | Within the form component the `form` object should be obtained
5 | via `(get-form)`, which obtains the `form` object from the React
6 | component's property. This should be done only after the component has been constructed.
7 |
--------------------------------------------------------------------------------
/examples/src/antizer_examples/app.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer-examples.app
2 | (:require [antizer-examples.reagent :as reagent]
3 | [antizer-examples.rum :as rum]
4 | [cljsjs.moment]))
5 |
6 | (defn init! []
7 | (case js/example
8 | "reagent" (reagent/init!)
9 | "rum" (rum/init!)))
10 |
11 | (enable-console-print!)
12 | (init!)
13 |
--------------------------------------------------------------------------------
/doc/common/locale-3.adoc:
--------------------------------------------------------------------------------
1 | Note that each locale to be used has to be included using `require` seperately.
2 |
3 | IMPORTANT: Due to how the moment.js library is being integrated with Ant Design, the DatePicker and Calendar components must be initialized in a certain way for the text to be displayed in the correct language. Please refer to https://github.com/priornix/antizer#known-issues[this] for the workaround.
--------------------------------------------------------------------------------
/doc/common/data-props.adoc:
--------------------------------------------------------------------------------
1 | // tag::warning1[]
2 | [WARNING]
3 | ====
4 | // end::warning1[]
5 |
6 | // tag::text[]
7 | Any properties that begins with `data` has to be in camel case instead of kebab case, as they will be treated as html attributes. Thus, *DO NOT* use `data-index` and `data-source` as they would not work, use `dataIndex` and `dataSource` instead.
8 | // end::text[]
9 |
10 | // tag::warning2[]
11 | ====
12 | // end::warning2[]
13 |
--------------------------------------------------------------------------------
/doc/common/return-values.adoc:
--------------------------------------------------------------------------------
1 | // tag::note1[]
2 | [NOTE]
3 | ====
4 | // end::note1[]
5 |
6 | // tag::text[]
7 | All return values from the callback functions of the Ant Design components and the form functions
8 | will be in the form of JavaScript objects. Please use `+++(js->clj)+++` to convert them before
9 | any ClojureScript operations are to be performed on them.
10 | // end::text[]
11 |
12 | // tag::note2[]
13 | ====
14 | // end::note2[]
15 |
--------------------------------------------------------------------------------
/doc/common/form-1.adoc:
--------------------------------------------------------------------------------
1 | ===== Form creation
2 | Antizer supports the use of https://ant.design/components/form/#Form.create(options)[Form.create()] which helps to collect and validate the form data automatically. If you prefer to do this manually or prefer using another validation library, you can skip the section below and use the the Form API found https://ant.design/components/form/#API[here].
3 |
4 | The following is an example of how to use Antizer's implementation of `Form.create()`:
--------------------------------------------------------------------------------
/doc/Makefile:
--------------------------------------------------------------------------------
1 | # Borrowed from https://github.com/funcool/buddy-auth/blob/master/doc/Makefile
2 | all: doc
3 |
4 | api:
5 | cd ..
6 | lein codox
7 |
8 | clean:
9 | rm -rf dist
10 |
11 | doc:
12 | asciidoctor -o dist/latest/index.html content.adoc
13 |
14 | examples:
15 | cd .. && rsync -av --progress examples/resources/ doc/dist/latest/examples --exclude js/
16 | lein with-profile +examples-dist cljsbuild once
17 |
18 | github:
19 | ghp-import -m "Generate documentation" -b gh-pages dist/
20 | git push origin gh-pages
21 |
--------------------------------------------------------------------------------
/doc/common/locale-2.adoc:
--------------------------------------------------------------------------------
1 | It is recommended to place the locale provider at the top level of the application.
2 | The whole list of supported locales can be found https://ant.design/docs/react/i18n#LocaleProvider[here].
3 |
4 | To add a new language, please refer to https://ant.design/components/locale-provider/#Add-a-new-language[this].
5 |
6 | ===== Moment.js
7 | Some Ant Design components (eg: `calendar`, `date-picker`,
8 | `date-picker-range-picker`) uses the Moment.js library to display
9 | the dates in the correct language.
10 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject antizer "0.2.2"
2 | :description "Antizer"
3 | :url "https://github.com/priornix/antizer"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.8.0"]
7 | [org.clojure/clojurescript "1.9.229"]
8 | [cljsjs/antd "2.12.3-0"]
9 | [cljsjs/moment "2.17.1-1"]]
10 | :plugins [[lein-codox "0.10.3"]]
11 | :codox {:language :clojurescript
12 | :metadata {:doc/format :markdown}
13 | :output-path "doc/dist/latest/api"
14 | :namespaces [antizer.core antizer.reagent antizer.rum]})
15 |
--------------------------------------------------------------------------------
/examples/resources/rum.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rum Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/examples/resources/reagent.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Reagent Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/doc/common/form-3.adoc:
--------------------------------------------------------------------------------
1 | ===== Field decoration
2 | `(decorate-field)` is used to add automatic data capture and validation features
3 | to each form field. This function accepts the following parameters:
4 |
5 | [source,clojure]
6 | ----
7 | (decorate-field form identifier [options] form-field)
8 | ----
9 |
10 | It corresponds to the
11 | https://ant.design/components/form/#this.props.form.getFieldDecorator(id,-options)[getFieldDecorator]
12 | function. For an explaination of the parameters, please refer to the
13 | https://priornix.github.io/antizer/latest/api/[API documentation],
14 | the Ant Design
15 | https://ant.design/components/form/#this.props.form.getFieldDecorator(id,-options)[Form page]
16 | and the examples provided.
17 |
18 | ===== Form functions
19 | After the form has been obtained via `(get-form)`, the form functions can be used.
20 |
21 | An example of the usage of these functions is shown here:
22 |
--------------------------------------------------------------------------------
/examples/resources/css/style.css:
--------------------------------------------------------------------------------
1 | .banner {
2 | color: #fff;
3 | background-color: #108ee9;
4 | }
5 |
6 | .banner-header {
7 | color: #fff;
8 | }
9 |
10 | .banner-logo {
11 | position: relative;
12 | top: 5px;
13 | font-size: 24px;
14 | }
15 |
16 | .banner a {
17 | color: #fff;
18 | }
19 |
20 | .content-area {
21 | min-height: 800px;
22 | background: #fff;
23 | padding: 20px 20px
24 | }
25 |
26 | .box {
27 | border: 1px solid #e9e9e9;
28 | border-radius: 3px;
29 | margin-bottom: 15px;
30 | }
31 |
32 | .box-content {
33 | padding: 7px 15px 15px;
34 | }
35 |
36 | .card {
37 | height: 100px
38 | }
39 |
40 | .card-photo {
41 | height: 180px
42 | }
43 |
44 | .example-button .ant-btn {
45 | margin-right: 1.5%;
46 | }
47 |
48 | .progress .ant-btn-group {
49 | margin: 0 3%;
50 | }
51 |
52 | .va-middle {
53 | vertical-align: middle;
54 | }
55 |
56 | .avatar h2 {
57 | margin-bottom: 10px;
58 | }
59 |
60 | .avatar span {
61 | margin-right: 3%;
62 | }
--------------------------------------------------------------------------------
/profiles.clj:
--------------------------------------------------------------------------------
1 | {:examples
2 | {:dependencies [[reagent "0.6.1"]
3 | [rum "0.10.8"]]
4 | :plugins [[lein-cljsbuild "1.1.5"]]
5 | :resource-paths ["examples/resources"]
6 | :source-paths ["examples/src"]
7 | :main ^:skip-aot antizer-examples.server
8 | :cljsbuild
9 | {:builds
10 | {:app
11 | {:source-paths ["examples/src"]
12 | :compiler
13 | {:main "antizer-examples.app"
14 | :asset-path "js/out"
15 | :output-dir "examples/resources/js/out"
16 | :output-to "examples/resources/js/app.js"
17 | :pretty-print false
18 | :optimizations :advanced}}}}}
19 |
20 | :examples-dist
21 | [:examples
22 | {:cljsbuild
23 | {:builds
24 | {:app
25 | {:compiler
26 | {:output-to "doc/dist/latest/examples/js/app.js"
27 | :output-dir "doc/dist/latest/examples/js/out"}}}}}]
28 |
29 | :examples-dev
30 | [:examples
31 | {:dependencies [[figwheel-sidecar "0.5.10"]]
32 | :plugins [[lein-figwheel "0.5.10"]]
33 | :figwheel {:css-dirs ["examples/resources/css"]}
34 | :cljsbuild
35 | {:builds
36 | {:app
37 | {:figwheel true
38 | :compiler
39 | {:optimizations :none
40 | :pretty-print true}}}}}]}
41 |
--------------------------------------------------------------------------------
/examples/src/antizer_examples/common.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer-examples.common)
2 |
3 | (def form-style {:label-col {:span 6}
4 | :wrapper-col {:span 13}})
5 |
6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 | ;;
8 | ;; Definitions and functions for the datatable
9 | ;;
10 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 | (def pagination {:show-size-changer true
12 | :page-size-options ["5" "10" "20"]
13 | :show-total #(str "Total: " % " users")})
14 |
15 | (defn comparison [data1 data2 field]
16 | (compare (get (js->clj data1 :keywordize-keys true) field)
17 | (get (js->clj data2 :keywordize-keys true) field)))
18 |
19 | ;; we need to use dataIndex instead of data-index, see README.md
20 | (def columns [{:title "Name" :dataIndex "name" :sorter #(comparison %1 %2 :name)}
21 | {:title "Age" :dataIndex "age" :sorter #(comparison %1 %2 :age)}
22 | {:title "Address" :dataIndex "address" :sorter #(comparison %1 %2 :address)}])
23 |
24 | (def people [{:id 1 :name "Tracey Davidson" :age 43 :address "5512 Pockrus Page Rd"}
25 | {:id 2 :name "Pierre de Wiles" :age 41 :address "358 Fermat's St"}
26 | {:id 3 :name "Lydia Weaver" :age 23 :address "1251 Fourth St"}
27 | {:id 4 :name "Willie Reynolds" :age 26 :address "2984 Beechcrest Rd"}
28 | {:id 5 :name "Richard Perelman" :age 51 :address "2003 Poincaré Ricci Rd"}
29 | {:id 6 :name "Srinivasa Ramanujan" :age 32 :address "1729 Taxi Cab St"}
30 | {:id 7 :name "Zoe Cruz" :age 31 :address "8593 Pine Rd"}
31 | {:id 8 :name "Adam Turing" :age 41 :address "1936 Automata Lane"}])
32 |
33 | (defn set-locale [country locale-atom]
34 | (let [locale-val (if (= country "zh_CN")
35 | nil country)]
36 | (reset! locale-atom locale-val)))
37 |
38 |
--------------------------------------------------------------------------------
/src/antizer/reagent.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer.reagent
2 | (:require [antizer.core :as ant]
3 | [goog.object :refer [getValueByKeys]]
4 | [reagent.core :as r])
5 | (:require-macros [antizer.macros :refer [export-funcs export-props export-form-funcs
6 | export-reagent-components]]))
7 |
8 | (defn create-form
9 | "Calls Form.create() decorator with the form to be created. form can be
10 | any hiccup form. Accepts the following options:
11 |
12 | :options - map of Form.create() options. Refer to:
13 | https://ant.design/components/form/#Form.create(options) for
14 | details"
15 | [form & {:keys [options] :or {options {}}}]
16 | (r/create-element
17 | (((getValueByKeys js/antd "Form" "create") (clj->js (ant/map-keys->camel-case options)))
18 | (r/reactify-component form))))
19 |
20 | (defn get-form
21 | "Returns the `form` created by Form.create(). This function must be called
22 | from within the `form` component"
23 | []
24 | (-> (r/current-component)
25 | (r/props)
26 | (js->clj :keywordize-keys true)
27 | (:form)))
28 |
29 | (defn decorate-field
30 | "Decorate a form field, corresponds to antd's getFieldDecorator() function
31 | Arguments:
32 |
33 | * form - the `form` object, obtained from `(get-form)`
34 | * id - field identifier, supports nested fields format in string format
35 | * options - the validation options for the field
36 | * field - the input field element that the validation will be applied to
37 |
38 | For more information about the validation options, please refer to:
39 | https://ant.design/components/form/#getFieldDecorator(id,-options)-parameters"
40 | ([form id field] (decorate-field form id {} field))
41 | ([form id options field]
42 | (let [field-decorator (:getFieldDecorator form)
43 | params (clj->js (ant/map-keys->camel-case options))]
44 | ((field-decorator id params) (r/as-element field)))))
45 |
46 | (export-form-funcs)
47 | (export-funcs)
48 | (export-props)
49 | (export-reagent-components)
50 |
--------------------------------------------------------------------------------
/src/antizer/core.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer.core
2 | (:require [clojure.string :as s]
3 | [clojure.set :refer [rename-keys]]
4 | [clojure.walk :as w]
5 | [goog.object :refer [getValueByKeys]]
6 | [cljsjs.antd]))
7 |
8 | (def antd-module js/antd)
9 |
10 | (defn kebab-case->camel-case
11 | "Converts from kebab case to camel case, eg: on-click to onClick"
12 | [input]
13 | (let [words (s/split input #"-")
14 | capitalize (->> (rest words)
15 | (map #(apply str (s/upper-case (first %)) (rest %))))]
16 | (apply str (first words) capitalize)))
17 |
18 | (defn map-keys->camel-case
19 | "Stringifys all the keys of a cljs hashmap and converts them
20 | from kebab case to camel case. If :html-props option is specified,
21 | then rename the html properties values to their dom equivalent
22 | before conversion"
23 | [data & {:keys [html-props]}]
24 | (let [convert-to-camel (fn [[key value]]
25 | [(kebab-case->camel-case (name key)) value])]
26 | (w/postwalk (fn [x]
27 | (if (map? x)
28 | (let [new-map (if html-props
29 | (rename-keys x {:class :className :for :htmlFor})
30 | x)]
31 | (into {} (map convert-to-camel new-map)))
32 | x))
33 | data)))
34 |
35 | (defn get-module-path [module-name]
36 | (s/split module-name #"\."))
37 |
38 | (defn get-prop
39 | "Retreives the value of the module's property"
40 | [module-name prop]
41 | (when prop
42 | (apply getValueByKeys antd-module (conj (get-module-path module-name) (name prop)))))
43 |
44 | (defn call-js-func
45 | "Calls a javascript function, converting the keys for any arguments
46 | that are hashmaps from kebab case to camel case
47 |
48 | * func - the native javascript to be called"
49 | [func & args]
50 | (apply func (clj->js (map map-keys->camel-case args))))
51 |
52 | (defn call-func
53 | "Calls the ant module function"
54 | [module-name & args]
55 | (let [path (get-module-path module-name)
56 | func (apply getValueByKeys antd-module path)]
57 | (apply call-js-func func args)))
58 |
--------------------------------------------------------------------------------
/src/antizer/macros.clj:
--------------------------------------------------------------------------------
1 | (ns antizer.macros
2 | (:require [clojure.string :as s]
3 | [antizer.antd :as antd]))
4 |
5 | (def antd-module 'js/antd)
6 |
7 | (defn module-name->kebab-case
8 | "Converts module and sub module names from camel case to kebab case
9 | eg: DatePicker to date-picker or Menu.Item to menu-item"
10 | [input]
11 | (->> (re-seq #"\w[a-z0-9]*" input)
12 | (map s/lower-case)
13 | (s/join "-")))
14 |
15 | (def get-symbol-name (comp symbol module-name->kebab-case))
16 |
17 | (defn get-module-path [module-name]
18 | (s/split module-name #"\."))
19 |
20 | (defn define-fn [fn-name]
21 | (let [fn-name (name fn-name)]
22 | `(defn ~(get-symbol-name fn-name) [& args#]
23 | (apply antizer.core/call-func ~fn-name args#))))
24 |
25 | (defn define-prop [prop-name]
26 | (let [prop-name (name prop-name)]
27 | `(defn ~(get-symbol-name prop-name) [prop#]
28 | (antizer.core/get-prop ~prop-name prop#))))
29 |
30 | (defn define-form-funcs [method-name]
31 | (let [method-name (name method-name)]
32 | `(defn ~(get-symbol-name method-name) [form# & args#]
33 | (apply antizer.core/call-js-func ((keyword ~method-name) form#) args#))))
34 |
35 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
36 | ;;
37 | ;; Functions for the different react libraries
38 | ;;
39 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40 | (defn define-reagent-component [component]
41 | (let [component (name component)]
42 | `(def ~(get-symbol-name component)
43 | (reagent.core/adapt-react-class
44 | (apply goog.object/getValueByKeys
45 | ~antd-module ~(get-module-path component))))))
46 |
47 | (defn define-rum-component [component]
48 | (let [component (name component)]
49 | `(defn ~(get-symbol-name component) [& args#]
50 | (apply antizer.rum/adapt-class
51 | (apply goog.object/getValueByKeys
52 | ~antd-module ~(get-module-path component)) args#))))
53 |
54 | (defmacro export-funcs []
55 | `(do ~@(map define-fn antd/funcs)))
56 |
57 | (defmacro export-props []
58 | `(do ~@(map define-prop antd/props)))
59 |
60 | (defmacro export-form-funcs []
61 | `(do ~@(map define-form-funcs antd/form-funcs)))
62 |
63 | (defmacro export-reagent-components []
64 | `(do ~@(map define-reagent-component antd/components)))
65 |
66 | (defmacro export-rum-components []
67 | `(do ~@(map define-rum-component antd/components)))
68 |
69 | ; (defn define-component [component]
70 | ; `(defn ~(get-symbol-name component) [& args#]
71 | ; (apply antizer.core/create-component ~component args#)))
72 |
73 | ; (defmacro export-antd-components []
74 | ; `(do ~@(map define-component antd/components)))
75 |
--------------------------------------------------------------------------------
/src/antizer/rum.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer.rum
2 | (:require [antizer.core :as ant]
3 | [goog.object :refer [getValueByKeys]]
4 | [rum.core])
5 | (:require-macros [antizer.macros
6 | :refer [export-form-funcs export-funcs
7 | export-props export-rum-components]]))
8 |
9 | ;; adapted from https://github.com/tonsky/rum/issues/20
10 | (defn adapt-class [react-class & args]
11 | (let [[opts children] (if (map? (first args))
12 | [(first args) (rest args)]
13 | [{} args])
14 | type# (first children)
15 | ;; we have to make sure to check if the children is sequential
16 | ;; as a list can be returned, eg: from a (for)
17 | new-children (if (sequential? type#)
18 | [(sablono.interpreter/interpret children)]
19 | children)
20 | ;; convert any options key value to a react element, if
21 | ;; a valid html element tag is used, using sablono
22 | vector->react-elems (fn [[key val]]
23 | (if (sequential? val)
24 | [key (sablono.interpreter/interpret val)]
25 | [key val]))
26 | new-options (into {} (map vector->react-elems opts))]
27 | (apply js/React.createElement react-class
28 | ;; sablono html-to-dom-attrs does not work for nested hashmaps
29 | (clj->js (ant/map-keys->camel-case new-options :html-props true))
30 | new-children)))
31 |
32 | (defn create-form
33 | "Calls Form.create() wrapper with the form to be created. `form` should
34 | be a `(rum.core/defcs)` component. Accepts the following options:
35 |
36 | * :options - map of Form.create() options. Refer to:
37 | https://ant.design/components/form/#Form.create(options) for
38 | details
39 | * :props - the properties hashmap to be passed to the component during the `:init`
40 | stage. Note that the recieved properties will be in the form of a
41 | JavaScript associative map"
42 | [form & {:keys [options props] :or {options {} props {}}}]
43 | (js/React.createElement
44 | (((getValueByKeys js/antd "Form" "create") (clj->js (ant/map-keys->camel-case options)))
45 | (:rum/class (meta form))) (clj->js props)))
46 |
47 | (defn get-form
48 | "Returns the `form` created by Form.create(). This function must be called
49 | from within the `(rum.core/defcs)` component, while passing in the component's
50 | `state`"
51 | [state]
52 | (-> (getValueByKeys (:rum/react-component state) "props")
53 | (js->clj :keywordize-keys true)
54 | :form))
55 |
56 | (defn decorate-field
57 | "Decorate form field, corresponds to antd's getFieldDecorator() function
58 |
59 | * form - the `form` object, obtained from `(get-form)`
60 | * id - field identifier, supports nested fields format in string format
61 | * options - the validation options for the field
62 | * field - the input field element that the validation will be applied to
63 |
64 | For more information about the validation options, please refer to:
65 | https://ant.design/components/form/#getFieldDecorator(id,-options)-parameters"
66 | ([form id field] (decorate-field form id {} field))
67 | ([form id options field]
68 | (let [field-decorator (:getFieldDecorator form)
69 | params (clj->js (ant/map-keys->camel-case options))]
70 | ((field-decorator id params) field))))
71 |
72 | (export-form-funcs)
73 | (export-funcs)
74 | (export-props)
75 | (export-rum-components)
76 |
--------------------------------------------------------------------------------
/src/antizer/antd.clj:
--------------------------------------------------------------------------------
1 | (ns antizer.antd)
2 |
3 | (def components '[Affix
4 | Alert
5 | Anchor
6 | Anchor.Link
7 | AutoComplete
8 | Avatar
9 | BackTop
10 | Badge
11 | Breadcrumb
12 | Breadcrumb.Item
13 | Button
14 | Button.Group
15 | Calendar
16 | Card
17 | Carousel
18 | Cascader
19 | Checkbox
20 | Checkbox.Group
21 | Col
22 | Collapse
23 | Collapse.Panel
24 | DatePicker
25 | DatePicker.MonthPicker
26 | DatePicker.RangePicker
27 | Dropdown
28 | Dropdown.Button
29 | Form
30 | Form.Item
31 | Icon
32 | Input
33 | Input.Group
34 | Input.Search
35 | Input.TextArea
36 | InputNumber
37 | Layout
38 | Layout.Content
39 | Layout.Footer
40 | Layout.Header
41 | Layout.Sider
42 | LocaleProvider
43 | Mention
44 | Menu
45 | Menu.Divider
46 | Menu.Item
47 | Menu.ItemGroup
48 | Menu.SubMenu
49 | Modal
50 | Pagination
51 | Popconfirm
52 | Popover
53 | Progress
54 | Radio
55 | Radio.Group
56 | Rate
57 | Row
58 | Select
59 | Select.Option
60 | Select.OptGroup
61 | Slider
62 | Spin
63 | Steps
64 | Steps.Step
65 | Switch
66 | Table
67 | Table.Column
68 | Tabs
69 | Tabs.TabPane
70 | Tag
71 | Tag.CheckableTag
72 | TimePicker
73 | Timeline
74 | Timeline.Item
75 | Tooltip
76 | Transfer
77 | Tree
78 | TreeSelect
79 | TreeSelect.TreeNode
80 | Tree.TreeNode
81 | Upload])
82 |
83 | (def props '[locales])
84 |
85 | (def funcs '[message.config
86 | message.error
87 | message.info
88 | message.loading
89 | message.success
90 | message.warn
91 | message.warning
92 | Modal.confirm
93 | Modal.error
94 | Modal.info
95 | Modal.success
96 | Modal.warning
97 | notification.close
98 | notification.config
99 | notification.destroy
100 | notification.error
101 | notification.info
102 | notification.open
103 | notification.success
104 | notification.warn
105 | notification.warning])
106 |
107 | (def form-funcs '[getFieldDecorator
108 | getFieldError
109 | getFieldsError
110 | getFieldValue
111 | getFieldsValue
112 | isFieldTouched
113 | isFieldsTouched
114 | isFieldValidating
115 | resetFields
116 | setFields
117 | setFieldsValue
118 | validateFields
119 | validateFieldsAndScroll])
120 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # antizer
2 |
3 | Antizer is a ClojureScript library implementing [Ant Design](https://ant.design/) React components for [Reagent](https://github.com/reagent-project/reagent) and [Rum](https://github.com/tonsky/rum).
4 |
5 | Ant Design is an enterprise-class UI design language and React-based implementation with the following features:
6 |
7 | * An enterprise-class UI design language for web applications.
8 | * A set of high-quality React components out of the box.
9 | * Extensive API documentation and examples.
10 |
11 | ## Resources
12 |
13 | * [Reagent Demo](https://priornix.github.io/antizer/latest/examples/reagent.html)
14 |
15 | * [Rum Demo](https://priornix.github.io/antizer/latest/examples/rum.html)
16 |
17 | * [Antizer Documentation](https://priornix.github.io/antizer/latest/)
18 |
19 | * [API Documentation](https://priornix.github.io/antizer/latest/api/)
20 |
21 | * [Ant Design Component Documentation](https://ant.design/docs/react/introduce)
22 |
23 | ## Status
24 |
25 | All the Ant Design components should be fully functional and production-ready. If you discover any missing or invalid components, please file a ticket.
26 |
27 | ### Who's Using Antizer
28 |
29 | Please let me know if you are using Antizer within your project, and I will gladly add that in here.
30 |
31 | ## Usage
32 |
33 | To use Antizer, add the following to your project.clj:
34 |
35 | ```clojure
36 | [antizer "0.2.2"]
37 | ```
38 |
39 | You would also need to add the ClojureScript React library that you will be using.
40 |
41 | For Reagent:
42 | ```clojure
43 | [reagent "X.Y.Z"]
44 | ```
45 |
46 | For Rum:
47 | ```clojure
48 | [rum "X.Y.Z"]
49 | ```
50 |
51 | It is also necessary to include the Ant Design CSS stylesheet in your HTML page. The CSS files can be obtained from the following classpaths:
52 |
53 | * `cljsjs/antd/development/antd.inc.css`
54 | * `cljsjs/antd/production/antd.min.inc.css`
55 |
56 | You can also follow the instructions for customization with LESS [here](https://ant.design/docs/react/customize-theme).
57 |
58 | ### Quick Example
59 |
60 | For Reagent:
61 | ```clojure
62 | (require '[antizer.reagent :as ant])
63 | (require '[reagent.core :as r])
64 |
65 | (defn render []
66 | [ant/button {:on-click #(ant/message-info "Hello Reagent!")} "Click me"])
67 |
68 | (defn init! []
69 | (r/render [render] (.-body js/document)))
70 | ```
71 |
72 | For Rum:
73 | ```clojure
74 | (require '[antizer.rum :as ant])
75 | (require '[rum.core :as rum])
76 |
77 | (defn render []
78 | (ant/button {:on-click #(ant/message-info "Hello Rum!")} "Click me"))
79 |
80 | (defn init! []
81 | (rum/mount (render) (.-body js/document)))
82 | ```
83 |
84 | ## Examples
85 |
86 | To compile the examples:
87 |
88 | ```bash
89 | lein with-profile +examples cljsbuild once
90 | ```
91 |
92 | To compile the examples and enable hot reloading with figwheel:
93 |
94 | ```bash
95 | lein with-profile +examples-dev figwheel
96 | ```
97 |
98 | After compilation, open up the respective HTML page in the `examples/resources` folder in your browser.
99 |
100 | ## Changes
101 |
102 | ### 0.2.2
103 | * Updated antd library to [2.12.3](https://ant.design/changelog#2.12.3).
104 | * Fixed resource typo in README.md and documentation.
105 | * Added missing components: Breadcumb.Item.
106 | * Added new component: Input.TextArea.
107 | * Fixed layout issue in examples.
108 | * Added note on DatePicker and Calendar known issues.
109 |
110 | ### 0.2.1
111 | * Updated antd library to [2.11.2](https://ant.design/changelog#2.11.2).
112 | * Updated instructions for CSS file inclusion.
113 | * Added Avatar component and example.
114 | * Fixed layout and CSS styles for examples.
115 | * Fixed cljsbuild settings for examples.
116 |
117 | ## Known Issues
118 |
119 | #### DatePicker and Calendar date text are not displayed in the correct language when no date value has been set.
120 |
121 | This is due to how the moment.js library is being packaged [currently](https://github.com/ant-design/ant-design/issues/4972#issuecomment-281235293). The date values must always be present for the components in order for the date to be displayed correctly. A bug report has been filed with the `antd` library [here](https://github.com/ant-design/ant-design/issues/6712).
122 |
123 | Here is a workaround for now:
124 |
125 | * Set default-value to a moment object ie: `:default-value (js/moment)` when using the DatePicker or Calendar component.
126 | * Disable the clear date feature by setting `:allow-clear` to `false`.
127 |
128 | ## Acknowledgement
129 |
130 | Thanks to Ant Design, [cljsjs/antd](https://github.com/cljsjs/packages/tree/master/antd), [Reagent](https://github.com/reagent-project/reagent), [Rum](https://github.com/tonsky/rum) and of course [ClojureScript](https://clojurescript.org), without which this project would not be possible.
131 |
132 | ## License
133 |
134 | Copyright © 2017 Michael Lim
135 |
136 | Licensed under Eclipse Public License (see LICENSE).
137 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Eclipse Public License - v 1.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial code and
12 | documentation distributed under this Agreement, and
13 |
14 | b) in the case of each subsequent Contributor:
15 |
16 | i) changes to the Program, and
17 |
18 | ii) additions to the Program;
19 |
20 | where such changes and/or additions to the Program originate from and are
21 | distributed by that particular Contributor. A Contribution 'originates' from
22 | a Contributor if it was added to the Program by such Contributor itself or
23 | anyone acting on such Contributor's behalf. Contributions do not include
24 | additions to the Program which: (i) are separate modules of software
25 | distributed in conjunction with the Program under their own license
26 | agreement, and (ii) are not derivative works of the Program.
27 |
28 | "Contributor" means any person or entity that distributes the Program.
29 |
30 | "Licensed Patents" mean patent claims licensable by a Contributor which are
31 | necessarily infringed by the use or sale of its Contribution alone or when
32 | combined with the Program.
33 |
34 | "Program" means the Contributions distributed in accordance with this
35 | Agreement.
36 |
37 | "Recipient" means anyone who receives the Program under this Agreement,
38 | including all Contributors.
39 |
40 | 2. GRANT OF RIGHTS
41 |
42 | a) Subject to the terms of this Agreement, each Contributor hereby grants
43 | Recipient a non-exclusive, worldwide, royalty-free copyright license to
44 | reproduce, prepare derivative works of, publicly display, publicly perform,
45 | distribute and sublicense the Contribution of such Contributor, if any, and
46 | such derivative works, in source code and object code form.
47 |
48 | b) Subject to the terms of this Agreement, each Contributor hereby grants
49 | Recipient a non-exclusive, worldwide, royalty-free patent license under
50 | Licensed Patents to make, use, sell, offer to sell, import and otherwise
51 | transfer the Contribution of such Contributor, if any, in source code and
52 | object code form. This patent license shall apply to the combination of the
53 | Contribution and the Program if, at the time the Contribution is added by the
54 | Contributor, such addition of the Contribution causes such combination to be
55 | covered by the Licensed Patents. The patent license shall not apply to any
56 | other combinations which include the Contribution. No hardware per se is
57 | licensed hereunder.
58 |
59 | c) Recipient understands that although each Contributor grants the licenses
60 | to its Contributions set forth herein, no assurances are provided by any
61 | Contributor that the Program does not infringe the patent or other
62 | intellectual property rights of any other entity. Each Contributor disclaims
63 | any liability to Recipient for claims brought by any other entity based on
64 | infringement of intellectual property rights or otherwise. As a condition to
65 | exercising the rights and licenses granted hereunder, each Recipient hereby
66 | assumes sole responsibility to secure any other intellectual property rights
67 | needed, if any. For example, if a third party patent license is required to
68 | allow Recipient to distribute the Program, it is Recipient's responsibility
69 | to acquire that license before distributing the Program.
70 |
71 | d) Each Contributor represents that to its knowledge it has sufficient
72 | copyright rights in its Contribution, if any, to grant the copyright license
73 | set forth in this Agreement.
74 |
75 | 3. REQUIREMENTS
76 |
77 | A Contributor may choose to distribute the Program in object code form under
78 | its own license agreement, provided that:
79 |
80 | a) it complies with the terms and conditions of this Agreement; and
81 |
82 | b) its license agreement:
83 |
84 | i) effectively disclaims on behalf of all Contributors all warranties and
85 | conditions, express and implied, including warranties or conditions of title
86 | and non-infringement, and implied warranties or conditions of merchantability
87 | and fitness for a particular purpose;
88 |
89 | ii) effectively excludes on behalf of all Contributors all liability for
90 | damages, including direct, indirect, special, incidental and consequential
91 | damages, such as lost profits;
92 |
93 | iii) states that any provisions which differ from this Agreement are offered
94 | by that Contributor alone and not by any other party; and
95 |
96 | iv) states that source code for the Program is available from such
97 | Contributor, and informs licensees how to obtain it in a reasonable manner on
98 | or through a medium customarily used for software exchange.
99 |
100 | When the Program is made available in source code form:
101 |
102 | a) it must be made available under this Agreement; and
103 |
104 | b) a copy of this Agreement must be included with each copy of the Program.
105 |
106 | Contributors may not remove or alter any copyright notices contained within
107 | the Program.
108 |
109 | Each Contributor must identify itself as the originator of its Contribution,
110 | if any, in a manner that reasonably allows subsequent Recipients to identify
111 | the originator of the Contribution.
112 |
113 | 4. COMMERCIAL DISTRIBUTION
114 |
115 | Commercial distributors of software may accept certain responsibilities with
116 | respect to end users, business partners and the like. While this license is
117 | intended to facilitate the commercial use of the Program, the Contributor who
118 | includes the Program in a commercial product offering should do so in a
119 | manner which does not create potential liability for other Contributors.
120 | Therefore, if a Contributor includes the Program in a commercial product
121 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend
122 | and indemnify every other Contributor ("Indemnified Contributor") against any
123 | losses, damages and costs (collectively "Losses") arising from claims,
124 | lawsuits and other legal actions brought by a third party against the
125 | Indemnified Contributor to the extent caused by the acts or omissions of such
126 | Commercial Contributor in connection with its distribution of the Program in
127 | a commercial product offering. The obligations in this section do not apply
128 | to any claims or Losses relating to any actual or alleged intellectual
129 | property infringement. In order to qualify, an Indemnified Contributor must:
130 | a) promptly notify the Commercial Contributor in writing of such claim, and
131 | b) allow the Commercial Contributor to control, and cooperate with the
132 | Commercial Contributor in, the defense and any related settlement
133 | negotiations. The Indemnified Contributor may participate in any such claim
134 | at its own expense.
135 |
136 | For example, a Contributor might include the Program in a commercial product
137 | offering, Product X. That Contributor is then a Commercial Contributor. If
138 | that Commercial Contributor then makes performance claims, or offers
139 | warranties related to Product X, those performance claims and warranties are
140 | such Commercial Contributor's responsibility alone. Under this section, the
141 | Commercial Contributor would have to defend claims against the other
142 | Contributors related to those performance claims and warranties, and if a
143 | court requires any other Contributor to pay any damages as a result, the
144 | Commercial Contributor must pay those damages.
145 |
146 | 5. NO WARRANTY
147 |
148 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
149 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
150 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
151 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
152 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
153 | appropriateness of using and distributing the Program and assumes all risks
154 | associated with its exercise of rights under this Agreement , including but
155 | not limited to the risks and costs of program errors, compliance with
156 | applicable laws, damage to or loss of data, programs or equipment, and
157 | unavailability or interruption of operations.
158 |
159 | 6. DISCLAIMER OF LIABILITY
160 |
161 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
162 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
163 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
164 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
165 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
166 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
167 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
168 | OF SUCH DAMAGES.
169 |
170 | 7. GENERAL
171 |
172 | If any provision of this Agreement is invalid or unenforceable under
173 | applicable law, it shall not affect the validity or enforceability of the
174 | remainder of the terms of this Agreement, and without further action by the
175 | parties hereto, such provision shall be reformed to the minimum extent
176 | necessary to make such provision valid and enforceable.
177 |
178 | If Recipient institutes patent litigation against any entity (including a
179 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself
180 | (excluding combinations of the Program with other software or hardware)
181 | infringes such Recipient's patent(s), then such Recipient's rights granted
182 | under Section 2(b) shall terminate as of the date such litigation is filed.
183 |
184 | All Recipient's rights under this Agreement shall terminate if it fails to
185 | comply with any of the material terms or conditions of this Agreement and
186 | does not cure such failure in a reasonable period of time after becoming
187 | aware of such noncompliance. If all Recipient's rights under this Agreement
188 | terminate, Recipient agrees to cease use and distribution of the Program as
189 | soon as reasonably practicable. However, Recipient's obligations under this
190 | Agreement and any licenses granted by Recipient relating to the Program shall
191 | continue and survive.
192 |
193 | Everyone is permitted to copy and distribute copies of this Agreement, but in
194 | order to avoid inconsistency the Agreement is copyrighted and may only be
195 | modified in the following manner. The Agreement Steward reserves the right to
196 | publish new versions (including revisions) of this Agreement from time to
197 | time. No one other than the Agreement Steward has the right to modify this
198 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The
199 | Eclipse Foundation may assign the responsibility to serve as the Agreement
200 | Steward to a suitable separate entity. Each new version of the Agreement will
201 | be given a distinguishing version number. The Program (including
202 | Contributions) may always be distributed subject to the version of the
203 | Agreement under which it was received. In addition, after a new version of
204 | the Agreement is published, Contributor may elect to distribute the Program
205 | (including its Contributions) under the new version. Except as expressly
206 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
207 | licenses to the intellectual property of any Contributor under this
208 | Agreement, whether expressly, by implication, estoppel or otherwise. All
209 | rights in the Program not expressly granted under this Agreement are
210 | reserved.
211 |
212 | This Agreement is governed by the laws of the State of New York and the
213 | intellectual property laws of the United States of America. No party to this
214 | Agreement will bring a legal action under this Agreement more than one year
215 | after the cause of action arose. Each party waives its rights to a jury trial
216 | in any resulting litigation.
217 |
--------------------------------------------------------------------------------
/examples/src/antizer_examples/rum.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer-examples.rum
2 | (:require [clojure.string :as string]
3 | [antizer.rum :as ant]
4 | [antizer-examples.common :as common]
5 | ;; each language has to be required seperately
6 | ;; in order for the display to be correct
7 | [cljsjs.moment]
8 | [cljsjs.moment.locale.es]
9 | [cljsjs.moment.locale.de]
10 | [cljsjs.moment.locale.ja]
11 | [cljsjs.moment.locale.ru]
12 | [cljsjs.moment.locale.zh-cn]
13 | [rum.core :as rum]))
14 |
15 | (rum/defcs auto-complete < (rum/local [] ::data)
16 | [state]
17 | (let [data (::data state)]
18 | [:div
19 | [:h2 (str "Autocomplete")]
20 | (ant/auto-complete
21 | ;; we need to use dataSource instead of data-source, see README.MD
22 | {:style {:width "80%"} :dataSource @data
23 | :on-search
24 | (fn [x]
25 | (reset! data
26 | (take 3 (iterate #(str % (string/reverse %)) x))))
27 | :placeholder "Enter something"})]))
28 |
29 | (defn avatar []
30 | [:div.avatar
31 | [:h2 "Avatar"]
32 | [:div
33 | (ant/avatar {:style {:background-color "#87d068"} :icon "user" :class "va-middle"})
34 | (ant/avatar {:style {:color "#f56a00" :background-color "#fde3cf"} :class "va-middle"} "U")
35 | (ant/avatar {:style {:background-color "#ffbf00"} :class "va-middle"} "John")
36 | (ant/badge {:count 10} (ant/avatar {:style {:background-color "#108ee9"} :shape "square" :icon "user"}))]])
37 |
38 | (defn buttons []
39 | [:div.example-button
40 | [:h2 "Buttons"]
41 | (ant/button {:type "primary"} "Primary")
42 | (ant/button "Default")
43 | (ant/button {:type "danger"} "Warning")
44 | (ant/button {:icon "shopping-cart" :type "primary"} "With icon")
45 | (ant/button {:icon "edit" :type "primary"})])
46 |
47 | (defn card []
48 | [:div
49 | [:h2 "Cards"]
50 | (ant/card {:title "Title" :bordered true :class "card"}
51 | [:p "Not the usual lorem ipsum"]) [:br]
52 | (ant/card {:bordered true :class "card-photo"}
53 | [:div [:img {:src "https://unsplash.it/330/120/?random"}]]
54 | (ant/col {:span 12} [:div [:h3 "Please rate me"]])
55 | (ant/col {:span 12} (ant/rate)))])
56 |
57 | (defn carousel []
58 | [:div
59 | [:h2 "Carousel"]
60 | (ant/carousel {:autoplay true :dots true}
61 | (for [i (range 3)]
62 | [:div {:key i} [:img {:src (str "https://unsplash.it/400/300/?random=" i)}]]))])
63 |
64 | (defn add-actions-column [columns data-atom]
65 | (conj columns
66 | {:title "Actions"
67 | :render
68 | #(ant/button {:icon "delete" :type "danger"
69 | :on-click
70 | (fn []
71 | (reset! data-atom
72 | (remove (fn [d] (= (get (js->clj %2) "id")
73 | (:id d))) @data-atom)))})}))
74 |
75 | (rum/defcs datatable < (rum/local common/people ::data)
76 | [state]
77 | (let [data (::data state)]
78 | [:div
79 | [:h2 "Data Table"]
80 | (ant/table
81 | {:columns (add-actions-column common/columns data) :dataSource @data
82 | :pagination common/pagination :row-key "id"
83 | :row-selection
84 | {:on-change
85 | #(let [selected (js->clj %2 :keywordize-keys true)]
86 | (ant/message-info (str "You have selected: " (map :name selected))))}})]))
87 |
88 | (rum/defcs user-form <
89 | {:init (fn [state props]
90 | (merge state (js->clj props :keywordize-keys true)))}
91 | [state]
92 | (let [form (ant/get-form state)]
93 | (ant/form {:layout "horizontal"}
94 | (ant/form-item (merge common/form-style {:label "Name"})
95 | (ant/decorate-field form "name" {:rules [{:required true}]}
96 | (ant/input)))
97 | (ant/form-item (merge common/form-style {:label "Email"})
98 | (ant/decorate-field form "email" {:rules [{:required true} {:type "email"}]}
99 | (ant/input)))
100 | (ant/form-item (merge common/form-style {:label "Address"})
101 | (ant/decorate-field form "address" {:initial-value "Some initial value" :rules [{:required true}]}
102 | (ant/input)))
103 | (ant/form-item (merge common/form-style {:label "Years of Experience"})
104 | (ant/decorate-field form "experience" {:rules [{:required true}]}
105 | (ant/radio-group
106 | (ant/radio {:value 1} "1-10")
107 | (ant/radio {:value 10} "10-30")
108 | (ant/radio {:value 30} "30-50")
109 | (ant/radio {:value 50} "> 50"))))
110 | (ant/form-item (merge common/form-style {:label "Start Date"})
111 | (ant/decorate-field form "date" {:initial-value (js/moment) :rules [{:required true}]}
112 | (ant/date-picker {:format "MMM Do YYYY"})))
113 | (ant/form-item (merge common/form-style {:label "Accept Terms?"})
114 | (ant/decorate-field form "accept-terms"
115 | (ant/switch)))
116 | (if (not (:hide-buttons? state))
117 | (ant/form-item {:wrapper-col {:offset 6}}
118 | (ant/col {:span 4}
119 | (ant/button {:type "primary" :on-click #(ant/validate-fields form)}
120 | "Submit"))
121 | (ant/col {:offset 1}
122 | (ant/button {:on-click #(ant/reset-fields form)}
123 | "Reset")))))))
124 |
125 | (defn form-example []
126 | [:div
127 | [:h2 "Form"]
128 | (ant/create-form user-form)])
129 |
130 | (rum/defcs localization < (rum/local "en_US" ::locale)
131 | [state]
132 | (let [locale (::locale state)]
133 | (ant/locale-provider {:locale (ant/locales @locale)}
134 | (ant/col
135 | [:h2 "Localization"]
136 | [:span "Choose a language:"
137 | (ant/select {:default-value "en_US" :on-change #(common/set-locale %1 locale) :style {:padding "10px"}}
138 | (ant/select-option {:value "en_US"} "English")
139 | (ant/select-option {:value "es_ES"} "Español")
140 | (ant/select-option {:value "de_DE"} "Deutsch")
141 | (ant/select-option {:value "ru_RU"} "Русский")
142 | (ant/select-option {:value "zh_CN"} "中文")
143 | (ant/select-option {:value "ja_JP"} "日本語")
144 | (ant/select-option {:value "tlh" :disabled true} "Klingon"))]
145 | (ant/pagination {:total 40 :show-size-changer true}) [:br]
146 | (ant/date-picker {:format "ddd MMM Do YYYY" :default-value (js/moment) :style {:width "60%"} :allow-clear false :show-today false}) [:br] [:br]
147 | (ant/time-picker {:style {:width "60%"}}) [:br] [:br]
148 | (ant/calendar {:fullscreen false :default-value (js/moment)})
149 | (ant/table {:columns common/columns})))))
150 |
151 | (defn messages []
152 | [:div.example-button
153 | [:h2 "Messages"]
154 | (ant/button {:on-click #(ant/message-info "Normal message")} "Normal")
155 | (ant/button {:on-click #(ant/message-success "Success message")} "Success")
156 | (ant/button {:on-click #(ant/message-warning "Warning message")} "Warning")
157 | (ant/button {:on-click #(ant/message-error "Error message")} "Error")
158 | (ant/button {:on-click #(ant/message-loading "This message will disappear in 10 seconds" 10)} "Timed")])
159 |
160 | (rum/defcs modal < (rum/local false ::modal1)
161 | (rum/local false ::modal-form)
162 | [state]
163 | (let [modal1 (::modal1 state)
164 | modal-form (::modal-form state)]
165 | [:div.example-button
166 | [:h2 "Modal"]
167 | (ant/button {:on-click #(reset! modal1 true)} "Modal Dialog")
168 | (ant/modal {:visible @modal1 :title "Title of modal"
169 | :on-ok #(reset! modal1 false) :on-cancel #(reset! modal1 false)}
170 | [:p "Some content 1"])
171 | (ant/button {:on-click #(ant/modal-confirm {:title "Are you sure?" :content "Some content"})} "Confirmation Modal")
172 | (ant/button {:on-click #(reset! modal-form true)} "Modal Form")
173 | (ant/modal {:visible @modal-form :title "Modal Form" :width 600
174 | :on-ok #(reset! modal-form false) :on-cancel #(reset! modal-form false)}
175 | (ant/create-form user-form :props {:hide-buttons? true}))]))
176 |
177 | (defn notifications []
178 | [:div.example-button
179 | [:h2 "Notifications"]
180 | (ant/button {:on-click #(ant/notification-open
181 | {:message "Timed Notification"
182 | :description "This notification will close after 4.5 seconds"})}
183 | "Notification")
184 | (ant/button {:on-click
185 | #(let [key (random-uuid)]
186 | (ant/notification-open
187 | {:message "Popup Notification"
188 | :duration 0
189 | :btn (ant/button {:type "primary"
190 | :on-click (fn [] (ant/notification-close key))}
191 | "Click to dismiss")
192 | :key key
193 | :description "This notification will not close until it is dismissed"}))}
194 | "Popup Notification")])
195 |
196 | (rum/defcs progress < (rum/local 50 ::percent)
197 | [state]
198 | (let [percent (::percent state)
199 | operate (fn [operation]
200 | (if (= :plus operation)
201 | (if (< @percent 100) (swap! percent + 10))
202 | (if (>= @percent 0) (swap! percent - 10))))
203 | status (cond
204 | (< @percent 42) "exception"
205 | (= @percent 100) "success"
206 | :else "active")]
207 | [:div.progress
208 | [:h2 "Progress"]
209 | (ant/progress {:type "circle" :percent @percent :status status})
210 | (ant/button-group
211 | (ant/button {:icon "plus" :on-click #(operate :plus)})
212 | (ant/button {:icon "minus" :on-click #(operate :minus)}))
213 | (ant/progress {:percent @percent :status status
214 | :style {:width "42%"}})]))
215 |
216 | (defn timeline []
217 | [:div
218 | [:h2 "Timeline"]
219 | (ant/timeline
220 | (ant/timeline-item {:color "red"} "6th June Project created")
221 | (ant/timeline-item {:color "blue"} "8th June Initial prototype done")
222 | (ant/timeline-item {:color "green"} "20th June Final release"))])
223 |
224 | (defn tooltip []
225 | [:div.example-button
226 | [:h2 "Tooltips and Popups " (ant/tooltip {:title "Found me!"}
227 | (ant/icon {:type "question-circle-o" :style {:font-size 13}}))]
228 | (ant/tooltip {:title "Tooltip"} (ant/button "Tooltip"))
229 | (ant/popover {:content "Dum dee dee dum dee dee dum" :title "Deedee dum"} (ant/button "Popover"))
230 | (ant/popconfirm {:title "Are you sure?"
231 | :on-confirm #(ant/message-success "You clicked OK")
232 | :on-cancel #(ant/message-error "You clicked Cancel")}
233 | (ant/button "Click to confirm"))])
234 |
235 | (defn tree []
236 | [:div
237 | [:h2 "Tree"]
238 | (ant/tree {:checkable true :default-expanded-keys ["functional" "clr" "jvm" "javascript" "nodejs"]
239 | :default-checked-keys ["clojure" "clojure-clr" "cljs" "lumo" "planck"]}
240 | (ant/tree-tree-node {:title "Functional Languages" :key "functional"}
241 | (ant/tree-tree-node {:title "CLR" :key "clr"}
242 | (ant/tree-tree-node {:title "Clojure CLR" :key "clojure-clr"}))
243 | (ant/tree-tree-node {:title "Haskell" :key "haskell"})
244 | (ant/tree-tree-node {:title "JVM" :key "jvm"}
245 | (ant/tree-tree-node {:title "Clojure" :key "clojure"})
246 | (ant/tree-tree-node {:title "Frege" :key "frege"})
247 | (ant/tree-tree-node {:title "Scala" :disable-checkbox true}))
248 | (ant/tree-tree-node {:title "JavaScript Engine" :key "javascript"}
249 | (ant/tree-tree-node {:title "ClojureScript" :key "cljs"}))
250 | (ant/tree-tree-node {:title "Node.js" :key "nodejs"}
251 | (ant/tree-tree-node {:title "Lumo" :key "lumo"}))
252 | (ant/tree-tree-node {:title "Planck" :key "planck"})))])
253 |
254 | (defn render-example
255 | "Render each example within a bordered box"
256 | [examples]
257 | ;; we need to generate a different key for each react element
258 | (ant/col {:span 12}
259 | (for [example examples]
260 | [:div.box {:key (random-uuid)}
261 | [:div.box-content
262 | (example)]])))
263 |
264 | (defn render-full-row [example]
265 | (ant/col {:span 24}
266 | [:div.box {:key (random-uuid)}
267 | [:div.box-content
268 | (example)]]))
269 |
270 | (defn content-area []
271 | (ant/layout-content {:class "content-area"}
272 | (ant/row {:gutter 12}
273 | (render-example [carousel buttons messages timeline tree progress])
274 | (render-example [card tooltip notifications auto-complete localization modal avatar]))
275 | (render-full-row form-example)
276 | (render-full-row datatable)))
277 |
278 | (defn side-menu []
279 | (ant/menu {:mode "inline" :theme :dark :style {:height "100%"}}
280 | (ant/menu-item {:disabled true} "Menu without icons")
281 | (ant/menu-item "Menu Item")
282 | (ant/menu-sub-menu {:title "Sub Menu"}
283 | (ant/menu-item "Item 1")
284 | (ant/menu-item "Item 2"))
285 | (ant/menu-item {:disabled true} "Menu with icons")
286 | (ant/menu-item [:span {:key "s"} (ant/icon {:type "home"}) "Menu Item"])
287 | (ant/menu-sub-menu {:title [:span (ant/icon {:type "setting"}) "Sub Menu"]}
288 | (ant/menu-item [:span {:key "s"} (ant/icon {:type "user"}) "Item 1"])
289 | (ant/menu-item [:span {:key "s"} (ant/icon {:type "notification"}) "Item 2"]))))
290 |
291 | (defn render-layout []
292 | (ant/locale-provider {:locale (ant/locales "en_US")}
293 | (ant/layout
294 | (ant/affix
295 | (ant/layout-header {:class "banner"}
296 | (ant/row
297 | (ant/col {:span 12} [:h2.banner-header {:key "layout"} "Antizer Rum Example"])
298 | (ant/col {:span 1 :offset 11}
299 | [:a {:href "https://github.com/priornix/antizer" :key "link"}
300 | (ant/icon {:class "banner-logo" :type "github"})]))))
301 | (ant/layout
302 | (ant/layout-sider (side-menu))
303 | (ant/layout {:style {:width "60%"}}
304 | (content-area))))))
305 |
306 | (defn init! []
307 | (rum/mount (render-layout)
308 | (js/document.getElementById "app")))
309 |
--------------------------------------------------------------------------------
/examples/src/antizer_examples/reagent.cljs:
--------------------------------------------------------------------------------
1 | (ns antizer-examples.reagent
2 | (:require [clojure.string :as string]
3 | [antizer.reagent :as ant]
4 | [antizer-examples.common :as common]
5 | ;; each language has to be required seperately
6 | ;; in order for the display to be correct
7 | [cljsjs.moment]
8 | [cljsjs.moment.locale.es]
9 | [cljsjs.moment.locale.de]
10 | [cljsjs.moment.locale.ja]
11 | [cljsjs.moment.locale.ru]
12 | [cljsjs.moment.locale.zh-cn]
13 | [reagent.core :as r]))
14 |
15 | (defn auto-complete []
16 | (let [data (r/atom [])]
17 | (fn []
18 | [:div
19 | [:h2 (str "Autocomplete")]
20 | [ant/auto-complete
21 | ;; we need to use dataSource instead of data-source, see README.MD
22 | {:style {:width "80%"} :dataSource @data
23 | :on-search
24 | (fn [x]
25 | (reset! data
26 | (take 3 (iterate #(str % (string/reverse %)) x))))
27 | :placeholder "Enter something"}]])))
28 |
29 | (defn avatar []
30 | [:div.avatar
31 | [:h2 "Avatar"]
32 | [:div
33 | [ant/avatar {:style {:background-color "#87d068"} :icon "user" :class "va-middle"}]
34 | [ant/avatar {:style {:color "#f56a00" :background-color "#fde3cf"} :class "va-middle"} "U"]
35 | [ant/avatar {:style {:background-color "#ffbf00"} :class "va-middle"} "John"]
36 | [ant/badge {:count 10} [ant/avatar {:style {:background-color "#108ee9"} :shape "square" :icon "user"}]]]])
37 |
38 | (defn buttons []
39 | [:div.example-button
40 | [:h2 "Buttons"]
41 | [ant/button {:type "primary"} "Primary"]
42 | [ant/button "Default"]
43 | [ant/button {:type "danger"} "Warning"]
44 | [ant/button {:icon "shopping-cart" :type "primary"} "With icon"]
45 | [ant/button {:icon "edit" :type "primary"}]])
46 |
47 | (defn card []
48 | [:div
49 | [:h2 "Cards"]
50 | [ant/card {:title "Title" :bordered true :class "card"}
51 | [:p "Not the usual lorem ipsum"]] [:br]
52 | [ant/card {:bordered true :class "card-photo"}
53 | [:div [:img {:src "https://unsplash.it/330/120/?random"}]]
54 | [ant/col {:span 12} [:div [:h3 "Please rate me"]]]
55 | [ant/col {:span 12} [ant/rate]]]])
56 |
57 | (defn carousel []
58 | [:div
59 | [:h2 "Carousel"]
60 | [ant/carousel {:autoplay true :dots true}
61 | (for [i (range 3)]
62 | [:div {:key i} [:img {:src (str "https://unsplash.it/400/300/?random=" i)}]])]])
63 |
64 | (defn add-actions-column [columns data-atom]
65 | (conj columns
66 | {:title "Actions"
67 | :render
68 | #(r/as-element
69 | [ant/button {:icon "delete" :type "danger"
70 | :on-click
71 | (fn []
72 | (reset! data-atom
73 | (remove (fn [d] (= (get (js->clj %2) "id")
74 | (:id d))) @data-atom)))}])}))
75 |
76 | (defn datatable []
77 | (let [data (r/atom common/people)]
78 | (fn []
79 | [:div
80 | [:h2 "Data Table"]
81 | [ant/table
82 | {:columns (add-actions-column common/columns data)
83 | :dataSource @data :pagination common/pagination :row-key "id"
84 | :row-selection
85 | {:on-change
86 | #(let [selected (js->clj %2 :keywordize-keys true)]
87 | (ant/message-info (str "You have selected: " (map :name selected))))}}]])))
88 |
89 | (defn user-form [display-buttons?]
90 | (fn [props]
91 | (let [form (ant/get-form)]
92 | [ant/form {:layout "horizontal"}
93 | [ant/form-item (merge common/form-style {:label "Name"})
94 | (ant/decorate-field form "name" {:rules [{:required true}]}
95 | [ant/input])]
96 | [ant/form-item (merge common/form-style {:label "Email"})
97 | (ant/decorate-field form "email" {:rules [{:required true} {:type "email"}]}
98 | [ant/input])]
99 | [ant/form-item (merge common/form-style {:label "Address"})
100 | (ant/decorate-field form "address" {:initial-value "Some initial value" :rules [{:required true}]}
101 | [ant/input])]
102 | [ant/form-item (merge common/form-style {:label "Years of Experience"})
103 | (ant/decorate-field form "experience" {:rules [{:required true}]}
104 | [ant/radio-group
105 | [ant/radio {:value 1} "1-10"]
106 | [ant/radio {:value 10} "10-30"]
107 | [ant/radio {:value 30} "30-50"]
108 | [ant/radio {:value 50} "> 50"]])]
109 | [ant/form-item (merge common/form-style {:label "Start Date"})
110 | (ant/decorate-field form "date" {:initial-value (js/moment) :rules [{:required true}]}
111 | [ant/date-picker {:format "MMM Do YYYY"}])]
112 | [ant/form-item (merge common/form-style {:label "Accept Terms?"})
113 | (ant/decorate-field form "accept-terms"
114 | [ant/switch])]
115 | (if display-buttons?
116 | [ant/form-item {:wrapper-col {:offset 6}}
117 | [ant/col {:span 4}
118 | [ant/button {:type "primary" :on-click #(ant/validate-fields form)}
119 | "Submit"]]
120 | [ant/col {:offset 1}
121 | [ant/button {:on-click #(ant/reset-fields form)}
122 | "Reset"]]])])))
123 |
124 | (defn form-example []
125 | [:div
126 | [:h2 "Form"]
127 | (ant/create-form (user-form true))])
128 |
129 | (defn localization []
130 | (let [locale-atom (r/atom "en_US")]
131 | (fn []
132 | [ant/locale-provider {:locale (ant/locales @locale-atom)}
133 | [:div
134 | [:h2 "Localization"]
135 | [:span "Choose a language:"
136 | [ant/select {:default-value "en_US" :on-change #(common/set-locale %1 locale-atom) :style {:padding "10px"}}
137 | [ant/select-option {:value "en_US"} "English"]
138 | [ant/select-option {:value "es_ES"} "Español"]
139 | [ant/select-option {:value "de_DE"} "Deutsch"]
140 | [ant/select-option {:value "ru_RU"} "Русский"]
141 | [ant/select-option {:value "zh_CN"} "中文"]
142 | [ant/select-option {:value "ja_JP"} "日本語"]
143 | [ant/select-option {:value "tlh" :disabled true} "Klingon"]]]
144 | [ant/pagination {:total 40 :show-size-changer true}] [:br]
145 | [ant/date-picker {:format "ddd MMM Do YYYY" :default-value (js/moment) :style {:width "60%"} :allow-clear false :show-today false}] [:br] [:br]
146 | [ant/time-picker {:style {:width "60%"}}] [:br] [:br]
147 | [ant/calendar {:fullscreen false :default-value (js/moment)}]
148 | [ant/table {:columns common/columns}]]])))
149 |
150 | (defn messages []
151 | [:div.example-button
152 | [:h2 "Messages"]
153 | [ant/button {:on-click #(ant/message-info "Normal message")} "Normal"]
154 | [ant/button {:on-click #(ant/message-success "Success message")} "Success"]
155 | [ant/button {:on-click #(ant/message-warning "Warning message")} "Warning"]
156 | [ant/button {:on-click #(ant/message-error "Error message")} "Error"]
157 | [ant/button {:on-click #(ant/message-loading "This message will disappear in 10 seconds" 10)} "Timed"]])
158 |
159 | (defn modal []
160 | (let [modal1 (r/atom false)
161 | modal-form (r/atom false)]
162 | (fn []
163 | [:div.example-button
164 | [:h2 "Modal"]
165 | [ant/button {:on-click #(reset! modal1 true)} "Modal Dialog"]
166 | [ant/modal {:visible @modal1 :title "Title of modal"
167 | :on-ok #(reset! modal1 false) :on-cancel #(reset! modal1 false)}
168 | (r/as-element [:p "Some content 1"])]
169 | [ant/button {:on-click #(ant/modal-confirm {:title "Are you sure?" :content "Some content"})} "Confirmation Modal"]
170 | [ant/button {:on-click #(reset! modal-form true)} "Modal Form"]
171 | [ant/modal {:visible @modal-form :title "Modal Form" :width 600
172 | :on-ok #(reset! modal-form false) :on-cancel #(reset! modal-form false)}
173 | (ant/create-form (user-form false))]])))
174 |
175 | (defn notifications []
176 | [:div.example-button
177 | [:h2 "Notifications"]
178 | [ant/button {:on-click #(ant/notification-open
179 | {:message "Timed Notification"
180 | :description "This notification will close after 4.5 seconds"})}
181 | "Notification"]
182 | [ant/button {:on-click
183 | #(let [key (random-uuid)]
184 | (ant/notification-open
185 | {:message "Popup Notification"
186 | :duration 0
187 | :btn (r/as-element
188 | [ant/button {:type "primary"
189 | :on-click (fn [] (ant/notification-close key))}
190 | "Click to dismiss"])
191 | :key key
192 | :description "This notification will not close until it is dismissed"}))}
193 | "Popup Notification"]])
194 |
195 | (defn progress []
196 | (let [percent (r/atom 50)]
197 | (fn []
198 | (let [operate (fn [operation]
199 | (if (= :plus operation)
200 | (if (< @percent 100) (swap! percent + 10))
201 | (if (>= @percent 0) (swap! percent - 10))))
202 | status (cond
203 | (< @percent 42) "exception"
204 | (= @percent 100) "success"
205 | :else "active")]
206 | [:div.progress
207 | [:h2 "Progress"]
208 | [ant/progress {:type "circle" :percent @percent :status status}]
209 | [ant/button-group
210 | [ant/button {:icon "plus" :on-click #(operate :plus)}]
211 | [ant/button {:icon "minus" :on-click #(operate :minus)}]]
212 | [ant/progress {:percent @percent :status status
213 | :style {:width "42%"}}]]))))
214 |
215 | (defn timeline []
216 | [:div
217 | [:h2 "Timeline"]
218 | [ant/timeline
219 | [ant/timeline-item {:color "red"} "6th June Project created"]
220 | [ant/timeline-item {:color "blue"} "8th June Initial prototype done"]
221 | [ant/timeline-item {:color "green"} "26th June Final Release"]]])
222 |
223 | (defn tooltip []
224 | [:div.example-button
225 | [:h2 "Tooltips and Popups " [ant/tooltip {:title "Found me!"}
226 | [ant/icon {:type "question-circle-o" :style {:font-size 13}}]]]
227 | [ant/tooltip {:title "Tooltip"} [ant/button "Tooltip"]]
228 | [ant/popover {:content "Dum dee dee dum dee dee dum" :title "Deedee dum"} [ant/button "Popover"]]
229 | [ant/popconfirm {:title "Are you sure?"
230 | :on-confirm #(ant/message-success "You clicked OK")
231 | :on-cancel #(ant/message-error "You clicked Cancel")}
232 | [ant/button "Click to confirm"]]])
233 |
234 | (defn tree []
235 | [:div
236 | [:h2 "Tree"]
237 | [ant/tree {:checkable true :default-expanded-keys ["functional" "clr" "jvm" "javascript" "nodejs"]
238 | :default-checked-keys ["clojure" "clojure-clr" "cljs" "lumo" "planck"]}
239 | [ant/tree-tree-node {:title "Functional Languages" :key "functional"}
240 | [ant/tree-tree-node {:title "CLR" :key "clr"}
241 | [ant/tree-tree-node {:title "Clojure CLR" :key "clojure-clr"}]]
242 | [ant/tree-tree-node {:title "Haskell" :key "haskell"}]
243 | [ant/tree-tree-node {:title "JVM" :key "jvm"}
244 | [ant/tree-tree-node {:title "Clojure" :key "clojure"}]
245 | [ant/tree-tree-node {:title "Frege" :key "frege"}]
246 | [ant/tree-tree-node {:title "Scala" :disable-checkbox true}]]
247 | [ant/tree-tree-node {:title "JavaScript Engine" :key "javascript"}
248 | [ant/tree-tree-node {:title "ClojureScript" :key "cljs"}]]
249 | [ant/tree-tree-node {:title "Node.js" :key "nodejs"}
250 | [ant/tree-tree-node {:title "Lumo" :key "lumo"}]]
251 | [ant/tree-tree-node {:title "Planck" :key "planck"}]]]])
252 |
253 | (defn render-example
254 | "Render each example within a bordered box"
255 | [examples]
256 | ;; we need to generate a different key for each react element
257 | [ant/col {:span 12}
258 | (for [example examples]
259 | [:div.box {:key (random-uuid)}
260 | [:div.box-content
261 | [example]]])])
262 |
263 | (defn render-full-row [example]
264 | [ant/col {:span 24}
265 | [:div.box {:key (random-uuid)}
266 | [:div.box-content
267 | [example]]]])
268 |
269 | (defn content-area []
270 | [ant/layout-content {:class "content-area"}
271 | [ant/row {:gutter 12}
272 | (render-example [carousel buttons messages timeline tree progress])
273 | (render-example [card tooltip notifications auto-complete localization modal avatar])
274 | (render-full-row form-example)
275 | (render-full-row datatable)]])
276 |
277 | (defn side-menu []
278 | [ant/menu {:mode "inline" :theme "dark" :style {:height "100%"}}
279 | [ant/menu-item {:disabled true} "Menu without icons"]
280 | [ant/menu-item "Menu Item"]
281 | [ant/menu-sub-menu {:title "Sub Menu"}
282 | [ant/menu-item "Item 1"]
283 | [ant/menu-item "Item 2"]]
284 | [ant/menu-item {:disabled true} "Menu with Icons"]
285 | [ant/menu-item (r/as-element [:span [ant/icon {:type "home"}] "Menu Item"])]
286 | [ant/menu-sub-menu {:title (r/as-element [:span [ant/icon {:type "setting"}] "Sub Menu"])}
287 | [ant/menu-item (r/as-element [:span [ant/icon {:type "user"}] "Item 1"])]
288 | [ant/menu-item (r/as-element [:span [ant/icon {:type "notification"}] "Item 2"])]]])
289 |
290 | (defn render-layout []
291 | (fn []
292 | [ant/locale-provider {:locale (ant/locales "en_US")}
293 | [ant/layout
294 | [ant/affix
295 | [ant/layout-header {:class "banner"}
296 | (r/as-element
297 | [ant/row
298 | [ant/col {:span 12} [:h2.banner-header "Antizer Reagent Example"]]
299 | [ant/col {:span 1 :offset 11}
300 | [:a {:href "https://github.com/priornix/antizer"}
301 | [ant/icon {:class "banner-logo" :type "github"}]]]])]]
302 | [ant/layout
303 | [ant/layout-sider [side-menu]]
304 | [ant/layout {:style {:width "60%"}}
305 | [content-area]]]]]))
306 |
307 | (defn init! []
308 | (r/render [render-layout]
309 | (js/document.getElementById "app")))
310 |
--------------------------------------------------------------------------------
/doc/content.adoc:
--------------------------------------------------------------------------------
1 | = Antizer - ClojureScript library for Ant Design React components
2 | Michael Lim
3 | :icons: font
4 | :toc: left
5 | :toclevels: 3
6 | :source-highlighter: pygments
7 | :pygments-style: friendly
8 |
9 | == Introduction
10 |
11 | https://ant.design/[Ant Design] is an enterprise-class UI design language and React-based implementation. It has the following features:
12 |
13 | * An enterprise-class UI design language for web applications.
14 | * A set of high-quality React components out of the box.
15 | * Extensive well-documented API and examples.
16 |
17 | Antizer is a ClojureScript library for Ant Design React components with bindings for Reagent and Rum.
18 |
19 | == Status
20 |
21 | All the Ant Design components should be fully functional and production-ready. If you discover any missing or invalid components, please file a ticket.
22 |
23 | == Install
24 |
25 | To use antizer, just add the following to your `project.clj`:
26 |
27 | [source,clojure]
28 | ----
29 | [antizer "0.2.2"]
30 | ----
31 |
32 | You would also need to add the ClojureScript React library that you will be using.
33 |
34 | For Reagent:
35 | [source,clojure]
36 | ----
37 | [reagent "X.Y.Z"]
38 | ----
39 |
40 | For Rum:
41 | [source,clojure]
42 | ----
43 | [rum "X.Y.Z"]
44 | ----
45 |
46 | It is also necessary to include the Ant Design CSS stylesheet in your HTML page. The CSS files can be obtained from the following classpaths:
47 |
48 | * `cljsjs/antd/development/antd.inc.css`
49 | * `cljsjs/antd/production/antd.min.inc.css`
50 |
51 | You can also follow the instructions for customization with LESS https://ant.design/docs/react/customize-theme[here].
52 |
53 | == Resources
54 |
55 | * https://priornix.github.io/antizer/latest/examples/reagent.html[Reagent Example]
56 | * https://priornix.github.io/antizer/latest/examples/rum.html[Rum Example]
57 | * https://priornix.github.io/antizer/latest/api/[API documentation]
58 | * https://ant.design/docs/react/introduce[Ant Design Components Documentation]
59 | * https://ant.design/docs/spec/introduce[Ant Design UI Guidelines]
60 |
61 | == Quick Example
62 |
63 | Here's the classic example to help you get started:
64 |
65 | For Reagent:
66 | [source,clojure]
67 | ----
68 | (require '[antizer.reagent :as ant])
69 | (require '[reagent.core :as r])
70 |
71 | (defn render []
72 | [ant/button {:on-click #(ant/message-info "Hello Reagent!")} "Click me"])
73 |
74 | (defn init! []
75 | (r/render [render] (.-body js/document)))
76 | ----
77 |
78 | For Rum:
79 | [source,clojure]
80 | ----
81 | (require '[antizer.rum :as ant])
82 | (require '[rum.core :as rum])
83 |
84 | (defn render []
85 | (ant/button {:on-click #(ant/message-info "Hello Rum!")} "Click me"))
86 |
87 | (defn init! []
88 | (rum/mount (render) (.-body js/document)))
89 | ----
90 |
91 | IMPORTANT: Please remember to include the CSS stylesheet https://cdnjs.com/libraries/antd[here], otherwise the components would not be styled correctly.
92 |
93 | TIP: It is not recommended to render to the element. This is only done for illustrative purposes.
94 |
95 | == Programming Guide
96 |
97 | === Naming convention
98 |
99 | In order to be consistent with ClojureScript naming conventions, all functions and properties are specified in lisp case
100 | (aka https://en.wikipedia.org/wiki/Naming_convention_%28programming%29#Multiple-word_identifiers[kebab-case]), eg: on-click, wrapper-col
101 |
102 | The Ant design component, functions and property have been translated to kebab-case, via the following rules:
103 |
104 | 1. Replace all instances of `.` with `-`
105 | 2. Insert a dash before any uppercase letters, except for the first letter.
106 | 3. Convert the whole string to lowercase.
107 |
108 | .Example of name mappings
109 | |===
110 | |Ant Design component or function name |Antizer name
111 |
112 | |Calendar
113 | |calendar
114 |
115 | |Checkbox.Group
116 | |checkbox-group
117 |
118 | |DatePicker.RangePicker
119 | |date-picker-range-picker
120 |
121 | |InputNumber
122 | |input-number
123 |
124 | |Modal.confirm
125 | |modal-confirm
126 |
127 | |notification.info
128 | |notification-info
129 |
130 | |===
131 |
132 | The full list of the Ant Design component and function names can be found https://github.com/priornix/antizer/blob/master/src/antizer/antd.clj[here].
133 |
134 | === Types
135 |
136 | As can be seen from the module list https://github.com/priornix/antizer/blob/master/src/antizer/antd.clj[here], there are three main types within the Antizer library that maps to the equivalent in the Ant Design library.
137 |
138 | * Functions
139 | * Properties
140 | * Components
141 |
142 | ==== Functions
143 |
144 | These are just normal ClojureScript functions. All the functions reside in their UI respective library namespaces:
145 |
146 | Example for reagent:
147 | [source,clojure]
148 | ----
149 | (require '[antizer.reagent :as ant])
150 |
151 | (ant/message-info "Hello World!")
152 | ----
153 |
154 | Example for rum:
155 | [source,clojure]
156 | ----
157 | (require '[antizer.rum :as ant])
158 |
159 | (ant/message-info "Hello World!")
160 | ----
161 |
162 | The full list of the Ant Design function names can be found https://github.com/priornix/antizer/blob/master/src/antizer/antd.clj#L82[here].
163 |
164 | ==== Components
165 |
166 | These are the backbone of the entire library. Please refer to the Components section of the respective
167 | ClojureScript React libraries.
168 |
169 | ==== Properties
170 |
171 | There is currently only one property type currently: `locales`. Please refer to the Localization section of
172 | the respective ClojureScript React libraries.
173 |
174 | === Return Values
175 | include::common/return-values.adoc[tags=text]
176 |
177 | == Reagent
178 |
179 | ==== Components
180 |
181 | The Antizer components can be treated like any Reagent components.
182 | To use them, wrap them in a Hiccup style list:
183 |
184 | [source,clojure]
185 | ----
186 | (require '[antizer.reagent :as ant])
187 |
188 | (defn render-menu []
189 | [ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}
190 | :on-click #(println "You have clicked" %1)}
191 | [ant/menu-item {:key "item1"} "Menu Item 1"]
192 | [ant/menu-item {:key "item2"} "Menu Item 2"]])
193 | ----
194 |
195 | include::common/component-1.adoc[]
196 |
197 | In some cases, certain components (eg: `menu-sub-menu` and `tooltip`) can accept React.Elements as their properties. In such cases, please use `(reagent.core/as-element)` to turn Reagent’s hiccup forms into React elements.
198 |
199 | [source,clojure]
200 | ----
201 | (require '[antizer.reagent :as ant])
202 | (require '[reagent.core :as r])
203 |
204 | (defn render-menu []
205 | [ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}}
206 | ;; adds a sub-menu containing a "home" icon and "SubMenu 1" text
207 | [ant/menu-sub-menu {:title
208 | (r/as-element
209 | [:span [ant/icon {:type "home"}] "SubMenu 1"])}]])
210 | ----
211 |
212 | The full list of the Ant Design component names can be found https://github.com/priornix/antizer/blob/master/src/antizer/antd.clj[here].
213 |
214 | ==== Forms
215 |
216 | include::common/form-1.adoc[]
217 |
218 | [source,clojure]
219 | ----
220 | (require '[antizer.reagent :as ant])
221 | (require '[reagent.core :as r])
222 |
223 | (defn actual-form []
224 | (fn [props]
225 | (let [my-form (ant/get-form)]
226 | [ant/form
227 | [ant/form-item {:label "Name"}
228 | (ant/decorate-field my-form "name"
229 | [ant/input])]
230 | [ant/form-item {:label "Password"}
231 | ;; validates that the password field is not empty
232 | (ant/decorate-field my-form "password" {:rules [{:required true}]}
233 | [ant/input])]])))
234 |
235 | (defn init-form []
236 | (ant/create-form (actual-form)))
237 |
238 | (defn init! []
239 | (r/render [init-form] (.-body js/document)))
240 | ----
241 |
242 | include::common/form-2.adoc[]
243 |
244 | In the case of Reagent, this can be done right at the start of the
245 | render stage, by wrapping the Reagent Hiccup elements with a function. Next, call
246 | `(antizer.reagent.get-form)` to obtain the form object that will be used by the
247 | field decorator and form functions.
248 |
249 | It is recommended to pass in the React props to the form render function so that the
250 | form object is accessible. This is needed in some cases like nested forms.
251 |
252 | TIP: Without the wrapper function, `(antizer.reagent.get-form)` would try to obtain
253 | the form value before the component has been constructed, and thus a `nil` value
254 | will be returned.
255 |
256 | include::common/form-3.adoc[]
257 |
258 | [source,clojure]
259 | ----
260 | (require '[antizer.reagent :as ant])
261 | (require '[reagent.core :as r])
262 |
263 | (defn actual-form []
264 | (fn [props]
265 | (let [my-form (ant/get-form)]
266 | [ant/form
267 | [ant/form-item {:label "Name"}
268 | (ant/decorate-field my-form "name" {:rules [{:required true :min 10}]}
269 | [ant/input])]
270 | [ant/form-item
271 | [ant/button {:on-click #(ant/validate-fields my-form)} "Submit"]
272 | [ant/button {:on-click #(ant/reset-fields my-form)} "Reset"]]])))
273 |
274 | (defn init-form []
275 | (ant/create-form (actual-form)))
276 |
277 | (defn init! []
278 | (r/render [init-form] (.-body js/document)))
279 | ----
280 |
281 | include::common/form-4.adoc[]
282 |
283 | [[localization]]
284 | ==== Localization / i18n
285 |
286 | ===== Locale Provider
287 |
288 | include::common/locale-1.adoc[]
289 |
290 | This is how it works:
291 |
292 | [source,clojure]
293 | ----
294 | (require '[antizer.reagent :as ant])
295 |
296 | (defn render-app []
297 | [ant/locale-provider {:locale (ant/locales "en_US")}
298 | [content]])
299 | ----
300 |
301 | include::common/locale-2.adoc[]
302 |
303 | To set the locale for Moment.js:
304 |
305 | [source,clojure]
306 | ----
307 | (require '[cljsjs.moment.locale.es])
308 |
309 | (defn render-app []
310 | [ant/locale-provider {:locale (ant/locales "es_ES")}
311 | [ant/calendar {:default-value (js/moment)}]])
312 | ----
313 |
314 | include::common/locale-3.adoc[]
315 |
316 | == Rum
317 |
318 | ==== Components
319 | The Antizer components can be treated like any other Rum components.
320 | This is how to make use of them:
321 |
322 | [source,clojure]
323 | ----
324 | (require '[antizer.rum :as ant])
325 |
326 | (defn render-menu []
327 | (ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}
328 | :on-click #(println "You have clicked" %1)}
329 | (ant/menu-item {:key "item1"} "Menu Item 1")
330 | (ant/menu-item {:key "item2"} "Menu Item 2")))
331 | ----
332 |
333 | include::common/component-1.adoc[]
334 |
335 | In some cases, certain components (eg: `menu-sub-menu` and `tooltip`) can accept React.Elements as their properties.
336 | In this case, simply send the hiccup form in:
337 |
338 | [source,clojure]
339 | ----
340 | (require '[antizer.rum :as ant])
341 |
342 | (defn render-menu []
343 | (ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}}
344 | ;; adds a sub-menu containing a "home" icon and "SubMenu 1" text
345 | (ant/menu-sub-menu {:title [:span (ant/icon {:type "home"}) "SubMenu 1"]})))
346 | ----
347 |
348 | The full list of the Ant Design component names can be found https://github.com/priornix/antizer/blob/master/src/antizer/antd.clj[here].
349 |
350 | ==== Forms
351 |
352 | include::common/form-1.adoc[]
353 |
354 | [source,clojure]
355 | ----
356 | (require '[antizer.rum :as ant])
357 | (require '[rum.core :as rum])
358 |
359 | (rum/defcs actual-form
360 | [state]
361 | (let [my-form (ant/get-form state)]
362 | (ant/form
363 | (ant/form-item {:label "Name"}
364 | (ant/decorate-field my-form "name"
365 | (ant/input)))
366 | (ant/form-item {:label "Password"}
367 | (ant/decorate-field my-form "password" {:rules [{:required true}]}
368 | (ant/input))))))
369 |
370 | (defn init-form []
371 | (ant/create-form actual-form))
372 |
373 | (defn init! []
374 | (rum/mount (init-form) (.-body js/document)))
375 | ----
376 |
377 | include::common/form-2.adoc[]
378 |
379 | In the case of Rum, use `rum.core/defcs` to define a component that receives the `state`
380 | parameter. Next, call `(antizer.rum/get-form)` with the `state` parameter to obtain the
381 | form object that will be used by the field decorator and form functions.
382 |
383 | include::common/form-3.adoc[]
384 |
385 | [source,clojure]
386 | ----
387 | (require '[antizer.rum :as ant])
388 | (require '[rum.core :as rum])
389 |
390 | (rum/defcs actual-form
391 | [state]
392 | (let [my-form (ant/get-form state)]
393 | (ant/form
394 | (ant/form-item {:label "Name"}
395 | (ant/decorate-field my-form "name" {:rules [{:required true :min 10}]}
396 | (ant/input)))
397 | (ant/form-item
398 | (ant/button {:on-click #(ant/validate-fields my-form)} "Submit")
399 | (ant/button {:on-click #(ant/reset-fields my-form)} "Reset")))))
400 |
401 | (defn init-form []
402 | (ant/create-form actual-form))
403 |
404 | (defn init! []
405 | (rum/mount (init-form) (.-body js/document)))
406 | ----
407 |
408 | include::common/form-4.adoc[]
409 |
410 | ==== Localization / i18n
411 |
412 | ===== Locale Provider
413 |
414 | include::common/locale-1.adoc[]
415 |
416 | This is how it works:
417 |
418 | [source,clojure]
419 | ----
420 | (require '[antizer.rum :as ant])
421 |
422 | (defn render-app []
423 | (ant/locale-provider {:locale (ant/locales "en_US")}
424 | (content)))
425 | ----
426 |
427 | include::common/locale-2.adoc[]
428 |
429 | To set the locale for Moment.js:
430 |
431 | [source,clojure]
432 | ----
433 | (require '[cljsjs.moment.locale.es])
434 | (require '[antizer.rum :as ant])
435 |
436 | (defn render-app []
437 | (ant/locale-provider {:locale (ant/locales "es_ES")}
438 | (ant/calendar {:default-value (js/moment)})))
439 | ----
440 |
441 | include::common/locale-3.adoc[]
442 |
443 | == Examples
444 |
445 | To compile the examples:
446 |
447 | [source,bash]
448 | ----
449 | lein with-profile +examples cljsbuild once
450 | ----
451 |
452 | To compile the examples and enable hot reloading with figwheel:
453 |
454 | [source,bash]
455 | ----
456 | lein with-profile +examples-dev figwheel
457 | ----
458 |
459 | After compilation, open up the respective HTML page in the `examples/resources` folder in your browser.
460 |
461 |
462 | == Troubleshooting
463 |
464 | * The table or auto complete component is not displaying the data.
465 |
466 | include::common/data-props.adoc[tags=text]
467 |
468 | * The text within the DatePicker and Calendar components are displayed in an incorrect language.
469 |
470 | Please refer to this known issue and workaround https://github.com/priornix/antizer#known-issues[here].
471 |
472 | == Acknowledgement
473 |
474 | Thanks to Ant Design, https://github.com/cljsjs/packages/tree/master/antd[cljsjs/antd], https://github.com/reagent-project/reagent[Reagent],
475 | https://github.com/tonsky/rum[Rum] and of course https://clojurescript.org[ClojureScript], without which this project would not be possible.
476 |
477 | == License
478 |
479 | Copyright © 2017 Michael Lim
480 |
481 | Licensed under Eclipse Public License (see LICENSE).
482 |
--------------------------------------------------------------------------------