snabberb 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +3 -0
- data/README.md +9 -1
- data/build.js +22 -0
- data/examples/rack/Gemfile.lock +4 -4
- data/examples/roda/Gemfile.lock +10 -10
- data/lib/snabberb.rb +17 -2
- data/lib/snabberb/version.rb +1 -1
- data/opal/snabberb.rb +1 -9
- data/opal/snabberb/component.rb +197 -6
- data/opal/vendor/snabbdom.js +989 -422
- metadata +6 -12
- data/opal/vendor/snabbdom-attributes.js +0 -71
- data/opal/vendor/snabbdom-class.js +0 -29
- data/opal/vendor/snabbdom-eventlisteners.js +0 -99
- data/opal/vendor/snabbdom-props.js +0 -30
- data/opal/vendor/snabbdom-style.js +0 -90
- data/opal/vendor/snabbdom-to-html.js +0 -4960
- data/opal/vendor/tovnode.js +0 -126
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a1c844a2e4053181db278aeee2cd4c97ccd982e783c6ffb2b6df48391fd0a7b
|
4
|
+
data.tar.gz: 53e6e1712e779c653f7d429048f44e77cb9be525619d5276ee441e00e4ccb92b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a0c84b3558683af920af98de8e341f0bb6c7d1a2320d1354a8cea76d7a73b83f6eaf99aae9115b5150e5f0643579b24c4b89076188756b04342dbae237db4a3
|
7
|
+
data.tar.gz: 1a3b2bed264e1443d38368dea5b05d93abe6f0a1544c0f88b25357d2c9e042456493418779721b250c0f52db5b9e3bccb33099ecb926ff92e409fbf7423bb8fe
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -105,7 +105,7 @@ class NestedExample < Snabberb::Component
|
|
105
105
|
h(:div, { style: { width: '100px' } }, [
|
106
106
|
h(:div, 'hello'),
|
107
107
|
])
|
108
|
-
]
|
108
|
+
])
|
109
109
|
end
|
110
110
|
end
|
111
111
|
```
|
@@ -150,6 +150,14 @@ Snabberb.prerender_script('LayoutClass', 'ApplicationClass', 'application_id', j
|
|
150
150
|
|
151
151
|
A detailed example can be found [in the Roda example](examples/roda).
|
152
152
|
|
153
|
+
### Generating HTML from a File
|
154
|
+
|
155
|
+
You can generate HTML from a component with a file.
|
156
|
+
|
157
|
+
Snabberb.html\_script('path/to/my\_component.rb', **needs)
|
158
|
+
|
159
|
+
This reads in the ruby file at the path and generates javascript that calls html on the CamelCased version of the file name.
|
160
|
+
|
153
161
|
## Installation
|
154
162
|
|
155
163
|
Add this line to your application's Gemfile:
|
data/build.js
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
// browserify build.js -p esmify -s snabbdom > opal/vendor/snabbdom.js
|
2
|
+
import { init } from './node_modules/snabbdom/build/package/init'
|
3
|
+
import { h } from './node_modules/snabbdom/build/package/h'
|
4
|
+
import { toVNode } from './node_modules/snabbdom/build/package/tovnode'
|
5
|
+
|
6
|
+
import { attributesModule } from './node_modules/snabbdom/build/package/modules/attributes'
|
7
|
+
import { classModule } from './node_modules/snabbdom/build/package/modules/class'
|
8
|
+
import { datasetModule } from './node_modules/snabbdom/build/package/modules/dataset'
|
9
|
+
import { eventListenersModule } from './node_modules/snabbdom/build/package/modules/eventlisteners'
|
10
|
+
import { propsModule } from './node_modules/snabbdom/build/package/modules/props'
|
11
|
+
import { styleModule } from './node_modules/snabbdom/build/package/modules/style'
|
12
|
+
|
13
|
+
module.exports.init = init
|
14
|
+
module.exports.h = h
|
15
|
+
module.exports.toVNode = toVNode
|
16
|
+
|
17
|
+
module.exports.attributesModule = attributesModule
|
18
|
+
module.exports.classModule = classModule
|
19
|
+
module.exports.datasetModule = datasetModule
|
20
|
+
module.exports.eventListenersModule = eventListenersModule
|
21
|
+
module.exports.propsModule = propsModule
|
22
|
+
module.exports.styleModule = styleModule
|
data/examples/rack/Gemfile.lock
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
snabberb (0.
|
4
|
+
snabberb (0.5.0)
|
5
5
|
opal (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ast (2.4.
|
10
|
+
ast (2.4.1)
|
11
11
|
c_lexer (2.6.4.1.1)
|
12
12
|
ast (~> 2.4.0)
|
13
13
|
parser (= 2.6.4.1)
|
@@ -16,7 +16,7 @@ GEM
|
|
16
16
|
parser (~> 2.6)
|
17
17
|
parser (2.6.4.1)
|
18
18
|
ast (~> 2.4.0)
|
19
|
-
rack (2.2.
|
19
|
+
rack (2.2.3)
|
20
20
|
|
21
21
|
PLATFORMS
|
22
22
|
ruby
|
@@ -28,4 +28,4 @@ DEPENDENCIES
|
|
28
28
|
snabberb!
|
29
29
|
|
30
30
|
BUNDLED WITH
|
31
|
-
2.1.
|
31
|
+
2.1.4
|
data/examples/roda/Gemfile.lock
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
snabberb (0.
|
4
|
+
snabberb (0.5.0)
|
5
5
|
opal (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ast (2.4.
|
10
|
+
ast (2.4.1)
|
11
11
|
c_lexer (2.6.4.1.1)
|
12
12
|
ast (~> 2.4.0)
|
13
13
|
parser (= 2.6.4.1)
|
14
|
-
concurrent-ruby (1.1.
|
14
|
+
concurrent-ruby (1.1.7)
|
15
15
|
execjs (2.7.0)
|
16
|
-
libv8 (
|
17
|
-
mini_racer (0.
|
18
|
-
libv8 (
|
16
|
+
libv8 (8.4.255.0)
|
17
|
+
mini_racer (0.3.1)
|
18
|
+
libv8 (~> 8.4.255)
|
19
19
|
opal (1.0.3)
|
20
20
|
ast (>= 2.3.0)
|
21
21
|
parser (~> 2.6)
|
22
|
-
opal-sprockets (0.4.
|
22
|
+
opal-sprockets (0.4.9.1.0.3.7)
|
23
23
|
opal (~> 1.0.0)
|
24
24
|
sprockets (~> 3.7)
|
25
25
|
tilt (>= 1.4)
|
26
26
|
parser (2.6.4.1)
|
27
27
|
ast (~> 2.4.0)
|
28
|
-
rack (2.2.
|
29
|
-
roda (3.
|
28
|
+
rack (2.2.3)
|
29
|
+
roda (3.36.0)
|
30
30
|
rack
|
31
31
|
sprockets (3.7.2)
|
32
32
|
concurrent-ruby (~> 1.0)
|
@@ -46,4 +46,4 @@ DEPENDENCIES
|
|
46
46
|
snabberb!
|
47
47
|
|
48
48
|
BUNDLED WITH
|
49
|
-
2.1.
|
49
|
+
2.1.4
|
data/lib/snabberb.rb
CHANGED
@@ -13,12 +13,12 @@ module Snabberb
|
|
13
13
|
wrap_h(obj)
|
14
14
|
when Array
|
15
15
|
wrap_a(obj)
|
16
|
-
when Numeric
|
16
|
+
when Numeric, TrueClass, FalseClass
|
17
17
|
obj
|
18
18
|
when nil
|
19
19
|
'Opal.nil'
|
20
20
|
else
|
21
|
-
wrap_s(obj)
|
21
|
+
wrap_s(obj.to_s)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -38,6 +38,21 @@ module Snabberb
|
|
38
38
|
"Opal.hash(#{args})"
|
39
39
|
end
|
40
40
|
|
41
|
+
# takes in a file and needs
|
42
|
+
# calls html on the CamelCased version of the file with the needs
|
43
|
+
def self.html_script(file, **needs)
|
44
|
+
klass = file.split('/').last
|
45
|
+
.split('.').first
|
46
|
+
.split('_').map(&:capitalize).join
|
47
|
+
|
48
|
+
script = <<~RUBY
|
49
|
+
#{File.read(file)}
|
50
|
+
#{klass}.html(`#{wrap(needs)}`)
|
51
|
+
RUBY
|
52
|
+
|
53
|
+
Opal.compile(script).strip.chomp(';')
|
54
|
+
end
|
55
|
+
|
41
56
|
def self.prerender_script(layout, application, application_id, javascript_include_tags: '', **needs)
|
42
57
|
needs = wrap(needs)
|
43
58
|
attach_func = wrap_s("Opal.$$.#{application}.$attach(\"#{application_id}\", #{needs})")
|
data/lib/snabberb/version.rb
CHANGED
data/opal/snabberb.rb
CHANGED
@@ -1,16 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'erb'
|
3
4
|
require 'native'
|
4
|
-
|
5
5
|
require 'vendor/snabbdom'
|
6
|
-
require 'vendor/snabbdom-attributes'
|
7
|
-
require 'vendor/snabbdom-class'
|
8
|
-
require 'vendor/snabbdom-eventlisteners'
|
9
|
-
require 'vendor/snabbdom-props'
|
10
|
-
require 'vendor/snabbdom-style'
|
11
|
-
require 'vendor/snabbdom-to-html'
|
12
|
-
require 'vendor/tovnode'
|
13
|
-
|
14
6
|
require 'snabberb/component'
|
15
7
|
|
16
8
|
module Snabberb
|
data/opal/snabberb/component.rb
CHANGED
@@ -5,6 +5,88 @@ module Snabberb
|
|
5
5
|
attr_accessor :node
|
6
6
|
attr_reader :root
|
7
7
|
|
8
|
+
%x{
|
9
|
+
const VOID = new Set([
|
10
|
+
'area',
|
11
|
+
'base',
|
12
|
+
'br',
|
13
|
+
'col',
|
14
|
+
'embed',
|
15
|
+
'hr',
|
16
|
+
'img',
|
17
|
+
'input',
|
18
|
+
'keygen',
|
19
|
+
'link',
|
20
|
+
'meta',
|
21
|
+
'param',
|
22
|
+
'source',
|
23
|
+
'track',
|
24
|
+
'wbr',
|
25
|
+
])
|
26
|
+
|
27
|
+
const IGNORE = new Set([
|
28
|
+
'attributes',
|
29
|
+
'childElementCount',
|
30
|
+
'children',
|
31
|
+
'classList',
|
32
|
+
'clientHeight',
|
33
|
+
'clientLeft',
|
34
|
+
'clientTop',
|
35
|
+
'clientWidth',
|
36
|
+
'currentStyle',
|
37
|
+
'firstElementChild',
|
38
|
+
'innerHTML',
|
39
|
+
'lastElementChild',
|
40
|
+
'nextElementSibling',
|
41
|
+
'ongotpointercapture',
|
42
|
+
'onlostpointercapture',
|
43
|
+
'onwheel',
|
44
|
+
'outerHTML',
|
45
|
+
'previousElementSibling',
|
46
|
+
'runtimeStyle',
|
47
|
+
'scrollHeight',
|
48
|
+
'scrollLeft',
|
49
|
+
'scrollLeftMax',
|
50
|
+
'scrollTop',
|
51
|
+
'scrollTopMax',
|
52
|
+
'scrollWidth',
|
53
|
+
'tabStop',
|
54
|
+
'tagName',
|
55
|
+
])
|
56
|
+
|
57
|
+
const BOOLEAN = new Set([
|
58
|
+
'disabled',
|
59
|
+
'visible',
|
60
|
+
'checked',
|
61
|
+
'readonly',
|
62
|
+
'required',
|
63
|
+
'allowfullscreen',
|
64
|
+
'autofocus',
|
65
|
+
'autoplay',
|
66
|
+
'compact',
|
67
|
+
'controls',
|
68
|
+
'default',
|
69
|
+
'formnovalidate',
|
70
|
+
'hidden',
|
71
|
+
'ismap',
|
72
|
+
'itemscope',
|
73
|
+
'loop',
|
74
|
+
'multiple',
|
75
|
+
'muted',
|
76
|
+
'noresize',
|
77
|
+
'noshade',
|
78
|
+
'novalidate',
|
79
|
+
'nowrap',
|
80
|
+
'open',
|
81
|
+
'reversed',
|
82
|
+
'seamless',
|
83
|
+
'selected',
|
84
|
+
'sortable',
|
85
|
+
'truespeed',
|
86
|
+
'typemustmatch',
|
87
|
+
])
|
88
|
+
}
|
89
|
+
|
8
90
|
# You can define needs in each component. They are automatically set as instance variables
|
9
91
|
#
|
10
92
|
# For example:
|
@@ -57,7 +139,7 @@ module Snabberb
|
|
57
139
|
end
|
58
140
|
|
59
141
|
def html
|
60
|
-
|
142
|
+
node_to_s(render)
|
61
143
|
end
|
62
144
|
|
63
145
|
# Building block for dom elements using Snabbdom h and Snabberb components.
|
@@ -96,11 +178,12 @@ module Snabberb
|
|
96
178
|
return unless request_ids.empty?
|
97
179
|
|
98
180
|
@@patcher ||= %x{snabbdom.init([
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
181
|
+
snabbdom.attributesModule,
|
182
|
+
snabbdom.classModule,
|
183
|
+
snabbdom.datasetModule,
|
184
|
+
snabbdom.eventListenersModule,
|
185
|
+
snabbdom.propsModule,
|
186
|
+
snabbdom.styleModule,
|
104
187
|
])}
|
105
188
|
node = @root.render
|
106
189
|
@@patcher.call(@root.node, node)
|
@@ -151,6 +234,114 @@ module Snabberb
|
|
151
234
|
end
|
152
235
|
end
|
153
236
|
end
|
237
|
+
|
238
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
239
|
+
def parse_sel(sel)
|
240
|
+
%x{
|
241
|
+
let tag = ''
|
242
|
+
let id = ''
|
243
|
+
const classes = {}
|
244
|
+
const parts = sel.split(".")
|
245
|
+
const last = parts.length - 1
|
246
|
+
|
247
|
+
parts.forEach((part, index) => {
|
248
|
+
if (index == last) {
|
249
|
+
part = part.split('#')
|
250
|
+
if (part.length > 1) id = part[1]
|
251
|
+
part = part[0]
|
252
|
+
index == 0 ? tag = part : classes[part] = true
|
253
|
+
} else if (!tag) {
|
254
|
+
tag = part
|
255
|
+
} else {
|
256
|
+
classes[part] = true
|
257
|
+
}
|
258
|
+
})
|
259
|
+
|
260
|
+
return {
|
261
|
+
tag: tag,
|
262
|
+
id: id,
|
263
|
+
classes: classes,
|
264
|
+
}
|
265
|
+
}
|
266
|
+
end
|
267
|
+
|
268
|
+
def node_to_s(vnode)
|
269
|
+
%x{
|
270
|
+
if (!vnode.sel) return self.$escape(vnode.text)
|
271
|
+
|
272
|
+
const sel = self.$parse_sel(vnode.sel)
|
273
|
+
|
274
|
+
for (const key in vnode.data.class) {
|
275
|
+
vnode.data.class[key] ? sel['classes'][key] = true : delete sel['classes'][key]
|
276
|
+
}
|
277
|
+
|
278
|
+
let attributes = {}
|
279
|
+
if (sel['id'].length > 0) attributes['id'] = sel['id']
|
280
|
+
|
281
|
+
const classes = Object.keys(sel['classes'])
|
282
|
+
if (classes.length > 0) attributes['class'] = classes.join(' ')
|
283
|
+
|
284
|
+
for (const key in vnode.data.attrs) {
|
285
|
+
attributes[key] = vnode.data.attrs[key]
|
286
|
+
}
|
287
|
+
|
288
|
+
for (const key in vnode.data.dataset) {
|
289
|
+
attributes['data-' + key] = vnode.data.dataset[key]
|
290
|
+
}
|
291
|
+
|
292
|
+
for (const key in vnode.data.props) {
|
293
|
+
if (!IGNORE.has(key)) {
|
294
|
+
const value = vnode.data.props[key]
|
295
|
+
|
296
|
+
if (BOOLEAN.has(key)) {
|
297
|
+
if (value) attributes[key] = key
|
298
|
+
} else {
|
299
|
+
attributes[key] = value
|
300
|
+
}
|
301
|
+
}
|
302
|
+
}
|
303
|
+
|
304
|
+
const styles = []
|
305
|
+
|
306
|
+
for (let key in vnode.data.style) {
|
307
|
+
const value = vnode.data.style[key]
|
308
|
+
key = key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
|
309
|
+
styles.push(key + ': ' + value)
|
310
|
+
}
|
311
|
+
|
312
|
+
if (styles.length > 0) attributes['style'] = styles.join('; ')
|
313
|
+
|
314
|
+
attributes = Object.keys(attributes).map(key =>
|
315
|
+
self.$escape(key) + '="' + self.$escape(attributes[key]) + '"'
|
316
|
+
)
|
317
|
+
|
318
|
+
const tag = sel['tag']
|
319
|
+
const elements = ['<' + tag]
|
320
|
+
if (attributes.length > 0) elements.push(' ' + attributes.join(' '))
|
321
|
+
elements.push('>')
|
322
|
+
|
323
|
+
if (!VOID.has(tag)) {
|
324
|
+
if (vnode.data.props && vnode.data.props.innerHTML) {
|
325
|
+
elements.push(vnode.data.props.innerHTML)
|
326
|
+
} else if (vnode.text) {
|
327
|
+
elements.push(self.$escape(vnode.text))
|
328
|
+
} else if (vnode.children) {
|
329
|
+
vnode.children.forEach(child =>
|
330
|
+
elements.push(self.$node_to_s(child))
|
331
|
+
)
|
332
|
+
}
|
333
|
+
|
334
|
+
elements.push('</' + tag + '>')
|
335
|
+
}
|
336
|
+
|
337
|
+
return elements.join('')
|
338
|
+
}
|
339
|
+
end
|
340
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
341
|
+
|
342
|
+
def escape(html)
|
343
|
+
ERB::Util.html_escape(html)
|
344
|
+
end
|
154
345
|
end
|
155
346
|
|
156
347
|
# Sublcass this to prerender applications.
|