-
Notifications
You must be signed in to change notification settings - Fork 1
/
gooroo.go
579 lines (501 loc) · 20.8 KB
/
gooroo.go
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
// The Gooroo package gathers a set of cumulative functions allowing you
// to create web applications on the Frontend side.
// To do this purpose, it implements DOM manipulation features based on syscall/js
// and webassembly.
// Its objective is to explore the possibilities of a modern, lightweight and
// javascript independent web library.
package gooroo
import (
"fmt"
"runtime"
"strings"
"syscall/js"
"github.com/Matbabs/Gooroo/dom"
"github.com/Matbabs/Gooroo/utils"
)
// DomComponent represents an element of the DOM. This element can be a tag, an attribute,
// a layout or even a binding. Most DomComponents can be nested within each other thanks to
// variadic parameters.
type DomComponent func() string
// DomBinding is a structure that allows to retain the link between an event, a callback function
// and a potential value to update when the event is triggered.
// All binding is reapplied during rendering.
type domBinding struct {
event string
callback js.Func
value *any
}
// DomStore allows to keep the state of change of a value in the store.
type domStore struct {
value any
hasChanged bool
}
var (
// Represents the global variable "document" of a website, useful for DOM manipulation and some
// JavaScript interraction.
document js.Value = js.Global().Get(dom.HTML_DOCUMENT)
// List of paths to add CSS style sheets already imported into the website.
stylesheets = []string{}
// List of paths to add Js scripts already imported into the website.
scripts = []string{}
// Communication channel that generates a new rendering for each message sent within it.
state = make(chan bool, 1)
// List of DomBindings registered for the application rendering.
bindings = make(map[string][]domBinding)
// Store the last domComponent that have been focused
lastDomComponentFocused = ""
// Store of local variables recorded in the application state.
store = make(map[string]*domStore)
// Store of memoized variables.
storeMemo = make(map[string]any)
// Store of memoized functions.
storeCallback = make(map[string]*func(...any) any)
)
// Manipulate DOM
// Hangs a CSS file in the <head> content of the website.
func Css(filepath string) {
if !utils.Contains(stylesheets, filepath) {
stylesheets = append(stylesheets, filepath)
elem := document.Call(dom.JS_CREATE_ELEMENT, dom.HTML_LINK)
document.Get(dom.HTML_HEAD).Call(dom.JS_APPEND_CHILD, elem)
elem.Set(dom.JS_REL, dom.HTML_STYLESHEET)
elem.Set(dom.JS_HREF, filepath)
}
}
// Hangs a Js file in the <head> content of the website.
func Js(filepath string) {
if !utils.Contains(scripts, filepath) {
scripts = append(scripts, filepath)
elem := document.Call(dom.JS_CREATE_ELEMENT, dom.HTML_SCRIPT)
document.Get(dom.HTML_HEAD).Call(dom.JS_APPEND_CHILD, elem)
elem.Set(dom.JS_SRC, filepath)
}
}
// Triggers a rendering of the DOM, of all the DomComponents declared in parameters.
func Html(domComponents ...DomComponent) {
for i := range domComponents {
elem := document.Call(dom.JS_CREATE_ELEMENT, dom.HTML_DIV)
document.Get(dom.HTML_BODY).Call(dom.JS_APPEND_CHILD, elem)
elem.Set(dom.JS_INNER_HTML, domComponents[i]())
}
}
// Clean the content of a string to prevent injections related to innerHtml attributes.
func sanitizeHtml(htmlStr *string) {
tmp := document.Call(dom.JS_CREATE_ELEMENT, dom.HTML_DIV)
tmp.Set(dom.JS_INNER_HTML, *htmlStr)
*htmlStr = tmp.Get(dom.JS_TEXT_CONTENT).String()
}
// Removes the whole rendering from the DOM, including the DomComponents passed as parameters
// in the Html() function.
func clearContext() {
document.Get(dom.HTML_BODY).Set(dom.JS_INNER_HTML, "")
}
// Create a functional DomBinding set on its parameters.
func generateBinding(id string, event string, value *any, callbacks ...func(js.Value)) domBinding {
return domBinding{
event,
js.FuncOf(
func(_ js.Value, args []js.Value) any {
needToChanged := false
switch event {
case dom.JS_EVENT_CHANGE, dom.JS_EVENT_KEYUP, dom.JS_EVENT_KEYDOWN:
// change value when event is emitted before callbacks calls
*value = args[0].Get(dom.JS_TARGET).Get(dom.JS_VALUE).String()
needToChanged = true
case dom.JS_EVENT_FOCUS:
// set last focused
lastDomComponentFocused = id
}
if event == dom.JS_EVENT_CHANGE || event == dom.JS_EVENT_CLICK {
for i := range callbacks {
callbacks[i](args[0])
}
}
if needToChanged {
// force state change but keep updated value
setHasChanged(value, *value)
}
return nil
},
),
value,
}
}
// Applies all the bindings to the DOM elements concerned.
func setBindings() {
for id := range bindings {
elem := document.Call(dom.JS_GET_ELEMENT_BY_CLASSNAME, id).Index(0)
for i := range bindings[id] {
// add event listener
elem.Call(dom.JS_ADD_EVENT_LISTENER, bindings[id][i].event, bindings[id][i].callback)
switch bindings[id][i].event {
case dom.JS_EVENT_CHANGE:
// add actual value if defined in input
elem.Set(dom.JS_VALUE, *(bindings[id][i].value))
case dom.JS_EVENT_FOCUS:
// reset focus to input if last focused
if id == lastDomComponentFocused {
elem.Call(dom.JS_EVENT_FOCUS)
}
}
}
}
}
// Deletes all the DomBindings stored locally.
func unsetBindings() {
bindings = make(map[string][]domBinding)
}
// Change variable from store & updateState
func setHasChanged(variable *any, setVal any) {
for key := range store {
if variable == &store[key].value {
store[key].value = setVal
store[key].hasChanged = true
updateState()
}
}
}
// Checks if one or more variables in the store have been changed.
func detectHasChanged(variables ...*any) bool {
for i := range variables {
for key := range store {
if variables[i] == &store[key].value && store[key].hasChanged {
return true
}
}
}
return false
}
// Reset to zero of all the changes of each variable stored in the store.
func clearHasChange() {
for key := range store {
if store[key].hasChanged {
store[key].hasChanged = false
}
}
}
// Starts the library's renderer. Allows to re-trigger the renderings when the
// state changes (with a UseSate variable for example), through the state channel.
// Must take a lambda function func() containing the call to Html() as parameter
// to execute a rendering context.
func Render(context func()) {
updateState()
for {
<-state
clearContext()
unsetBindings()
context()
clearHasChange()
setBindings()
}
}
// Called to trigger in parallel a message sending in the chan state and consequently
// request the new rendering of the application.
func updateState() {
state <- true
}
// Returns a stateful value, and a function to update it.
// During the initial render, the returned state (state) is the same as the value
// passed as the first argument (initialState).
// The setState function is used to update the state. It accepts a new state value
// and enqueues a re-render of the DOM.
func UseState(initialValue any) (actualValue *any, f func(setterValue any)) {
_, file, no, _ := runtime.Caller(1)
key := utils.CallerToKey(file, no)
utils.MapInit(key, store, &domStore{initialValue, false})
return &store[key].value, func(setVal any) {
setHasChanged(&store[key].value, setVal)
}
}
// Accepts a function that contains imperative, possibly effectful code.
// The default behavior for effects is to fire the effect after every completed render.
// That way an effect is always recreated if one of its variables changes (in variadics params).
func UseEffect(callback func(), variables ...*any) {
if len(variables) == 0 || detectHasChanged(variables...) {
callback()
}
}
// Pass an inline callback and an array of dependencies. useCallback will return a memoized
// version of the callback that only changes if one of the dependencies has changed.
func UseCallback(callback func(...any) any, variables ...*any) *func(...any) any {
_, file, no, _ := runtime.Caller(1)
key := utils.CallerToKey(file, no)
utils.MapInit(key, storeCallback, &callback)
if len(variables) == 0 || detectHasChanged(variables...) {
storeCallback[key] = &callback
}
return storeCallback[key]
}
// Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized
// value when one of the dependencies has changed. This optimization helps to avoid expensive
// calculations on every render.
func UseMemo(callback func() any, variables ...*any) any {
_, file, no, _ := runtime.Caller(1)
key := utils.CallerToKey(file, no)
utils.MapInitCallback(key, storeMemo, callback)
if len(variables) == 0 || detectHasChanged(variables...) {
storeMemo[key] = callback()
}
return storeMemo[key]
}
// Generate code for the DOM
// Fires again if one of the insiders has a dom.ELEMENT_PARAM anchor, to hook
// the parameter as an attribute of the HTML element passed to the DOM.
func insertDomComponentParams(opener string, insiders ...DomComponent) (string, []DomComponent) {
var insidersWithoutParam []DomComponent
for _, insider := range insiders {
if strings.Contains(insider(), dom.ELEMENT_PARAM) {
split := (strings.Split(opener, dom.ELEMENT_PARAM_SPLIT))
opener = fmt.Sprintf("%s %s%s%s", split[0], strings.Split(insider(), dom.ELEMENT_PARAM)[1], dom.ELEMENT_PARAM_SPLIT, split[1])
} else {
insidersWithoutParam = append(insidersWithoutParam, insider)
}
}
return opener, insidersWithoutParam
}
// Returns the html rendering of the DomComponent reproduced recursively with all its DomComponents insiders
func htmlDomComponent(opener string, closer string, insiders ...DomComponent) DomComponent {
if len(insiders) > 0 {
htmlStr, insiders := insertDomComponentParams(opener, insiders...)
for _, insider := range insiders {
htmlStr += insider()
}
htmlStr += closer
return func() string { return htmlStr }
}
return func() string { return fmt.Sprintf("%s%s", opener, closer) }
}
// Same function as htmlDomComponent() but only if the condition in parameter is valid.
func If(condition bool, insiders ...DomComponent) DomComponent {
if condition {
htmlStr := ""
for _, insider := range insiders {
htmlStr += insider()
}
return func() string { return htmlStr }
}
return func() string { return "" }
}
// Same operation as htmlDomComponent() but applies the function passed in parameter for the
// whole array. The "key" element is used to make the link with the elements within the function.
func For[T string | int | int32 | int64 | float32 | float64 | bool | any](elements []T, keyDomComponent func(i int) DomComponent) DomComponent {
if len(elements) > 0 {
htmlStr := ""
for i := range elements {
htmlStr += keyDomComponent(i)()
}
return func() string { return htmlStr }
}
return func() string { return "" }
}
// DomComponents
// Declare an html element with the <div> tag.
func Div(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_DIV_OPENER, dom.HTML_DIV_CLOSER, insiders...)
}
// Declare an html element with the <p> tag.
func P[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_P_OPENER, textStr), dom.HTML_P_CLOSER, insiders...)
}
// Declare an html element with the <span> tag.
func Span[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_SPAN_OPENER, textStr), dom.HTML_SPAN_CLOSER, insiders...)
}
// Declare an html element with the <ul> tag.
func Ul(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_UL_OPENER, dom.HTML_UL_CLOSER, insiders...)
}
// Declare an html element with the <li> tag.
func Li(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_LI_OPENER, dom.HTML_LI_CLOSER, insiders...)
}
// Declare an html element with the <table> tag.
func Table(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_TABLE_OPENER, dom.HTML_TABLE_CLOSER, insiders...)
}
// Declare an html element with the <tr> tag.
func Tr(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_TR_OPENER, dom.HTML_TR_CLOSER, insiders...)
}
// Declare an html element with the <th> tag.
func Th(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_TH_OPENER, dom.HTML_TH_CLOSER, insiders...)
}
// Declare an html element with the <td> tag.
func Td(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_TD_OPENER, dom.HTML_TD_CLOSER, insiders...)
}
// Declare an html element with the <h1> tag.
func H1[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_H1_OPENER, textStr), dom.HTML_H1_CLOSER, insiders...)
}
// Declare an html element with the <h2> tag.
func H2[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_H2_OPENER, textStr), dom.HTML_H2_CLOSER, insiders...)
}
// Declare an html element with the <h3> tag.
func H3[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_H3_OPENER, textStr), dom.HTML_H3_CLOSER, insiders...)
}
// Declare an html element with the <h4> tag.
func H4[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_H4_OPENER, textStr), dom.HTML_H4_CLOSER, insiders...)
}
// Declare an html element with the <a> tag.
func A[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_A_OPENER, textStr), dom.HTML_A_CLOSER, insiders...)
}
// Declare an html element with the <form> tag.
func Form(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_FORM_OPENER, dom.HTML_FORM_CLOSER, insiders...)
}
// Declare an html element with the <textarea> tag.
func TextArea(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_TEXTAREA_OPENER, dom.HTML_TEXTAREA_CLOSER, insiders...)
}
// Declare an html element with the <select> tag.
func Select(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_SELECT_OPENER, dom.HTML_SELECT_CLOSER, insiders...)
}
// Declare an html element with the <option> tag.
func Option[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_OPTION_OPENER, textStr), dom.HTML_OPTION_CLOSER, insiders...)
}
// Declare an html element with the <input> tag.
func Input(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_INPUT_OPENER, "", insiders...)
}
// Declare an html element with the <button> tag.
func Button[T string | int | int32 | int64 | float32 | float64 | bool](text T, insiders ...DomComponent) DomComponent {
textStr := utils.AnyStr(text)
sanitizeHtml(&textStr)
return htmlDomComponent(fmt.Sprintf("%s%s", dom.HTML_BUTTON_OPENER, textStr), dom.HTML_BUTTON_CLOSER, insiders...)
}
// Declare an html element with the <img> tag.
func Img(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_IMG_OPENER, "", insiders...)
}
// Declare an html element with the <i> tag.
func I(insiders ...DomComponent) DomComponent {
return htmlDomComponent(dom.HTML_I_OPENER, "", insiders...)
}
// Declare an html element with the <br> tag.
func Br() DomComponent {
return htmlDomComponent(dom.HTML_BR_OPENER, "")
}
// Declare an html element with the <hr> tag.
func Hr() DomComponent {
return htmlDomComponent(dom.HTML_HR_OPENER, "")
}
// DomComponentsParams
// Declare an attribute of an html element with the value 'class='
func ClassName(className string) DomComponent {
sanitizeHtml(&className)
return func() string {
return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_CLASSNAME, className)
}
}
// Declare an attribute of an html element with the value 'style='
func Style(style string) DomComponent {
sanitizeHtml(&style)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_STYLE, style) }
}
// Declare an attribute of an html element with the value 'href='
func Href(href string) DomComponent {
sanitizeHtml(&href)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_HREF, href) }
}
// Declare an attribute of an html element with the value 'src='
func Src(src string) DomComponent {
sanitizeHtml(&src)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_SRC, src) }
}
// Declare an attribute of an html element with the value 'value='
func Value(value string) DomComponent {
sanitizeHtml(&value)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_VALUE, value) }
}
// Declare an attribute of an html element with the value 'id='
func Id(id string) DomComponent {
sanitizeHtml(&id)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_ID, id) }
}
// Declare an attribute of an html element with the value 'type='
func Type(_type string) DomComponent {
sanitizeHtml(&_type)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_TYPE, _type) }
}
// Declare an attribute of an html element with the value 'placeholder='
func Placeholder(placeholder string) DomComponent {
sanitizeHtml(&placeholder)
return func() string {
return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_PLACEHOLDER, placeholder)
}
}
// Declare an attribute of an html element with the value 'title='
func Title(title string) DomComponent {
sanitizeHtml(&title)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_TITLE, title) }
}
// DomComponentsParamsStructure
// Declare une configuration CSS dans l'attribut d'un element html avec la valeur 'style=',
// de manière a paramettrer un 'display: flex'
func FlexLayout(flow string, justify string, align string, gap string) DomComponent {
gapStr := utils.AnyStr(gap)
sanitizeHtml(&flow)
sanitizeHtml(&justify)
sanitizeHtml(&align)
sanitizeHtml(&gapStr)
layout := fmt.Sprintf("%s %s;%s %s;%s %s;%s %s;%s %s", dom.CSS_PARAM_DISPLAY, dom.CSS_PARAM_DISPLAY_FLEX,
dom.CSS_PARAM_FLOW, flow, dom.CSS_PARAM_JUSTIFY, justify, dom.CSS_PARAM_ALIGN, align, dom.CSS_PARAM_GAP, gapStr)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_STYLE, layout) }
}
// Declare une configuration CSS dans l'attribut d'un element html avec la valeur 'style=',
// de manière a paramettrer un 'display: grid'
func GridLayout[T string | int](columns T, rows T, gap string) DomComponent {
columnsStr := utils.AnyStr(columns)
rowsStr := utils.AnyStr(rows)
sanitizeHtml(&columnsStr)
sanitizeHtml(&rowsStr)
sanitizeHtml(&gap)
layout := fmt.Sprintf("%s %s;%s %s%s%s;%s %s%s%s;%s %s", dom.CSS_PARAM_DISPLAY, dom.CSS_PARAM_DISPLAY_GRID,
dom.CSS_PARAM_GRID_COLUMNS, dom.CSS_PARAM_GRID_REPEAT_OPENER, columnsStr, dom.CSS_PARAM_GRID_REPEAT_CLOSER,
dom.CSS_PARAM_GRID_ROWS, dom.CSS_PARAM_GRID_REPEAT_OPENER, rowsStr, dom.CSS_PARAM_GRID_REPEAT_CLOSER, dom.CSS_PARAM_GAP, gap)
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_STYLE, layout) }
}
// DomComponentsParamsBinding
// Declare a binding on the event 'click' on the attached element to trigger
// the function passed in parameter.
func OnClick(callbacks ...func(js.Value)) DomComponent {
_, file, no, _ := runtime.Caller(1)
key := utils.CallerToKey(file, no)
bindings[key] = append(bindings[key], generateBinding(key, dom.JS_EVENT_CLICK, nil, callbacks...))
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_CLASSNAME, key) }
}
// Declare a binding on the event 'change' on the attached element to trigger
// the function passed in parameter.
func OnChange(value *any, callbacks ...func(js.Value)) DomComponent {
_, file, no, _ := runtime.Caller(1)
key := utils.CallerToKey(file, no)
bindings[key] = append(bindings[key], generateBinding(key, dom.JS_EVENT_CHANGE, value, callbacks...))
bindings[key] = append(bindings[key], generateBinding(key, dom.JS_EVENT_KEYUP, value, callbacks...))
bindings[key] = append(bindings[key], generateBinding(key, dom.JS_EVENT_KEYDOWN, value, callbacks...))
bindings[key] = append(bindings[key], generateBinding(key, dom.JS_EVENT_FOCUS, value, callbacks...))
return func() string { return fmt.Sprintf("%s%s'%s'", dom.ELEMENT_PARAM, dom.HTML_PARAM_CLASSNAME, key) }
}