;; -*- 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)) (} (