;;;; pages.lisp -- html generation functions for dnd (in-package :dnd) ;;; RENDER PROTOCOL (defgeneric render (view object &key) (:documentation "Render OBJECT as VIEW. VIEW could be anything, but it is intended to be a keyword for usin in EQL method specializers.")) (defmacro defrender (view (spec &rest kwargs) &body body) "A helper macro for defining specializations of render." (let ((viewvar (gensym))) `(defmethod render ((,viewvar (eql ,view)) ,spec &key ,@kwargs) ,@body))) (defrender :list ((data list) (class "listview") (item-class "listitem")) "A catch all for rendering lists of renderable data items as unordered lists. CLASS is the lass string for the containing list. ITEM-CLASS is the class string for the contained list items." (with-html (:ol :class class (dolist (item data) (:li (render :list-item item :class item-class)))))) (defrender :three-column-layout (data) "A catch all specialization for rendering data in three columns. You must specialize :left :milsddle :right on your desired data type." (with-html (:div :class "three-column-layout" (:div :class "left-column" (render :left data)) (:div :class "middle-column" (render :middle data)) (:div :class "right-column" (render :right data))))) ;;; PAGES (defmacro with-page ((&key title) &body body) "A helper macro fordefining some standard page boilerplate." `(with-html-string (:doctype) (:html (:head (:title ,title)) (:body ,@body)))) (defclass/std doorkeeper () ((message))) (defun doorkeeper (&key (message "Come ye player, Wot's yer name?")) (render :page (make-instance 'doorkeeper :message message ))) (defrender :page ((page doorkeeper)) (with-page (:title "Tavern Door") (:h1 (message page)) (:form :method "POST" :action "/tavern-door" (:label :for "NICK" "Wut's yer handle?:") (:input :name "NICK") (:button :type "submit" "Announce Thyself")) (:h2 "Eh? Ye need to announce thyeself?") (:a :href "/register" "Follow me..."))) (defclass/std goddess-shrine () ()) (defrender :page ((page goddess-shrine)) (with-page (:title "A Sacred Shrine") (:header (:h1 "Pray and become a hero...")) (:form :method "POST" :action "/godess-shrine" (:label :for "NAME" "Enter the epithet by which the ages shall know thy hero:") (:input :name "NAME") (:button :type "submit" "Pray To The Goddess")))) (defclass/std player-registration () ()) (defrender :page ((page player-registration)) (with-page (:title "Register Player") (:header (:h1 "Choose a Nickname Player")) (:form :method "POST" :action "/register" (:label :for "NICK" "Choose a nickname. No spaces. Letters, Numbers, and -._") (:input :name "NICK" :placeholder "superbob") (:button :type "submit" "Register")))) (defun register () (render :page (make-instance 'player-registration))) (defclass/std tavern () ((player alerts))) (defun tavern (player) (render :page (make-instance 'tavern :player player))) (defrender :page ((tavern tavern)) (with-page (:title "A Bustling Tavern") (render :three-column-layout tavern))) (defun tavern-text (player) (with-page (:title "A Bustling Tavern") (render :page-text (make-instance 'tavern :player player)))) (defrender :page-text ((tavern tavern)) (let ((player (player tavern))) (with-html (render :details player) (:table (:tr (:td (:h4 "Your Heroes")) (:td (:h4 "Your Campaigns"))) (:tr (:td (:h4 "Gossip & Gab")) (:td (:h4 "Comrades in Arms"))))))) (defrender :left ((tavern tavern)) (let ((player (player tavern))) (with-html (render :details player) (:h4 "Your Heroes") (render :list (player-heroes player))))) (defrender :middle ((tavern tavern)) (with-html (:h4 "Your Campaigns ") (render :list (player-campaigns (player tavern))))) (defrender :right ((tavern tavern)) (with-html (:h4 "Gossip & Gab") (render :list (alerts tavern)) (:h4 "Comrades in Arms") (render :list (fetch-comrades (player tavern))))) (defrender :details ((player player)) (with-html (:div :class "player details" (:h3 "Welcome " (player-nick player))))) (defun navbar () (with-html (:nav :class "navbar" :aria-label "Navigation" (:div :class "logo" :aria-label "DND logo" "DND") (:ul :class "nav-links" :aria-label "Nav links" (:li (:a :href "/hero" :aria-label "Hero profile" "🧝")) (:li (:a :href "/inventory" :aria-label "Inventory" "🎒")) (:li (:a :href "/quests" :aria-label "Quests" "📜")) (:li (:a :href "/tavern" :aria-label "Tavern" "🍺")))))) (defun hall-of-heroes () (with-html (:ul :class "hall-of-heroes" (dolist (hero (all-heroes)) (:li (hero-name hero) "the" (hero-class hero) (hero-title hero))))))