;; -*- lisp -*-
;; See the file LICENCE for licence information.
(in-package :it.bese.ucw)
;; TODO better integrate ucw dirty checking and validation with dojo
(enable-sharpquote<>-syntax)
(enable-bracket-syntax)
(defmacro rendering-dojo-tooltip-for (id &body body)
"This macro will bind the html rendered in its body to the dom node given by ID as a tooltip."
`(<:span (@ "dojo:type" "tooltip" "dojo:connectid" ,id) :style "display: none"
,@body))
;; TODO move to parenscript? name?
(defun to-js-boolean (value)
(if value 'true 'false))
;;; This file contains some dojo widget wrappers. They are basically CLOS objects
;;; representing dojo widgets with their parameters.
(defmacro def-dojo-widget (name supers slots &rest args)
(flet ((find-arg (name)
(find name args :key #'car))
(delete-arg (arg)
(setf args (delete arg args :test #'eq))))
(let ((default-initargs (find-arg :default-initargs))
(dojo-type)
(metaclass (find-arg :metaclass)))
(setf args (copy-list args))
(if (listp name)
(setf dojo-type (second name)
name (first name)))
(if default-initargs
(progn
(delete-arg default-initargs)
(setf default-initargs (copy-list default-initargs)))
(setf default-initargs (list :default-initargs)))
(if metaclass
(delete-arg metaclass)
(setf metaclass (list :metaclass 'standard-component-class)))
(when dojo-type
(setf (getf (rest default-initargs) :dojo-type) dojo-type))
(unless (find 'dojo-widget supers)
(setf supers (append supers (list 'dojo-widget))))
`(defcomponent ,name ,supers ,slots
,@(when (> (length default-initargs) 1)
(list default-initargs))
,metaclass
,@args))))
(defcomponent dojo-widget (widget-component html-element)
((dojo-type :accessor dojo-type-of :initarg :dojo-type)
(widget-id :accessor widget-id))
(:documentation "An abstract dojo widget that does not render anything."))
(defmethod (setf dom-id) :after (value (self dojo-widget))
(setf (widget-id self) (strcat value "-widget")))
(defmethod rendered-form-fields ((self dojo-widget))
(list (widget-id self)))
(defmethod initialize-instance :after ((self dojo-widget) &key)
;; trigger our specialized method above
(setf (dom-id self) (dom-id self))
(setf (dojo-type-of self) (dojo-type-of self)))
(defcomponent simple-dojo-widget (dojo-widget)
()
(:documentation "A dojo widget which should be wrapped in a
with a dojoType=\"...\" attribute."))
(defmacro with-dojo-widget-tag ((widget &rest args) &body body)
;; if we started ajax rendering from here then do not render the dojo div, because it's
;; against the dojo contract to replace those dom nodes. rather render the body div, so
;; the client side will only replace that dom node...
;; TODO currently-ajax-rendered-component should be dropped if dijit rendering does not need it
`(if (eq (currently-ajax-rendered-component) ,widget)
(progn
,@body)
(<:div :id (widget-id ,widget)
(@ "dojo:type" (dojo-type-of ,widget) ,@args)
,@body)))
(defmethod render-widget-wrapper :around ((self simple-dojo-widget) next-render-method)
"Wrap the simple dojo widget in a single
tag."
(<:div :class (css-class self) :id (widget-id self) :style (css-style self)
(@ "dojo:type" (dojo-type-of self))
(funcall next-render-method)))
(defmethod render ((self simple-dojo-widget))
(<:div :id (widget-id self)
(@ "dojo:type" (dojo-type-of self))))
;;;
;;; dojo-content-pane - ContentPane
;;;
(def-dojo-widget (dojo-content-pane "ContentPane") ()
((body :initform nil :initarg :body :accessor body-of :component t)))
(defmethod render ((self dojo-content-pane))
(with-dojo-widget-tag (self)
(awhen (body-of self)
(etypecase it
(function (funcall it))
(component (render it))))))
;;;
;;; dojo-tab-container - TabContainer
;;;
(def-dojo-widget (dojo-tab-container "TabContainer") (switching-container)
((do-layout-p :initform nil :initarg :do-layout-p :accessor do-layout-p)
(remember-selected-tab-p :initform nil :initarg :remember-selected-tab-p :accessor remember-selected-tab-p))
(:default-initargs :forbid-ajax-rendering-p t :client-side-p t))
(defmethod render-widget-wrapper :around ((self dojo-tab-container) next-render-method)
;; we can't use with-dojo-widget-tag because TabContainer does not tolerate extra levels in its body
(<:div :id (dom-id self) :style (css-style self) :class (css-class self)
(@ "dojo:widgetId" (widget-id self)
"dojo:type" (dojo-type-of self)
"dojo:doLayout" (to-js-boolean (do-layout-p self))
"dojo:selectedChild" (awhen (container.current-component self)
(widget-id it))
"dojo:postInitialize" (when (remember-selected-tab-p self)
(js:js* `(ucw.widget.tab-container.setup-remember-selected-tab
,(widget-id self)))))
(funcall next-render-method)))
(defmethod render ((self dojo-tab-container))
(iter (for (nil . tab) in (container.contents self))
;; TODO selected tabs shouldn't be rendered in a second request
(render-ajax-stub tab)))
;;;
;;; dojo-tab
;;;
(def-dojo-widget dojo-tab (list-container dojo-content-pane)
((label :initform nil :initarg :label :accessor label-of)
(closablep :initform t :initarg :closablep :accessor closablep))
(:default-initargs :dom-id (js:gen-js-name-string :prefix "_dj-tab")))
(defmethod render-widget-wrapper :around ((self dojo-tab) next-render-method)
(with-dojo-widget-tag (self "dojo:label" (label-of self)
"dojo:closable" (to-js-boolean (closablep self))
"dojo:postInitialize"
(js:js* `(progn
(log.debug "Setting up content loader of tab " this)
(.set-handler
(dojo.widget.by-id ,(widget-id self))
(lambda (pane node)
(log.debug "AJAX-getting tab pane " pane)
(ucw.io.execute-ajax-action
(create :url
,(action-href
(register-ajax-action (:with-call/cc nil :make-new-frame nil)
(within-dom-replacements-tag
(ajax-render self))))
:forms-to-ask (array)
:progress-label ,#"progress-label.loading-tab"))))
(log.debug "Setting up on-close of tab " this)
,(when (closablep self)
`(dojo.event.connect this "onClose"
(lambda ()
(log.debug "Calling server to close the tab " ,(dom-id self))
,(js-to-lisp-rpc* (:progress-label #"progress-label.closing-tab"
:sync false)
()
(awhen (parent self)
(without-dirtyness-tracking
(unless (remove-component it (funcall (container.key-generator it) self))
(ucw.component.warn "Tab ~S was not found in the container when the close server callback was called" self)))))
(return true)))))))
(call-next-method)))
(defmethod ajax-render-new-tab ((self dojo-tab) &key (select t))
(within-xhtml-tag "tabs"
(render-ajax-stub self))
(}
(