cl-scopes/web/dom.lisp

80 lines
1.7 KiB
Common Lisp

;;;; cl-scopes/web/dom - "Data Output Model" = simple and dedicated HTML generator
(defpackage :scopes/web/dom
(:use :common-lisp)
(:local-nicknames (:util :scopes/util)
(:alx :alexandria))
(:export #:render #:text
#:dl #:link))
(in-package :scopes/web/dom)
;;;; basic / common stuff
(defvar *output* (make-string-output-stream))
(defmacro render (&body body)
`(let ((*output* (make-string-output-stream)))
,@body
(get-output-stream-string *output*)))
(defmacro text (s)
`(put-string (string ,s)))
(defmacro put-string (s)
`(write-string ,s *output*))
(defmacro put-char (c)
`(write-char ,c *output*))
(defmacro element (tag attrs &body body)
`(progn
(start ,tag ,attrs)
,@body
(end ,tag)))
(defun put-attrs (plist)
(loop for (key val . r) on plist by #'cddr do
(put-char #\Space)
(when val
(put-string (string-downcase key))
(when (not (eq val t))
(put-string "=\"")
(put-string (attr-str key val))
(put-char #\")))))
(defun attr-str (key val)
(case key
((:id :class) (util:to-string val))
(t (string val))))
(defun start (tag &optional attrs)
(put-char #\<)
(put-string tag)
(put-attrs attrs)
(put-char #\>))
(defun end (tag)
(put-string "</")
(put-string tag)
(put-char #\>))
(defun newline ()
(put-char #\Newline))
;;;; tag-specific renderers
(defun dl (attrs plist)
(element "dl" attrs
(loop for (key val . r) on plist by #'cddr do
(element "dt" nil (put-string (string-downcase key)))
(dd nil val))))
(defun dd (attrs v)
(if (atom v)
(setf v (list v)))
(dolist (el v)
(element "dd" attrs (text el))))
(defmacro link (attrs &body body)
`(element "a" ,attrs ,@body))