reblocks-ui

2023-06-18

A set of UI widgets for Reblocks web framework!

Upstream URL

github.com/40ants/reblocks-ui

Author

Alexander Artemenko

License

BSD
README

Reblocks-UI

REBLOCKS-UI ASDF System Details

Introduction

Reblocks-ui brings Zurb Foundation styling to your Weblocks application.

When you inherit your widgets from reblocks-ui/core:ui-widget. Reblocks will fetch Zurb Foundation styles and JS automatically:

(defwiget my-widget (reeblocks-ui:ui-widget)
  ((...))

Simple Demo

This demo shows how to process form data in a callback and to update the widget accordingly:

(reblocks/widget:defwidget hello
    nil
  ((name :initform nil :accessor name)))

(defmethod reblocks/widget:render ((widget hello))
  (cond
   ((name widget)
    (reblocks/html:with-html
      (reblocks-ui/form:with-html-form (:post
                                        (lambda (&key name &allow-other-keys)
                                          (setf (name widget) nil)
                                          (reblocks/widget:update widget)))
        (:p "Hello " (name widget))
        (:p (:input :type "submit" :class "button alert" :value "Reset")))))
   (t
    (reblocks-ui/form:with-html-form (:post
                                      (lambda (&key name &allow-other-keys)
                                        (setf (name widget) name)
                                        (reblocks/widget:update widget)))
      (:p (:input :type "text" :name "name" :placeholder "Enter your name")
       (:input :type "submit" :class "button" :value "Add"))))))

Go to HTML documentation to see this code in action.

Core

API

class reblocks-ui/core:ui-widget (widget)

Use this class as a parent for all widgets, who use UI.

class reblocks-ui/core:widget (ui-widget)

Use this class as a parent for all widgets, who use UI. Warning: 'widget' was renamed to 'ui-widget' and will be removed after 2020-06-01.

variable reblocks-ui/core:*foundation-dependencies* (#<REBLOCKS/DEPENDENCIES:REMOTE-DEPENDENCY url: "https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js">

#<REBLOCKS/DEPENDENCIES:REMOTE-DEPENDENCY url: "https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css"> #<REBLOCKS-PARENSCRIPT:PARENSCRIPT-DEPENDENCY >)

Dependencies for widgets based on Foundation framework.

Also may be useful if you want to include them as a whole app's dependencies.

To calculate right integity value, use: curl https://url | openssl dgst -sha256 -binary | openssl enc -base64 -A

HTML Forms

Submit Confirmation

You might also want to warn user about some destructive actions.

To do this, provide REQUIRES-CONFIRMATION-P argument to the with-html-form macro. Optionally you might provide CONFIRMATION-QUESTION argument with a text of the question. Pay attention how does question changes when you are clicking a button in this demo:

(reblocks/widget:defwidget demo
    nil
  ((done :initform nil :accessor done)))

(defmethod reblocks/widget:render ((widget demo))
  (cond
   ((done widget)
    (reblocks/html:with-html
      (reblocks-ui/form:with-html-form (:post
                                        (lambda (&rest args)
                                          (declare (ignore args))
                                          (setf (done widget) nil)
                                          (reblocks/widget:update widget))
                                        :requires-confirmation-p t
                                        :confirm-question "Changed your mind?")
        (:p (:input :type "submit" :class "button alert" :value "Reset")))))
   (t
    (reblocks-ui/form:with-html-form (:post
                                      (lambda (&rest args)
                                        (declare (ignore args))
                                        (setf (done widget) t)
                                        (reblocks/widget:update widget))
                                      :requires-confirmation-p t)
      (:p (:input :type "submit" :class "button" :value "Submit"))))))

Go to HTML documentation to see this code in action.

Showing Errors

Form processing usually includes error checking. You can use error-placeholder and form-error-placeholder inside the body of with-html-form macro to mark places where errors should be show.

There can be only one form error placeholder and it will show errors which are not related to some particular field. Field error placeholders are named and usually should be placed above or below a field.

After you've used placeholder inside the form, use field-error function inside an action's code to display the error related to the field or just signal any ERROR to show a form-wide error.

An example below, shows both types of error. Enter "bob" as a login, to check how a form-wide error will look like:

(reblocks/widget:defwidget login-widget
    nil
  ((login :initform nil :accessor login)
   (password :initform nil :accessor password)))

(defun login-is-ok (login)
  (cond
   ((zerop (length login))
    (reblocks-ui/form:field-error "login" "Login is required."))
   ((not (alpha-char-p (elt login 0)))
    (reblocks-ui/form:field-error "login"
                                  "Login should start from a aplha character."))
   (t t)))

(defun password-is-ok (pass)
  (cond
   ((zerop (length pass))
    (reblocks-ui/form:field-error "password" "Password is required"))
   ((< (length pass) 8)
    (reblocks-ui/form:field-error "password"
                                  (format nil
                                          "Minimum length is 8 symbols. You entered ~A."
                                          (length pass))))
   ((zerop (count-if #'digit-char-p pass))
    (reblocks-ui/form:field-error "password"
                                  (format nil
                                          "Password should contain some digits.")))
   ((zerop (count-if #'alpha-char-p pass))
    (reblocks-ui/form:field-error "password"
                                  (format nil
                                          "Password should contains some alpha characters.")))
   (t t)))

(defmethod reblocks/widget:render ((widget login-widget))
  (cond
   ((and (login widget) (password widget))
    (reblocks/html:with-html
      (reblocks-ui/form:with-html-form (:post
                                        (lambda (&key &allow-other-keys)
                                          (setf (login widget) nil
                                                (login widget) nil)
                                          (reblocks/widget:update widget)))
        (:p (format nil "Hello, ~A." (login widget)))
        (:p (:input :type "submit" :class "button alert" :value "Reset")))))
   (t
    (reblocks-ui/form:with-html-form (:post
                                      (lambda
                                          (
                                           &key login password
                                           &allow-other-keys)
                                        (when (string-equal login "bob")
                                          (error "Login ~A is already taken."
                                                 login))
                                        (when
                                            (every #'identity
                                                   (list (login-is-ok login)
                                                         (password-is-ok
                                                          password)))
                                          (setf (login widget) login
                                                (password widget) password)
                                          (reblocks/widget:update widget))))
      (reblocks-ui/form:form-error-placeholder)
      (:p
       (:input :type "text" :name "login" :placeholder
        "Enter your login, it should start from the letter.")
       (reblocks-ui/form:error-placeholder "login")
       (:input :type "password" :name "password" :placeholder
        "Use chars and numbers")
       (reblocks-ui/form:error-placeholder "password")
       (:input :type "submit" :class "button" :value "Login"))))))

Go to HTML documentation to see this code in action.

API

macro reblocks-ui/form:with-html-form (METHOD-TYPE ACTION &KEY ID CLASS ENCTYPE (USE-AJAX-P T) EXTRA-SUBMIT-CODE REQUIRES-CONFIRMATION-P (CONFIRM-QUESTION "Are you sure?") (SUBMIT-FN "initiateFormAction("~A", $(this), "~A")")) &BODY BODY

Wraps a body with (:form ...) using reblocks/html:with-html.

  • METHOD-TYPE argument should be a keyword :GET or :POST.

  • ACTION argument should be a function callback which will be called on form submit. Form fields will be passed as keyword arguments, using their names. To make your code more robust, use &ALLOW-OTHER-KEYS in the lambda list.

  • ID, CLASS and ENCTYPE arguments are transformed into appropriate arguments of HTML <form ...>...</form> node.

  • EXTRA-SUBMIT-CODE argument might contain a list of string with simple JS code, which will be called on form submit before code provided in SUBMIT-FN argument.

  • By default, form submission is done using AJAX. If you want to do old-school GET or POST request, set USE-AJAX-P argument to NIL.

  • If REQUIRES-CONFIRMATION-P is true, then user will be asked a question defined by CONFIRM-QUESTION argument. Zurb Foundation's modal window will be used to show a popup. See Submit Confirmation section for an example of code.

class reblocks-ui/core:ui-widget (widget)

Use this class as a parent for all widgets, who use UI.

class reblocks-ui/form:error-placeholder (widget)

function reblocks-ui/form:error-placeholder name &key (widget-class 'error-placeholder)

This function creates and renders a widget to show an error message related to some form field.

It should be called inside with-html-form macro.

NAME argument should be a string denoting a form field. Later, you can call field-error function to signal an error from the action function. You will need to pass the NAME as the first argument to the field-error function.

function reblocks-ui/form:form-error-placeholder &key (widget-class 'form-error-placeholder)

This function creates and renders a widget to show an error for the whole form.

It should be called inside with-html-form macro.

Later, you can call form-error function to signal an error from the action function.

condition reblocks-ui/form:field-error (form-error)

function reblocks-ui/form:field-error name message

Signals an error which will be shown for the whole form.lisp

You need to use error-placeholder function inside the with-html-form macro to set a place where an error message should be shown. Otherwise, the error will be logged and ignored.

If there is no a error-placeholder (1 2) call with corresponding NAME argument, then error message can be shown for the whole form in a place where form-error-placeholder function was called.

condition reblocks-ui/form:form-error (error)

function reblocks-ui/form:form-error message

Signals an error which will be shown for the whole form.lisp

You need to use form-error-placeholder function inside the with-html-form macro to set a place where an error message should be shown. Otherwise, the error will be logged and ignored.

function reblocks-ui/form:get-field-errors-count

Returns total number of errors, reported by field-error function.

You can use this function and call form-error (1 2) or interrupt action if the result is not zero.

function reblocks-ui/form:get-field-errors field-name

Returns all errors, reported for the field with name given in FIELD-NAME.

function reblocks-ui/form:render-button NAME &KEY (VALUE (HUMANIZE-NAME NAME)) ID (CLASS "button") (ONCLICK "disableIrrelevantButtons(this);") DISABLEDP

Renders a button in a form.

  • NAME - name of the html control. The name is attributized before being rendered.

  • VALUE - a value on html control. Humanized name is default.

  • ID - id of the html control. Default is nil.

  • CLASS - a class used for styling. By default, "submit".

  • DISABLEDP - button is disabled if true.

function reblocks-ui/form:render-form-and-button NAME ACTION &KEY (VALUE (HUMANIZE-NAME NAME)) (METHOD :GET) BUTTON-ID (BUTTON-CLASS "button") (USE-AJAX-P T) FORM-ID FORM-CLASS

Renders a button within a form. This function can be used a short cut to quickly render a sumbit button.

function reblocks-ui/form:render-link action label &key (ajaxp t) id class title render-fn

Renders an action into a href link. If AJAXP is true (the default), the link will be rendered in such a way that the action will be invoked via AJAX or will fall back to a regular request if JavaScript is not available. When the user clicks on the link the action will be called on the server.

ACTION may be a function or a result of a call to reblocks/actions:make-action-url. ID, CLASS and TITLE represent their HTML counterparts. RENDER-FN is an optional function of one argument that is reponsible for rendering the link's content (i.e. its label). The default rendering function just calls PRINC-TO-STRING on the label and renders it with escaping. Internally, render-fn should use reblocks:with-html macro to write output into the right stream.

WARNING! This function generates ... element and any bot, crawling the internet will hit this action with GET request.

function reblocks-ui/form:render-textarea name &key (label (humanize-name name)) value id class disabledp

Renders a textarea.

  • NAME - name of the html control. The name is attributized before being rendered.

  • VALUE - a value on html control. Humanized name is default.

  • ID - id of the html control. Default is nil.

  • CLASS - a class used for styling. By default, "submit".

  • DISABLEDP - button is disabled if true.

Untitled

class reblocks-ui/popup:popup-widget (ui-widget)

This widgets shows a popup window.

Inherit from this class and define a method for render-popup-content generic-function. Then you will be able to instantiate your class instance and call show-popup generic function.

generic-function reblocks-ui/popup:show-popup widget

Shows popup window.

generic-function reblocks-ui/popup:hide-popup widget

Hides popup window.

generic-function reblocks-ui/popup:render-popup-content widget

Renders inner HTML for popup window. You need to define a method for this generic function and specialize it for your own class.

accessor reblocks-ui/popup:visible-p (popup-widget) (= nil)


[generated by 40ANTS-DOC]

Dependencies (10)

  • ci
  • doc
  • docs-builder
  • log4cl
  • log4cl-extras
  • parenscript
  • quri
  • reblocks
  • reblocks-lass
  • reblocks-parenscript
  • GitHub
  • Quicklisp