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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +865 -45
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +11 -5
- data/lib/glimmer/dsl/web/component_expression.rb +2 -1
- data/lib/glimmer/dsl/web/dsl.rb +24 -0
- data/lib/glimmer/dsl/web/formatting_element_expression.rb +19 -0
- data/lib/glimmer/dsl/web/observe_expression.rb +42 -0
- data/lib/glimmer/helpers/glimmer_helper.rb +5 -7
- data/lib/glimmer/web/component.rb +22 -0
- data/lib/glimmer/web/element_proxy.rb +10 -14
- data/lib/glimmer/web/event_proxy.rb +7 -4
- data/lib/glimmer/web/formatting_element_proxy.rb +56 -0
- data/lib/glimmer/web/listener_proxy.rb +12 -11
- data/lib/glimmer-dsl-web/samples/hello/hello_observer.rb +90 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_paragraph.rb +67 -0
- data/lib/glimmer-dsl-web/samples/regular/button_counter.rb +36 -0
- metadata +21 -8
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.12
|
data/glimmer-dsl-web.gemspec
CHANGED
@@ -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.
|
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.
|
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-
|
15
|
-
s.description = "Glimmer DSL for Web (Ruby in the Browser Web GUI Frontend Library)
|
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.
|
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
|
data/lib/glimmer/dsl/web/dsl.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
28
|
-
|
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, :
|
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:,
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|