glimmer-dsl-web 0.0.10 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.10
1
+ 0.0.12
@@ -2,17 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-web 0.0.10 ruby lib
5
+ # stub: glimmer-dsl-web 0.0.12 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-web".freeze
9
- s.version = "0.0.10".freeze
9
+ s.version = "0.0.12".freeze
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2024-01-05"
15
- s.description = "Glimmer DSL for Web (Ruby in the Browser Web GUI Frontend Library) - Enables frontend GUI development with Ruby by adopting a DSL that follows web-like HTML syntax, enabling the transfer of HTML/CSS/JS skills to Ruby frontend development. This library relies on Opal Ruby.".freeze
14
+ s.date = "2024-01-07"
15
+ s.description = "Glimmer DSL for Web (Ruby in the Browser Web GUI Frontend Library) enables building Web GUI frontends using Ruby in the Browser, as per Matz's recommendation in his RubyConf 2022 keynote speech to replace JavaScript with Ruby. It aims at providing the simplest, most intuitive, most straight-forward, and most productive frontend library in existence. The library follows the Ruby way (with DSLs and TIMTOWTDI) and the Rails way (Convention over Configuration) while supporting both Unidirectional (One-Way) Data-Binding (using <=) and Bidirectional (Two-Way) Data-Binding (using <=>). Dynamic rendering (and re-rendering) of HTML content is also supported via Content Data-Binding. And, modular design is supported with Glimmer Web Components. You can finally live in pure Rubyland on the Web in both the frontend and backend with Glimmer DSL for Web! This library relies on Opal Ruby.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
18
18
  "CHANGELOG.md",
@@ -37,7 +37,10 @@ Gem::Specification.new do |s|
37
37
  "lib/glimmer-dsl-web/samples/hello/hello_form.rb",
38
38
  "lib/glimmer-dsl-web/samples/hello/hello_glimmer_component_helper/address_form.rb",
39
39
  "lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb",
40
+ "lib/glimmer-dsl-web/samples/hello/hello_observer.rb",
41
+ "lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb",
40
42
  "lib/glimmer-dsl-web/samples/hello/hello_world.rb",
43
+ "lib/glimmer-dsl-web/samples/regular/button_counter.rb",
41
44
  "lib/glimmer-dsl-web/vendor/jquery.js",
42
45
  "lib/glimmer/config/opal_logger.rb",
43
46
  "lib/glimmer/data_binding/element_binding.rb",
@@ -47,8 +50,10 @@ Gem::Specification.new do |s|
47
50
  "lib/glimmer/dsl/web/data_binding_expression.rb",
48
51
  "lib/glimmer/dsl/web/dsl.rb",
49
52
  "lib/glimmer/dsl/web/element_expression.rb",
53
+ "lib/glimmer/dsl/web/formatting_element_expression.rb",
50
54
  "lib/glimmer/dsl/web/general_element_expression.rb",
51
55
  "lib/glimmer/dsl/web/listener_expression.rb",
56
+ "lib/glimmer/dsl/web/observe_expression.rb",
52
57
  "lib/glimmer/dsl/web/p_expression.rb",
53
58
  "lib/glimmer/dsl/web/property_expression.rb",
54
59
  "lib/glimmer/dsl/web/select_expression.rb",
@@ -59,6 +64,7 @@ Gem::Specification.new do |s|
59
64
  "lib/glimmer/web/component.rb",
60
65
  "lib/glimmer/web/element_proxy.rb",
61
66
  "lib/glimmer/web/event_proxy.rb",
67
+ "lib/glimmer/web/formatting_element_proxy.rb",
62
68
  "lib/glimmer/web/listener_proxy.rb"
63
69
  ]
64
70
  s.homepage = "http://github.com/AndyObtiva/glimmer-dsl-web".freeze
@@ -74,7 +80,7 @@ Gem::Specification.new do |s|
74
80
  s.add_runtime_dependency(%q<opal>.freeze, ["= 1.8.2".freeze])
