2024-05-25 12:48:35 +00:00
|
|
|
(defpackage #:hsx/element
|
2024-05-25 03:00:39 +00:00
|
|
|
(:use #:cl)
|
2024-05-25 16:29:58 +00:00
|
|
|
(:export #:element-type
|
2024-05-25 03:00:39 +00:00
|
|
|
#:element-props
|
2024-05-25 15:57:06 +00:00
|
|
|
#:element-children
|
2024-05-25 03:00:39 +00:00
|
|
|
#:create-element
|
2024-05-27 07:03:23 +00:00
|
|
|
#:expand-component))
|
2024-05-25 12:48:35 +00:00
|
|
|
(in-package #:hsx/element)
|
2024-05-25 03:00:39 +00:00
|
|
|
|
2024-05-28 03:33:01 +00:00
|
|
|
|
2024-05-26 14:29:30 +00:00
|
|
|
;;;; class definitions
|
|
|
|
|
2024-05-25 03:00:39 +00:00
|
|
|
(defclass element ()
|
2024-05-25 16:29:58 +00:00
|
|
|
((type
|
|
|
|
:reader element-type
|
|
|
|
:initarg :type)
|
2024-05-25 03:00:39 +00:00
|
|
|
(props
|
|
|
|
:reader element-props
|
2024-05-25 15:57:06 +00:00
|
|
|
:initarg :props)
|
|
|
|
(children
|
|
|
|
:reader element-children
|
|
|
|
:initarg :children)))
|
2024-05-25 03:00:39 +00:00
|
|
|
|
2024-05-28 07:17:16 +00:00
|
|
|
(defclass tag-element (element) ())
|
2024-05-26 14:29:30 +00:00
|
|
|
|
|
|
|
(defclass html-tag-element (tag-element) ())
|
|
|
|
|
2024-05-28 07:17:16 +00:00
|
|
|
(defclass fragment-element (element) ())
|
2024-05-26 14:29:30 +00:00
|
|
|
|
|
|
|
(defclass component-element (element) ())
|
|
|
|
|
|
|
|
|
|
|
|
;;;; constructor
|
|
|
|
|
2024-05-25 16:29:58 +00:00
|
|
|
(defun create-element (type props &rest children)
|
2024-05-26 14:29:30 +00:00
|
|
|
(let ((elm (make-instance (cond ((functionp type) 'component-element)
|
|
|
|
((string= type "<>") 'fragment-element)
|
|
|
|
((string= type "html") 'html-tag-element)
|
|
|
|
(t 'tag-element))
|
2024-05-26 03:26:09 +00:00
|
|
|
:type type
|
|
|
|
:props props
|
|
|
|
:children (flatten children))))
|
2024-05-26 14:29:30 +00:00
|
|
|
(create-element-hook elm)
|
|
|
|
elm))
|
2024-05-25 11:09:09 +00:00
|
|
|
|
2024-05-25 03:00:39 +00:00
|
|
|
(defun flatten (x)
|
|
|
|
(labels ((rec (x acc)
|
|
|
|
(cond ((null x) acc)
|
|
|
|
((atom x) (cons x acc))
|
|
|
|
(t (rec
|
|
|
|
(car x)
|
|
|
|
(rec (cdr x) acc))))))
|
|
|
|
(rec x nil)))
|
2024-05-26 04:57:51 +00:00
|
|
|
|
2024-05-26 14:29:30 +00:00
|
|
|
(defmethod create-element-hook ((elm element)))
|
|
|
|
|
|
|
|
(defmethod create-element-hook ((elm fragment-element))
|
|
|
|
(when (element-props elm)
|
|
|
|
(error "Cannot pass props to fragment.")))
|
|
|
|
|
|
|
|
(defmethod create-element-hook ((elm component-element))
|
|
|
|
;dry-run to validate props
|
2024-05-27 07:03:23 +00:00
|
|
|
(expand-component elm))
|
2024-05-26 14:29:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
;;;; methods
|
|
|
|
|
|
|
|
(defmethod print-object ((elm tag-element) stream)
|
|
|
|
(with-accessors ((type element-type)
|
|
|
|
(props element-props)
|
|
|
|
(children element-children)) elm
|
|
|
|
(if children
|
|
|
|
(format stream (if (rest children)
|
|
|
|
"~@<<~a~a>~2I~:@_~<~@{~a~^~:@_~}~:>~0I~:@_</~a>~:>"
|
|
|
|
"~@<<~a~a>~2I~:_~<~a~^~:@_~:>~0I~_</~a>~:>")
|
|
|
|
type
|
|
|
|
(props->string props)
|
|
|
|
children
|
|
|
|
type)
|
|
|
|
(format stream "<~a~a></~a>"
|
|
|
|
type
|
|
|
|
(props->string props)
|
|
|
|
type))))
|
|
|
|
|
|
|
|
(defun props->string (props)
|
|
|
|
(with-output-to-string (stream)
|
|
|
|
(loop
|
|
|
|
:for (key value) :on props :by #'cddr
|
|
|
|
:do (format stream (if (typep value 'boolean)
|
|
|
|
"~@[ ~a~]"
|
2024-05-27 09:11:04 +00:00
|
|
|
" ~a=\"~a\"")
|
2024-05-26 14:29:30 +00:00
|
|
|
(string-downcase key)
|
|
|
|
value))))
|
|
|
|
|
|
|
|
(defmethod print-object ((elm html-tag-element) stream)
|
|
|
|
(format stream "<!DOCTYPE html>~%")
|
|
|
|
(call-next-method))
|
|
|
|
|
|
|
|
(defmethod print-object ((elm fragment-element) stream)
|
|
|
|
(with-accessors ((children element-children)) elm
|
|
|
|
(if children
|
|
|
|
(format stream (if (rest children)
|
|
|
|
"~<~@{~a~^~:@_~}~:>"
|
|
|
|
"~<~a~:>")
|
|
|
|
children))))
|
|
|
|
|
|
|
|
(defmethod print-object ((elm component-element) stream)
|
2024-05-27 07:03:23 +00:00
|
|
|
(print-object (expand-component elm) stream))
|
|
|
|
|
|
|
|
(defmethod expand-component ((elm component-element))
|
|
|
|
(with-accessors ((type element-type)
|
|
|
|
(props element-props)
|
|
|
|
(children element-children)) elm
|
|
|
|
(apply type (merge-children-into-props props children))))
|
|
|
|
|
|
|
|
(defun merge-children-into-props (props children)
|
|
|
|
(append props
|
|
|
|
(and children
|
|
|
|
(list :children children))))
|