From f6a085a952572c5edbba1d6c7179158603d6bf08 Mon Sep 17 00:00:00 2001 From: Khatchig Anteblian Date: Fri, 4 Aug 2023 12:30:53 -0400 Subject: [PATCH 1/2] Streamline form-helper function APIs Refactored form-helper functions into a simpler API to avoid verbose argument lists with dictionaries and keys. Also updated docstrings to reflect the new function APIs. --- src/joy/form-helper.janet | 153 +++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 69 deletions(-) diff --git a/src/joy/form-helper.janet b/src/joy/form-helper.janet index 283f4eb..f1bbbf9 100644 --- a/src/joy/form-helper.janet +++ b/src/joy/form-helper.janet @@ -2,110 +2,118 @@ (import ./helper :prefix "") (import ./csrf :prefix "") -(defn- field [kind val key & attrs] - [:input (merge {:type kind :name (string key) :value (get val key)} (table ;attrs))]) +(defn- field [kind name & attrs] + [:input (merge {:type kind :name (string name)} (struct ;attrs))]) -(def hidden-field - `(hidden-field val key & attrs) +(defn hidden-field + `(hidden-field name & attrs) Generates an html element where - val is a dictionary and key is the value html attribute of a key - in the val dictionary. If key is nil, an error will be thrown. + name is a keyword denoting the name html attribute. Ex. - (hidden-field {:a "a" :b "b"} :a :class "a-class" :style "a-style") - (hidden-field {:a "a" :b "b"} :b)` - (partial field "hidden")) + (hidden-field :myhiddenfield :value "hiddenvalue" :class "a-class" :style "a-style") + (hidden-field :api-token :value "secret-token") + (hidden-field :valueless-hidden-field)` + [name & attrs] + (field "hidden" name ;attrs)) -(def text-field - `(text-field val key & attrs) +(defn text-field + `(text-field name & attrs) Generates an html element where - val is a dictionary and key is the value html attribute of a key - in the val dictionary. If key is nil, an error will be thrown. + name is a keyword denoting the name html attribute. Ex. - (text-field {:a "a" :b "b"} :a :class "a-class" :style "a-style") - (text-field {:a "a" :b "b"} :b)` - (partial field "text")) + (text-field :username :placeholder "Enter Username" :class "a-class" :style "a-style") + (text-field :some-prefilled-text :value "I am prefilled!") + (text-field :text-field)` + [name & attrs] + (field "text" name ;attrs)) -(def email-field - `(email-field val key & attrs) +(defn email-field + `(email-field name & attrs) Generates an html element where - val is a dictionary and key is the value html attribute of a key - in the val dictionary. If key is nil, an error will be thrown. + name is a keyword denoting the name html attribute. Ex. - (email-field {:a "a" :b "b"} :a :class "a-class" :style "a-style") - (email-field {:a "a" :b "b"} :b)` - (partial field "email")) + (email-field :email-address :placeholder "Email" :value "me@example.com") + (email-field :email :class "a-class" :style "a-style") + (email-field :email)` + [name & attrs] + (field "email" name ;attrs)) -(def password-field - `(password-field val key & attrs) +(defn password-field + `(password-field name & attrs) Generates an html element where - val is a dictionary and key is the value html attribute of a key - in the val dictionary. If key is nil, an error will be thrown. + name is a keyword denoting the name html attribute. Ex. - (password-field {:a "a" :b "b"} :a :class "a-class" :style "a-style") - (password-field {:a "a" :b "b"} :b)` - (partial field "password")) + (password-field :pass-field :placeholder "Password" :class "a-class" :style "a-style") + (password-field :pswd :class "a-class" :style "a-style") + (password-field :pswd)` + [name & attrs] + (field "password" name ;attrs)) -(def file-field - `(file-field val key & attrs) +(defn file-field + `(file-field name & attrs) Generates an html element where - val is a dictionary and key is the value html attribute of a key - in the val dictionary. If key is nil, an error will be thrown. + name is a keyword denoting the name html attribute. Ex. - (file-field {:a "a" :b "b"} :a :class "a-class" :style "a-style") - (file-field {:a "a" :b "b"} :b)` - (partial field "file")) + (file-field :file-field :accept "image/*,.pdf") + (file-field :file-field :class "a-class" :style "a-style") + (file-field :file-field)` + [name & attrs] + (field "file" name ;attrs)) (defn checkbox-field - `(checkbox-field val key & attrs) + `(checkbox-field name checked? & attrs) Generates two inputs, one hidden and one checkbox - where val is a dictionary and key is the value html attribute of a key - in that val dictionary. The first checkbox input is hidden + where name is a keyword denoting the name html attribute, + and checked? is a boolean denoting whether the checkbox is + checked by default. Ex. - (checkbox-field {:enabled true} :enabled :class "a-class" :style "a-style") + (checkbox-field :neovim? true :true "you're cool" :false "reconsider") + (checkbox-field :something false :class "a-class" :style "a-style") => - - ` - [val key & attrs] - (let [checked (if (or (true? (get val key)) - (one? (get val key))) - {:checked ""} - {}) + + + + + ` + [name checked? & attrs] + (let [checked (if checked? {:checked ""} {}) attrs (struct ;attrs)] - - [[:input {:type "hidden" :name key :value (get attrs :false 0)}] - [:input (merge {:type "checkbox" :name key :value (get attrs :true 1)} + [(hidden-field name :value (get attrs :false 0)) + [:input (merge {:type "checkbox" :name (string name) :value (get attrs :true 1)} checked attrs)]])) (defn form-for - `Generates a
html element where action-args is a tuple + `(form-for action-args & body) + + Generates a html element where action-args is a tuple of [request route-keyword route-arg1 route-arg2...] and body is the rest of the form. The form requires the request for the csrf-token and any put, patch or delete http methods. @@ -115,21 +123,22 @@ (form-for [request :account/patch {:id 1}] (label :name "Account name") - (text-field {:name "name"} :name) + (text-field :name) (submit "Save name"))` [action-args & body] (let [[request] action-args - action (apply router/action-for (drop 1 action-args))] + action (apply router/action-for (drop 1 action-args)) + _method (action :_method)] [:form action body (csrf-field request) - (when (truthy? (action :_method)) - (hidden-field action :_method))])) + (when (truthy? _method) + (hidden-field :_method :value _method))])) (defn form-with - [request &opt options & body] - ` + `(form-with request &opt options & body) + Generates an html element where the request is the request dictionary and options are any form options. @@ -146,13 +155,14 @@ (form-with request {:route :account/new :enctype "multipart/form-data"} (label :name "name") - (file-field {} :name) + (file-field :name) (submit "Upload file")) (form-with request (merge (action-for :account/edit {:id 1}) {:enctype "multipart/form-data"}) (label :name "name") - (file-field {} :name) + (file-field :name) (submit "Upload file"))` + [request &opt options & body] (default options {}) (let [{:action action :route route} options action (if (truthy? action) @@ -160,16 +170,19 @@ (if (truthy? route) (router/action-for ;(if (indexed? route) route [route])) {:action ""})) - attrs (merge options action)] + attrs (merge options action) + _method (get attrs :_method)] [:form attrs body (csrf-field request) - (when (truthy? (get attrs :_method)) - (hidden-field attrs :_method))])) + (when (truthy? _method) + (hidden-field :_method :value _method))])) (defn label - `Generates a