├── 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 | --------------------------------------------------------------------------------