75
81
  s.add_runtime_dependency(%q<opal-rails>.freeze, ["= 2.0.3".freeze])
76
82
  s.add_runtime_dependency(%q<opal-async>.freeze, ["~> 1.4.0".freeze])
77
- s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.4.6".freeze])
83
+ s.add_runtime_dependency(%q<opal-jquery>.freeze, ["~> 0.5.0".freeze])
78
84
  s.add_runtime_dependency(%q<to_collection>.freeze, [">= 2.0.1".freeze, "< 3.0.0".freeze])
79
85
  s.add_development_dependency(%q<puts_debuggerer>.freeze, [">= 0".freeze])
80
86
  s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0".freeze, "< 14.0.0".freeze])
@@ -21,8 +21,9 @@ module Glimmer
21
21
  if block.source_location && (block.source_location == parent.content&.__getobj__&.source_location)
22
22
  parent.content.call(parent) unless parent.content.called?
23
23
  else
24
- super
24
+ super(parent, keyword, *args, &block)
25
25
  end
26
+ parent.post_add_content
26
27
  end
27
28
  end
28
29
  end
@@ -1,5 +1,27 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'glimmer/dsl/engine'
2
23
  require 'glimmer/dsl/web/element_expression'
24
+ require 'glimmer/dsl/web/formatting_element_expression'
3
25
  require 'glimmer/dsl/web/listener_expression'
4
26
  require 'glimmer/dsl/web/property_expression'
5
27
  require 'glimmer/dsl/web/p_expression'
@@ -9,6 +31,7 @@ require 'glimmer/dsl/web/data_binding_expression'
9
31
  require 'glimmer/dsl/web/content_data_binding_expression'
10
32
  require 'glimmer/dsl/web/shine_data_binding_expression'
11
33
  require 'glimmer/dsl/web/component_expression'
34
+ require 'glimmer/dsl/web/observe_expression'
12
35
 
13
36
  module Glimmer
14
37
  module DSL
@@ -22,6 +45,7 @@ module Glimmer
22
45
  property
23
46
  content_data_binding
24
47
  shine_data_binding
48
+ formatting_element
25
49
  element
26
50
  ]
27
51
  )
@@ -0,0 +1,19 @@
1
+ require 'glimmer/dsl/expression'
2
+
3
+ require 'glimmer/web/formatting_element_proxy'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Web
8
+ class FormattingElementExpression < Expression
9
+ def can_interpret?(parent, keyword, *args, &block)
10
+ Glimmer::Web::FormattingElementProxy.keyword_supported?(keyword, parent: parent)
11
+ end
12
+
13
+ def interpret(parent, keyword, *args, &block)
14
+ Glimmer::Web::FormattingElementProxy.format(keyword, *args, &block)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/dsl/static_expression'
23
+ require 'glimmer/dsl/top_level_expression'
24
+ require 'glimmer/dsl/observe_expression'
25
+ require 'glimmer/web/component'
26
+
27
+ module Glimmer
28
+ module DSL
29
+ module SWT
30
+ class ObserveExpression < StaticExpression
31
+ include TopLevelExpression
32
+ include Glimmer::DSL::ObserveExpression
33
+
34
+ def interpret(parent, keyword, *args, &block)
35
+ observer_registration = super(parent, keyword, *args, &block)
36
+ Glimmer::Web::Component.interpretation_stack.last&.observer_registrations&.push(observer_registration)
37
+ observer_registration
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -15,13 +15,11 @@ module GlimmerHelper
15
15
  component_args_json = JSON.dump(component_args)
16
16
  opal_script = <<~Opal
17
17
  require 'glimmer-dsl-web'
