;; SX example API handlers — defhandler definitions ;; ;; These serve the live demos on the Examples docs pages. ;; Each handler's source is displayed in the "Server handler" code block ;; on its corresponding example page (self-referencing via handler-source). ;; --------------------------------------------------------------------------- ;; Click to Load ;; --------------------------------------------------------------------------- (defhandler click (&key) (let ((now (format-time (now) "%H:%M:%S"))) (~click-result :time now))) ;; --------------------------------------------------------------------------- ;; Form Submission ;; --------------------------------------------------------------------------- (defhandler form (&key) (let ((name (form-data "name"))) (~form-result :name name))) ;; --------------------------------------------------------------------------- ;; Polling ;; --------------------------------------------------------------------------- (defhandler poll (&key) (let ((now (format-time (now) "%H:%M:%S")) (count (inc-counter "poll" :max 10))) (~poll-result :time now :count count))) ;; --------------------------------------------------------------------------- ;; Delete Row ;; --------------------------------------------------------------------------- (defhandler delete (&key item-id) ;; Empty response — outerHTML swap removes the row "") ;; --------------------------------------------------------------------------- ;; Inline Edit ;; --------------------------------------------------------------------------- (defhandler edit-form (&key) (let ((value (request-arg "value"))) (~inline-edit-form :value value))) (defhandler edit-save (&key) (let ((value (form-data "value"))) (~inline-view :value value))) (defhandler edit-cancel (&key) (let ((value (request-arg "value"))) (~inline-view :value value))) ;; --------------------------------------------------------------------------- ;; Out-of-Band Swaps ;; --------------------------------------------------------------------------- (defhandler oob (&key) (let ((now (format-time (now) "%H:%M:%S"))) (<> (p :class "text-emerald-600 font-medium" "Box A updated!") (p :class "text-sm text-stone-500" "at " now) (div :id "oob-box-b" :sx-swap-oob "innerHTML" (p :class "text-violet-600 font-medium" "Box B updated via OOB!") (p :class "text-sm text-stone-500" "at " now))))) ;; --------------------------------------------------------------------------- ;; Lazy Loading ;; --------------------------------------------------------------------------- (defhandler lazy (&key) (let ((now (format-time (now) "%H:%M:%S"))) (~lazy-result :time now))) ;; --------------------------------------------------------------------------- ;; Infinite Scroll ;; --------------------------------------------------------------------------- (defhandler scroll (&key) (let ((page (or (parse-int (request-arg "page")) 2)) (start (+ (* (- page 1) 5) 1)) (next (+ page 1))) (<> (map (fn (i) (div :class "px-4 py-3 border-b border-stone-100 text-sm text-stone-700" "Item " i " — loaded from page " page)) (range start (+ start 5))) (if (<= next 6) (div :id "scroll-sentinel" :sx-get (str "/examples/api/scroll?page=" next) :sx-trigger "intersect once" :sx-target "#scroll-items" :sx-swap "beforeend" :class "p-3 text-center text-stone-400 text-sm" "Loading more...") (div :class "p-3 text-center text-stone-500 text-sm font-medium" "All items loaded."))))) ;; --------------------------------------------------------------------------- ;; Progress Bar ;; --------------------------------------------------------------------------- (defhandler progress-start (&key) (let ((job-id (new-job))) (~progress-status :percent 0 :job-id job-id))) (defhandler progress-status (&key) (let ((job-id (request-arg "job")) (percent (advance-job job-id))) (~progress-status :percent percent :job-id job-id))) ;; --------------------------------------------------------------------------- ;; Active Search ;; --------------------------------------------------------------------------- (defhandler search (&key) (let ((q (request-arg "q")) (results (filter-list LANGUAGES q))) (~search-results :items results :query q))) ;; --------------------------------------------------------------------------- ;; Inline Validation ;; --------------------------------------------------------------------------- (defhandler validate (&key) (let ((email (request-arg "email"))) (cond ((not email) (~validation-error :message "Email is required")) ((not (contains? email "@")) (~validation-error :message "Invalid email format")) ((contains? TAKEN_EMAILS (lower email)) (~validation-error :message (str email " is already taken"))) (t (~validation-ok :email email))))) ;; --------------------------------------------------------------------------- ;; Value Select ;; --------------------------------------------------------------------------- (defhandler values (&key) (let ((cat (request-arg "category")) (items (get VALUE_SELECT_DATA cat))) (if (empty? items) (option :value "" "No items") (map (fn (i) (option :value i i)) items)))) ;; --------------------------------------------------------------------------- ;; Reset on Submit ;; --------------------------------------------------------------------------- (defhandler reset-submit (&key) (let ((msg (or (form-data "message") "(empty)")) (now (format-time (now) "%H:%M:%S"))) (~reset-message :message msg :time now))) ;; --------------------------------------------------------------------------- ;; Edit Row ;; --------------------------------------------------------------------------- (defhandler editrow-form (&key row-id) (let ((row (get ROWS row-id))) (~edit-row-form :id row-id :name (get row "name") :price (get row "price") :stock (get row "stock")))) (defhandler editrow-save (&key row-id) (let ((name (form-data "name")) (price (form-data "price")) (stock (form-data "stock"))) (~edit-row-view :id row-id :name name :price price :stock stock))) (defhandler editrow-cancel (&key row-id) (let ((row (get ROWS row-id))) (~edit-row-view :id row-id :name (get row "name") :price (get row "price") :stock (get row "stock")))) ;; --------------------------------------------------------------------------- ;; Bulk Update ;; --------------------------------------------------------------------------- (defhandler bulk (&key) (let ((action (request-arg "action")) (ids (form-list "ids")) (status (if (= action "activate") "active" "inactive"))) (update-users ids :status status) (map (fn (u) (~bulk-row :id (get u "id") :name (get u "name") :email (get u "email") :status (get u "status"))) USERS))) ;; --------------------------------------------------------------------------- ;; Swap Positions ;; --------------------------------------------------------------------------- (defhandler swap-log (&key) (let ((mode (request-arg "mode")) (n (inc-counter "swap")) (now (format-time (now) "%H:%M:%S"))) (<> (div :class "px-3 py-2 text-sm text-stone-700" "[" now "] " mode " (#" n ")") (span :id "swap-counter" :sx-swap-oob "innerHTML" :class "self-center text-sm text-stone-500" "Count: " n)))) ;; --------------------------------------------------------------------------- ;; Select Filter (Dashboard) ;; --------------------------------------------------------------------------- (defhandler dashboard (&key) (let ((now (format-time (now) "%H:%M:%S"))) (<> (div :id "dash-header" :class "p-3 bg-violet-50 rounded mb-3" (h4 :class "font-semibold text-violet-800" "Dashboard Header") (p :class "text-sm text-violet-600" "Generated at " now)) (div :id "dash-stats" :class "grid grid-cols-3 gap-3 mb-3" (div :class "p-3 bg-emerald-50 rounded text-center" (p :class "text-2xl font-bold text-emerald-700" "142") (p :class "text-xs text-emerald-600" "Users")) (div :class "p-3 bg-blue-50 rounded text-center" (p :class "text-2xl font-bold text-blue-700" "89") (p :class "text-xs text-blue-600" "Orders")) (div :class "p-3 bg-amber-50 rounded text-center" (p :class "text-2xl font-bold text-amber-700" "$4.2k") (p :class "text-xs text-amber-600" "Revenue"))) (div :id "dash-footer" :class "p-3 bg-stone-100 rounded" (p :class "text-sm text-stone-500" "Last updated: " now))))) ;; --------------------------------------------------------------------------- ;; Tabs ;; --------------------------------------------------------------------------- (defhandler tabs (&key tab) (let ((content (get TAB_CONTENT tab))) (<> content (div :id "tab-buttons" :sx-swap-oob "innerHTML" :class "flex border-b border-stone-200" (map (fn (t) (~tab-btn :tab (first t) :label (last t) :active (if (= (first t) tab) "true" "false"))) TAB_LIST))))) ;; --------------------------------------------------------------------------- ;; Animations ;; --------------------------------------------------------------------------- (defhandler animate (&key) (let ((color (random-choice "bg-violet-100" "bg-emerald-100" "bg-blue-100" "bg-amber-100")) (now (format-time (now) "%H:%M:%S"))) (~anim-result :color color :time now))) ;; --------------------------------------------------------------------------- ;; Dialogs ;; --------------------------------------------------------------------------- (defhandler dialog (&key) (~dialog-modal :title "Confirm Action" :message "Are you sure you want to proceed?")) (defhandler dialog-close (&key) "") ;; --------------------------------------------------------------------------- ;; Keyboard Shortcuts ;; --------------------------------------------------------------------------- (defhandler keyboard (&key) (let ((key (request-arg "key")) (actions {:s "Search panel activated" :n "New item created" :h "Help panel opened"}) (action (get actions key))) (~kbd-result :key key :action action))) ;; --------------------------------------------------------------------------- ;; PUT / PATCH ;; --------------------------------------------------------------------------- (defhandler pp-edit-all (&key) (let ((p (get-profile))) (~pp-form-full :name (get p "name") :email (get p "email") :role (get p "role")))) (defhandler put-profile (&key) (let ((name (form-data "name")) (email (form-data "email")) (role (form-data "role"))) (~pp-view :name name :email email :role role))) (defhandler pp-cancel (&key) (let ((p (get-profile))) (~pp-view :name (get p "name") :email (get p "email") :role (get p "role")))) ;; --------------------------------------------------------------------------- ;; JSON Encoding ;; --------------------------------------------------------------------------- (defhandler json-echo (&key) (let ((data (request-json)) (body (json-pretty data)) (ct (request-header "content-type"))) (~json-result :body body :content-type ct))) ;; --------------------------------------------------------------------------- ;; Vals & Headers ;; --------------------------------------------------------------------------- (defhandler echo-vals (&key) (let ((vals (request-args))) (~echo-result :label "values" :items vals))) (defhandler echo-headers (&key) (let ((headers (request-headers :prefix "X-"))) (~echo-result :label "headers" :items headers))) ;; --------------------------------------------------------------------------- ;; Loading States ;; --------------------------------------------------------------------------- (defhandler slow (&key) (sleep 2000) (let ((now (format-time (now) "%H:%M:%S"))) (~loading-result :time now))) ;; --------------------------------------------------------------------------- ;; Request Abort (sync replace) ;; --------------------------------------------------------------------------- (defhandler slow-search (&key) (let ((delay (random-int 500 2000))) (sleep delay) (let ((q (request-arg "q"))) (~sync-result :query q :delay delay)))) ;; --------------------------------------------------------------------------- ;; Retry ;; --------------------------------------------------------------------------- (defhandler flaky (&key) (let ((n (inc-counter "flaky"))) (if (!= (mod n 3) 0) (error 503) (~retry-result :attempt n :message "Success! The endpoint finally responded."))))