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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6178782a46938a5569124c4fc28c148ed953a9bccf8cd7e4be2ba1835ca2b2c
4
- data.tar.gz: 925315dd99157e735c0c05771349473eee08381a567a4a532348e2287630a0d4
3
+ metadata.gz: 5a1c844a2e4053181db278aeee2cd4c97ccd982e783c6ffb2b6df48391fd0a7b
4
+ data.tar.gz: 53e6e1712e779c653f7d429048f44e77cb9be525619d5276ee441e00e4ccb92b
5
5
  SHA512:
6
- metadata.gz: b1b5b865a8bc623aef95f22f5c2cb1c53020ed7ec41d06b7f604cd88ba6ed94e677b0f765d23fd4bc96a7f09bac010263d2bcabe4c3894c8e7ee0ca882bbed0c
7
- data.tar.gz: 72f2d035af6f132b2a06fb165ea6ee20ab58ad50d165b41557ca028815b356808549346dd729cafc076d95c8642939cdf365f8a1ec9d045cde8b06eb25920c29
6
+ metadata.gz: 4a0c84b3558683af920af98de8e341f0bb6c7d1a2320d1354a8cea76d7a73b83f6eaf99aae9115b5150e5f0643579b24c4b89076188756b04342dbae237db4a3
7
+ data.tar.gz: 1a3b2bed264e1443d38368dea5b05d93abe6f0a1544c0f88b25357d2c9e042456493418779721b250c0f52db5b9e3bccb33099ecb926ff92e409fbf7423bb8fe
data/.gitignore CHANGED
@@ -8,6 +8,8 @@
8
8
  /tmp/
9
9
  /examples/roda/public
10
10
  /examples/roda/build
11
+ /node_modules/
12
+ package-lock.json
11
13
 
12
14
  .DS_STORE
13
15
  *.swp
@@ -7,6 +7,9 @@ Metrics/AbcSize:
7
7
  Metrics/BlockLength:
8
8
  Enabled: False
9
9
 
10
+ Metrics/ClassLength:
11
+ Enabled: False
12
+
10
13
  Metrics/CyclomaticComplexity:
11
14
  Enabled: False
12
15
 
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:
@@ -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
@@ -1,13 +1,13 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- snabberb (0.2.3)
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.0)
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.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.2
31
+ 2.1.4
@@ -1,32 +1,32 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- snabberb (0.2.5)
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.0)
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.6)
14
+ concurrent-ruby (1.1.7)
15
15
  execjs (2.7.0)
16
- libv8 (7.3.492.27.1)
17
- mini_racer (0.2.9)
18
- libv8 (>= 6.9.411)
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.8.1.0.3.7)
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.2)
29
- roda (3.30.0)
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.2
49
+ 2.1.4
@@ -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})")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Snabberb
4
- VERSION = '0.3.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -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
@@ -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
- `toHTML(#{render})`
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
- snabbdom_attributes.default,
100
- snabbdom_class.default,
101
- snabbdom_eventlisteners.default,
102
- snabbdom_props.default,
103
- snabbdom_style.default,
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.