18
- Document.ready? do
19
- component_args_json = '#{component_args_json}'
20
- component_args = JSON.parse(component_args_json)
21
- component_args << {} if !component_args.last.is_a?(Hash)
22
- component_args.last[:parent] = "##{component_id}"
23
- #{component_class_name}.render(*component_args)
24
- end
18
+ component_args_json = '#{component_args_json}'
19
+ component_args = JSON.parse(component_args_json)
20
+ component_args << {} if !component_args.last.is_a?(Hash)
21
+ component_args.last[:parent] = "##{component_id}"
22
+ #{component_class_name}.render(*component_args)
25
23
  Opal
26
24
  content_tag(:div, id: component_script_container_id, class: ['glimmer_component_script_container', "#{component_file}_script_container"]) do
27
25
  content_tag(:div, '', id: component_id, class: ['glimmer_component', component_file]) +
@@ -176,6 +176,10 @@ module Glimmer
176
176
  def reset_component_namespaces
177
177
  @component_namespaces = Set[Object, Glimmer::Web]
178
178
  end
179
+
180
+ def interpretation_stack
181
+ @interpretation_stack ||= []
182
+ end
179
183
  end
180
184
  # <- end of class methods
181
185
 
@@ -184,6 +188,7 @@ module Glimmer
184
188
  alias parent_proxy parent
185
189
 
186
190
  def initialize(parent, args, options, &content)
191
+ Component.interpretation_stack.push(self)
187
192
  @parent = parent
188
193
  options = args.delete_at(-1) if args.is_a?(Array) && args.last.is_a?(Hash)
189
194
  if args.is_a?(Hash)
@@ -203,6 +208,14 @@ module Glimmer
203
208
  @parent ||= @markup_root.parent
204
209
  raise Glimmer::Error, 'Invalid Glimmer web component for having an empty markup! Please fill markup block!' if @markup_root.nil?
205
210
  execute_hooks('after_render')
211
+
212
+ # TODO adapt for web
213
+ observer_registration_cleanup_listener = proc do
214
+ observer_registrations.compact.each(&:deregister)
215
+ observer_registrations.clear
216
+ end
217
+ @markup_root.handle_observation_request('on_remove', observer_registration_cleanup_listener)
218
+ post_add_content if content.nil?
206
219
  end
207
220
 
208
221
  # Subclasses may override to perform post initialization work on an added child
@@ -210,6 +223,15 @@ module Glimmer
210
223
  # No Op by default
211
224
  end
212
225
 
226
+ def post_add_content
227
+ Component.interpretation_stack.pop
228
+ end
229
+
230
+ # This stores observe keyword registrations of model/attribute observers
231
+ def observer_registrations
232
+ @observer_registrations ||= []
233
+ end
234
+
213
235
  def can_handle_observation_request?(observation_request)
214
236
  observation_request = observation_request.to_s
215
237
  result = false
@@ -139,11 +139,14 @@ module Glimmer
139
139
  @children.dup.each do |child|
140
140
  child.remove
141
141
  end
142
+ on_remove_listeners = listeners_for('on_remove').dup
142
143
  remove_all_listeners
143
144
  dom_element.remove
144
145
  parent&.post_remove_child(self)
145
146
  @removed = true
146
- # listeners_for('widget_removed').each {|listener| listener.call(Event.new(widget: self))}
147
+ on_remove_listeners.each do |listener|
148
+ listener.original_event_listener.call(EventProxy.new(listener: listener))
149
+ end
147
150
  end
148
151
 
149
152
  def remove_all_listeners
@@ -217,10 +220,12 @@ module Glimmer
217
220
  if parent_selector
218
221
  Document.find(parent_selector)
219
222
  else
220
- # TODO consider moving this to initializer
221
223
  options[:parent] ||= 'body'
222
224
  the_element = Document.find(options[:parent])
223
- the_element = Document.find('body') if the_element.length == 0
225
+ if the_element.length == 0
226
+ options[:parent] = 'body'
227
+ the_element = Document.find('body')
228
+ end
224
229
  the_element
225
230
  end
226
231
  end
@@ -297,6 +302,7 @@ module Glimmer
297
302
 
298
303
  def dom
299
304
  # TODO auto-convert known glimmer attributes like parent to data attributes like data-parent
