-
Notifications
You must be signed in to change notification settings - Fork 10
/
patch.clj
144 lines (123 loc) · 4.43 KB
/
patch.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(ns ripley.live.patch
"Define patch methods components can use and how they are encoded in messages
and evaluated."
(:refer-clojure :exclude [replace])
(:require [ripley.impl.dynamic :as dynamic]
[clojure.java.io :as io]
[clojure.string :as str]))
(defmulti js-eval-script identity)
(defmulti make-patch "Create a patch message by keyword name"
(fn [patch-method _id & [_payload]]
patch-method))
(defmulti target-parent? identity)
(defmulti render-mode identity)
(defmacro define-patch-method
[name doc {:keys [type js-eval
payload? render-mode target-parent?]
:or {payload? true
render-mode :html
target-parent? false}}]
(let [pl (when payload? ['payload])
kw-name (keyword name)]
`(do
(defmethod js-eval-script
~type [_#]
~js-eval)
(defmethod make-patch ~kw-name [_# id# & [~'payload]]
~@(when payload?
[`(assert ~'payload ~(str "Patch method " (keyword name)
" (" type ") requires a payload"))])
[id# ~type ~@(when payload?
['payload])])
(defmethod target-parent? ~kw-name [_#] ~target-parent?)
(defmethod render-mode ~kw-name [_#] ~render-mode)
(defn ~name
~doc
([~@pl]
(~name dynamic/*component-id* ~@pl))
([component-id# ~@pl]
[component-id# ~type ~@pl])))))
(define-patch-method replace
"Replace the whole element's HTML content."
{:type "R"
:js-eval "ripley.R(elt,payload);"})
(define-patch-method append
"Append HTML to the end of element."
{:type "A"
:js-eval "elt.innerHTML += payload;"})
(define-patch-method prepend
"Prepend HTML to the start of element."
{:type "P"
:js-eval "ripley.P(elt,payload);"})
(define-patch-method delete
"Delete element."
{:type "D" :payload? false
:js-eval "elt.parentElement.removeChild(elt);"})
(define-patch-method delete-many
"Delete multiple elements."
{:type "D+"
:js-eval "payload.forEach(id=>{let e=_rg(id); e.parentNode.removeChild(e)});"})
(define-patch-method insert-after
"Insert HTML after the end of component."
{:type "F"
:js-eval "ripley.F(elt,payload);"})
(define-patch-method move-after
"Move existing live component after the end of component."
{:type "M"
:js-eval "elt.insertAdjacentElement(\"afterend\",ripley.get(payload));"})
(define-patch-method move-first
"Move existing child to be the first child of its parent."
{:type "<"
:js-eval "elt.parentElement.insertAdjacentElement(\"afterbegin\",elt);"})
(define-patch-method move-last
"Move existing child to be the last child of its parent."
{:type ">"
:js-eval "elt.parentElement.insertAdjacentElement(\"beforeend\",elt);"})
(define-patch-method child-order
"Set order of children"
{:type "O"
:js-eval "
var fc = elt.insertAdjacentElement(\"afterbegin\",_rg(payload[0]));
for(let i=1;i<payload.length;i++) {
fc = fc.insertAdjacentElement(\"afterend\",_rg(payload[i]));
}
"})
(define-patch-method attributes
"Set element attributes. Nil value removes attribute."
{:type "@"
:js-eval "for(var attr in payload) { ripley.setAttr(elt, attr, payload[attr]) }"
:target-parent? true
:render-mode :json})
(define-patch-method eval-js
"Eval js with 'this' bound to the live component element"
{:type "E"
:render-mode :json
:js-eval "(new Function(payload)).call(elt);"})
(define-patch-method template
"Fill element from template"
{:type "T"
:render-mode :json
:js-eval "ripley.T(elt,payload);"})
(define-patch-method callback-error
"Callback error handler."
{:type "CE"
:render-mode :json
:js-eval "ripley.handleResult('onfailure',payload[0], payload[1]);"})
(define-patch-method callback-success
"Callback success handler."
{:type "CO"
:render-mode :json
:js-eval "ripley.handleResult('onsuccess',payload[0], payload[1]);"})
(def live-client-script
(delay
(-> "live-client-template.js" io/resource slurp
(str/replace
"__PATCH__"
(str "switch(method) {\n"
(str/join "\n"
(for [type (keys (methods js-eval-script))]
(str "case \"" type "\":" (js-eval-script type) " break;")))
"\ndefault: let pf = window[\"ripley_patch_\"+method];
if(pf!==undefined) pf(elt,payload);
else console.error(\"Unrecognized patch method: \", method);"
"\n}")))))