305
+ # TODO check if we need to avoid rendering content block if no content is available
300
306
  @dom ||= html {
301
307
  send(keyword, html_options) {
302
308
  args.first if args.first.is_a?(String)
@@ -433,22 +439,12 @@ module Glimmer
433
439
  end
434
440
 
435
441
  def handle_observation_request(keyword, original_event_listener)
436
- # case keyword
437
- # when 'on_widget_removed'
438
- # listeners_for(keyword.sub(/^on_/, '')) << original_event_listener.to_proc
439
- # else
440
- handle_javascript_observation_request(keyword, original_event_listener)
441
- # end
442
- end
443
-
444
- def handle_javascript_observation_request(keyword, original_event_listener)
445
442
  listener = ListenerProxy.new(
446
443
  element: self,
447
444
  selector: selector,
448
445
  dom_element: dom_element,
449
446
  event_attribute: keyword,
450
- listener: original_event_listener,
451
- original_event_listener: original_event_listener
447
+ original_event_listener: original_event_listener,
452
448
  )
453
449
  listener.register
454
450
  listeners_for(keyword) << listener
@@ -24,9 +24,11 @@ module Glimmer
24
24
  class EventProxy
25
25
  attr_reader :js_event, :listener
26
26
 
27
- def initialize(js_event:, listener:)
28
- @js_event = js_event
27
+ # Instantiates EventProxy
28
+ # When js_event is nil, it is a custom event
29
+ def initialize(listener:, js_event: nil)
29
30
  @listener = listener
31
+ @js_event = js_event
30
32
  end
31
33
 
32
34
  def element = listener.element
@@ -34,6 +36,7 @@ module Glimmer
34
36
  def event_attribute = listener.event_attribute
35
37
 
36
38
  def original_event
39
+ return if js_event.nil?
37
40
  Native(`#{js_event.to_n}.originalEvent`)
38
41
  end
39
42
 
@@ -41,14 +44,14 @@ module Glimmer
41
44
  property_name = method_name.to_s.camelcase
42
45
  super(method_name, include_private) ||
43
46
  js_event.respond_to?(method_name, include_private) ||
44
- `#{property_name} in #{original_event.to_n}`
47
+ (original_event && `#{property_name} in #{original_event.to_n}`)
45
48
  end
46
49
 
47
50
  def method_missing(method_name, *args, &block)
48
51
  property_name = method_name.to_s.camelcase
49
52
  if js_event.respond_to?(method_name, true)
50
53
  js_event.send(method_name, *args, &block)
51
- elsif `#{property_name} in #{original_event.to_n}`
54
+ elsif (original_event && `#{property_name} in #{original_event.to_n}`)
52
55
  original_event[property_name]
53
56
  else
54
57
  super(method_name, *args, &block)
@@ -0,0 +1,56 @@
1
+ # backtick_javascript: true
2
+
3
+ # Copyright (c) 2023-2024 Andy Maleh
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module Glimmer
25
+ module Web
26
+ class FormattingElementProxy
27
+ class << self
28
+ include Glimmer
29
+
30
+ def keyword_supported?(keyword, parent: nil)
31
+ keyword = keyword.to_s
32
+ (
33
+ FORMATTING_ELEMENT_KEYWORDS.include?(keyword) ||
34
+ (parent&.keyword == 'p' && keyword == 'span')
35
+ )
36
+ end
37
+
38
+ def format(keyword, *args, &block)
39
+ content = nil
40
+ if block_given?
41
+ content = block.call.to_s
42
+ elsif args.any? && !args.first.is_a?(Hash)
43
+ content = args.first.to_s
44
+ end
45
+ attribute_hash = args.last.is_a?(Hash) ? args.last : {}
46
+ content_block = proc { content } unless content.nil?
47
+ html {
48
+ send(keyword, attribute_hash, &content_block)
49
+ }.to_s
50
+ end
51
+ end
52
+
53
+ FORMATTING_ELEMENT_KEYWORDS = %w[b i strong em sub sup del ins small mark br]
54
+ end
55
+ end
56
+ end
@@ -24,33 +24,34 @@ require 'glimmer/web/event_proxy'
24
24
  module Glimmer
25
25
  module Web
26
26
  class ListenerProxy
27
- attr_reader :element, :event_attribute, :event_name, :dom_element, :selector, :listener, :js_listener, :original_event_listener
27
+ attr_reader :element, :event_attribute, :event_name, :dom_element, :selector, :js_listener, :original_event_listener
28
28
 
29
- def initialize(element:, event_attribute:, dom_element:, selector:, listener:)
29
+ def initialize(element:, event_attribute:, dom_element:, selector:, original_event_listener:)
30
30
  @element = element
31
31
  @event_attribute = event_attribute
32
- @event_name = event_attribute.sub(/^on/, '')
32
+ @event_name = event_attribute.sub(/^on_/, '').sub(/^on/, '')
33
33
  @dom_element = dom_element
34
34
  @selector = selector
35
- @listener = listener
36
- @js_listener = lambda do |js_event|
37
- event = EventProxy.new(js_event: js_event, listener: self)
38
- result = listener.call(event)
39
- result = true if result.nil?
40
- result
35
+ if !event_attribute.start_with?('on_') # custom event
36
+ @js_listener = lambda do |js_event|
37
+ event = EventProxy.new(js_event: js_event, listener: self)
38
+ result = original_event_listener.call(event)
39
+ result = true if result.nil?
40
+ result
41
+ end
41
42
  end
42
43
  @original_event_listener = original_event_listener
43
44
  end
44
45
 
45
46
  def register
46
- @dom_element.on(@event_name, &@js_listener)
47
+ @dom_element.on(@event_name, &@js_listener) unless @js_listener.nil?
47
48
  end
48
49
  alias observe register
49
50
  alias reregister register
50
51
 
51
52
  def unregister
52
53
  # TODO contribute fix to opal to allow passing observer with & to off with selector not specified as nil
53
- @dom_element.off(@event_name, @js_listener)
54
+ @dom_element.off(@event_name, @js_listener) unless @js_listener.nil?
54
55
  @element.listeners_for(@event_attribute).delete(self)
55
56
  end
56
57
  alias unobserve unregister
@@ -0,0 +1,90 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-web'
23
+
24
+ class NumberHolder
25
+ attr_accessor :number
26
+
27
+ def initialize
28
+ self.number = 50
29
+ end
30
+ end
31
+
32
+ class HelloObserver
33
+ include Glimmer::Web::Component
34
+
35
+ before_render do
36
+ @number_holder = NumberHolder.new
37
+ end
38
+
39
+ after_render do
40
+ # Observe Model attribute @number_holder.number for changes and update View
41
+ # Observer is automatically cleaned up if remove method is called on rendered HelloObserver
42
+ # or its top-level element
43
+ observe(@number_holder, :number) do
44
+ number_string = @number_holder.number.to_s
45
+ @number_input.value = number_string unless @number_input.value == number_string
46
+ @range_input.value = number_string unless @range_input.value == number_string
47
+ end
48
+ # Bidirectional Data-Binding does the same thing automatically
49
+ # Just disable the observe block above as well as the oninput listeners below
50
+ # and enable the `value <=> [@number_holder, :number]` lines to try the data-binding version
51
+ # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
52
+ end
53
+
54
+ markup {
55
+ div {
56
+ div {
57
+ @number_input = input(type: 'number', value: @number_holder.number, min: 0, max: 100) {
58
+ # oninput listener updates Model attribute @number_holder.number
59
+ oninput do
60
+ @number_holder.number = @number_input.value.to_i
61
+ end
62
+
63
+ # Bidirectional Data-Binding simplifies the implementation significantly
64
+ # by enabling the following line and disabling oninput listeners as well
65
+ # as the after_body observe block observer
66
+ # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
67
+ # value <=> [@number_holder, :number]
68
+ }
69
+ }
70
+ div {
71
+ @range_input = input(type: 'range', value: @number_holder.number, min: 0, max: 100) {
72
+ # oninput listener updates Model attribute @number_holder.number
73
+ oninput do
74
+ @number_holder.number = @range_input.value.to_i
75
+ end
76
+
77
+ # Bidirectional Data-Binding simplifies the implementation significantly
78
+ # by enabling the following line and disabling oninput listeners as well
79
+ # as the after_body observe block observer
80
+ # Learn more about Bidirectional and Unidirectional Data-Binding in hello_data_binding.rb
81
+ # value <=> [@number_holder, :number]
82
+ }
83
+ }
84
+ }
85
+ }
86
+ end
87
+
88
+ Document.ready? do
89
+ HelloObserver.render
90
+ end
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2023-2024 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer-dsl-web'
23
+
24
+ class HelloParagraph
25
+ include Glimmer::Web::Component
26
+
27
+ markup {
28
+ div {
29
+ h1(class: 'title') {
30
+ 'Flying Cars Become 100% Safe with AI Powered Balance!'
31
+ }
32
+
33
+ p(class: 'intro') {"
34
+ In the early 2030's, #{em('flying cars')} became affordable after their prices dropped
35
+ below #{small(del('$100,000'))}#{ins('$80,000')} as a result of the innovations of #{strong('Travel-X')}. Still, that did not
36
+ make #{em('flying cars')} any popular due to the extreme difficulty in piloting such flying vehicles for the average
37
+ person, making it very tough to pass the tests for getting a piloting license given the learning curve.
38
+ "}
39
+
40
+ p {"
41
+ That said, #{b('Travel-X')} has recently come up with a new feature for their flagship #{i('flying car')},
42
+ the Ptero#{sub(1)}#{sup('TM')}, which relies on AI#{sub(2)} to automatically balance the flying cars in mid-air,
43
+ thus significantly facilitating their piloting by the average consumer.
44
+ "}
45
+
46
+ p(class: 'conclusion') {"
47
+ That Ptero#{sup('TM')} will be so stable and well balanced while flying that the consumer will be able to drive
48
+ as if it is a plain old car, with the only difference being vertical elevation, the control of which will be handled
49
+ automatically by AI. The Ptero#{sup('TM')} will debut for #{span(style: 'text-decoration: underline dashed;'){'$79,000'}}.
50
+ "}
51
+
52
+ h2(class: 'legend-title') {
53
+ mark('Legend:')
54
+ }
55
+
56
+ p(class: 'legend') {"
57
+ #{strong("1- Ptero:")} Pterosaur is flying dinosaur species#{br}
58
+ #{strong("2- AI:")} Artificial Intelligence#{br}
59
+ "}
60
+
61
+ }
62
+ }
63
+ end
64
+
65
+ Document.ready? do
66
+ HelloParagraph.render
67
+ end
@@ -0,0 +1,36 @@
1
+ require 'glimmer-dsl-web'
2
+
3
+ class Counter
4
+ attr_accessor :count
5
+
6
+ def initialize
7
+ self.count = 0
8
+ end
9
+ end
10
+
11
+ class ButtonCounter
12
+ include Glimmer::Web::Component
13
+
14
+ before_render do
15
+ @counter = Counter.new
16
+ end
17
+
18
+ markup {
19
+ div {
20
+ button {
21
+ # Unidirectional Data-Binding indicating that on every change to @counter.count, the value
22
+ # is read and converted to "Click To Increment: #{value} ", and then automatically
23
+ # copied to button innerText (content) to display to the user
24
+ inner_text <= [@counter, :count,
25
+ on_read: ->(value) { "Click To Increment: #{value} " }
26
+ ]
27
+
28
+ onclick {
29
+ @counter.count += 1
30
+ }
31
+ }
32
+ }
33
+ }
34
+ end
35
+
36
+ ButtonCounter.render