glimmer-dsl-opal 0.0.3 → 0.0.4
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/README.md +133 -8
- data/VERSION +1 -1
- data/lib/glimmer/data_binding/list_selection_binding.rb +51 -0
- data/lib/glimmer/dsl/opal/dsl.rb +3 -0
- data/lib/glimmer/dsl/opal/list_expression.rb +17 -0
- data/lib/glimmer/dsl/opal/list_selection_data_binding_expression.rb +42 -0
- data/lib/glimmer/opal/document_proxy.rb +20 -10
- data/lib/glimmer/opal/element_proxy.rb +15 -5
- data/lib/glimmer/opal/list_proxy.rb +80 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4abedea4353104a18359a2131eee38b7cff23498f246e35ddec92380798c5555
|
4
|
+
data.tar.gz: cbba1e1d0b89ebd01c9ddba94cfbeeee5359f93b985e192d826829369794c85e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8223f782d4e4daeefa03081e95b54fc4ab0e9782825a4fd4c17d041d73284c7bf1c78a3141defa526b648e5d6ab41b63ffafea610584d235f7f6b29adb21d3ca
|
7
|
+
data.tar.gz: d8f62269cc84ae305f3e6cbd74d371272008d7ed1f6d2d8bb44b3fb95cc96247c7628fb95f788a19e2e30d67aedf017dfc19e82b8467af349d000013caa05219
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
# <img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=65 /> Glimmer DSL for Opal 0.0.
|
2
|
+
# <img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=65 /> Glimmer DSL for Opal 0.0.4 (Web GUI for Desktop Apps)
|
3
3
|
[](http://badge.fury.io/rb/glimmer-dsl-opal)
|
4
4
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
5
5
|
|
@@ -7,10 +7,12 @@
|
|
7
7
|
|
8
8
|
It enables running [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps on the web via [Rails](https://rubyonrails.org/) 5 and [Opal](https://opalrb.com/) 1.
|
9
9
|
|
10
|
-
NOTE: Alpha Version 0.0.
|
11
|
-
- Hello, World!
|
12
|
-
- Hello, Combo!
|
13
|
-
- Hello, Computed!
|
10
|
+
NOTE: Alpha Version 0.0.4 only supports capabilities below (detailed under [Examples](#examples)):
|
11
|
+
- [Hello, World!](#hello-world)
|
12
|
+
- [Hello, Combo!](#hello-combo)
|
13
|
+
- [Hello, Computed!](#hello-computed)
|
14
|
+
- [Hello, List Single Selection!](#hello-list-single-selection)
|
15
|
+
- [Hello, List Multi Selection!](#hello-list-multi-selection)
|
14
16
|
|
15
17
|
Other Glimmer DSL gems:
|
16
18
|
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
|
@@ -27,6 +29,7 @@ The following keywords from [glimmer-dsl-swt](https://github.com/AndyObtiva/glim
|
|
27
29
|
- `button`
|
28
30
|
- `text`
|
29
31
|
- `composite`
|
32
|
+
- `list` & `list(:multi)`
|
30
33
|
- `grid_layout`
|
31
34
|
- `layout_data`
|
32
35
|
|
@@ -51,7 +54,7 @@ Add the following to `Gemfile` (NOTE: if you run into issues, they are probably
|
|
51
54
|
```
|
52
55
|
gem 'opal-rails'
|
53
56
|
gem 'opal-browser'
|
54
|
-
gem 'glimmer-dsl-opal', '~> 0.0.
|
57
|
+
gem 'glimmer-dsl-opal', '~> 0.0.4', require: false
|
55
58
|
```
|
56
59
|
|
57
60
|
Edit `config/initializers/assets.rb` and add:
|
@@ -183,8 +186,6 @@ class HelloComputed
|
|
183
186
|
end
|
184
187
|
end
|
185
188
|
|
186
|
-
require_relative "hello_computed/contact"
|
187
|
-
|
188
189
|
class HelloComputed
|
189
190
|
include Glimmer
|
190
191
|
|
@@ -270,6 +271,130 @@ You should see "Hello, Computed!"
|
|
270
271
|
|
271
272
|

|
272
273
|
|
274
|
+
### Hello, List Single Selection!
|
275
|
+
|
276
|
+
Add the following Glimmer code to `app/assets/javascripts/application.js.rb`
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
class Person
|
280
|
+
attr_accessor :country, :country_options
|
281
|
+
|
282
|
+
def initialize
|
283
|
+
self.country_options=["", "Canada", "US", "Mexico"]
|
284
|
+
self.country = "Canada"
|
285
|
+
end
|
286
|
+
|
287
|
+
def reset_country
|
288
|
+
self.country = "Canada"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class HelloListSingleSelection
|
293
|
+
include Glimmer
|
294
|
+
def launch
|
295
|
+
person = Person.new
|
296
|
+
shell {
|
297
|
+
composite {
|
298
|
+
list {
|
299
|
+
selection bind(person, :country)
|
300
|
+
}
|
301
|
+
button {
|
302
|
+
text "Reset"
|
303
|
+
on_widget_selected do
|
304
|
+
person.reset_country
|
305
|
+
end
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}.open
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
HelloListSingleSelection.new.launch
|
313
|
+
```
|
314
|
+
Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
315
|
+
|
316
|
+

|
317
|
+
|
318
|
+
Glimmer app on the web (using `glimmer-dsl-opal` gem):
|
319
|
+
|
320
|
+
Start the Rails server:
|
321
|
+
```
|
322
|
+
rails s
|
323
|
+
```
|
324
|
+
|
325
|
+
Visit `http://localhost:3000`
|
326
|
+
|
327
|
+
You should see "Hello, List Single Selection!"
|
328
|
+
|
329
|
+

|
330
|
+
|
331
|
+
### Hello, List Multi Selection!
|
332
|
+
|
333
|
+
Add the following Glimmer code to `app/assets/javascripts/application.js.rb`
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
class Person
|
337
|
+
attr_accessor :provinces, :provinces_options
|
338
|
+
|
339
|
+
def initialize
|
340
|
+
self.provinces_options=[
|
341
|
+
"",
|
342
|
+
"Quebec",
|
343
|
+
"Ontario",
|
344
|
+
"Manitoba",
|
345
|
+
"Saskatchewan",
|
346
|
+
"Alberta",
|
347
|
+
"British Columbia",
|
348
|
+
"Nova Skotia",
|
349
|
+
"Newfoundland"
|
350
|
+
]
|
351
|
+
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
352
|
+
end
|
353
|
+
|
354
|
+
def reset_provinces
|
355
|
+
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
class HelloListMultiSelection
|
360
|
+
include Glimmer
|
361
|
+
def launch
|
362
|
+
person = Person.new
|
363
|
+
shell {
|
364
|
+
composite {
|
365
|
+
list(:multi) {
|
366
|
+
selection bind(person, :provinces)
|
367
|
+
}
|
368
|
+
button {
|
369
|
+
text "Reset"
|
370
|
+
on_widget_selected do
|
371
|
+
person.reset_provinces
|
372
|
+
end
|
373
|
+
}
|
374
|
+
}
|
375
|
+
}.open
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
HelloListMultiSelection.new.launch
|
380
|
+
```
|
381
|
+
Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
382
|
+
|
383
|
+

|
384
|
+
|
385
|
+
Glimmer app on the web (using `glimmer-dsl-opal` gem):
|
386
|
+
|
387
|
+
Start the Rails server:
|
388
|
+
```
|
389
|
+
rails s
|
390
|
+
```
|
391
|
+
|
392
|
+
Visit `http://localhost:3000`
|
393
|
+
|
394
|
+
You should see "Hello, List Multi Selection!"
|
395
|
+
|
396
|
+

|
397
|
+
|
273
398
|
## Help
|
274
399
|
|
275
400
|
### Issues
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'glimmer/data_binding/observable'
|
2
|
+
require 'glimmer/data_binding/observer'
|
3
|
+
|
4
|
+
module Glimmer
|
5
|
+
module DataBinding
|
6
|
+
# SWT List element selection binding
|
7
|
+
class ListSelectionBinding
|
8
|
+
include Glimmer
|
9
|
+
include Observable
|
10
|
+
include Observer
|
11
|
+
|
12
|
+
attr_reader :element_proxy
|
13
|
+
|
14
|
+
PROPERTY_TYPE_UPDATERS = {
|
15
|
+
:string => lambda { |element_proxy, value| element_proxy.select(element_proxy.index_of(value.to_s)) },
|
16
|
+
:array => lambda { |element_proxy, value| element_proxy.selection=(value || []) }
|
17
|
+
}
|
18
|
+
|
19
|
+
PROPERTY_EVALUATORS = {
|
20
|
+
:string => lambda do |selection_array|
|
21
|
+
return nil if selection_array.empty?
|
22
|
+
selection_array[0]
|
23
|
+
end,
|
24
|
+
:array => lambda do |selection_array|
|
25
|
+
selection_array
|
26
|
+
end
|
27
|
+
}
|
28
|
+
|
29
|
+
# Initialize with list element and property_type
|
30
|
+
# property_type :string represents default list single selection
|
31
|
+
# property_type :array represents list multi selection
|
32
|
+
def initialize(element_proxy, property_type)
|
33
|
+
property_type = :string if property_type.nil? or property_type == :undefined
|
34
|
+
@element_proxy = element_proxy
|
35
|
+
@property_type = property_type
|
36
|
+
@element_proxy.on_widget_disposed do |dispose_event|
|
37
|
+
unregister_all_observables
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(value)
|
42
|
+
PROPERTY_TYPE_UPDATERS[@property_type].call(@element_proxy, value) unless evaluate_property == value
|
43
|
+
end
|
44
|
+
|
45
|
+
def evaluate_property
|
46
|
+
selection_array = @element_proxy.selection.to_a #TODO refactor send('selection') into proper method invocation
|
47
|
+
PROPERTY_EVALUATORS[@property_type].call(selection_array)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/glimmer/dsl/opal/dsl.rb
CHANGED
@@ -15,7 +15,9 @@ require 'glimmer/dsl/opal/combo_selection_data_binding_expression'
|
|
15
15
|
require 'glimmer/dsl/opal/widget_listener_expression'
|
16
16
|
require 'glimmer/dsl/opal/grid_layout_expression'
|
17
17
|
require 'glimmer/dsl/opal/text_expression'
|
18
|
+
require 'glimmer/dsl/opal/list_expression'
|
18
19
|
require 'glimmer/dsl/opal/layout_data_expression'
|
20
|
+
require 'glimmer/dsl/opal/list_selection_data_binding_expression'
|
19
21
|
|
20
22
|
module Glimmer
|
21
23
|
module DSL
|
@@ -25,6 +27,7 @@ module Glimmer
|
|
25
27
|
%w[
|
26
28
|
widget_listener
|
27
29
|
combo_selection_data_binding
|
30
|
+
list_selection_data_binding
|
28
31
|
data_binding
|
29
32
|
text
|
30
33
|
property
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'glimmer/dsl/static_expression'
|
2
|
+
require 'glimmer/dsl/parent_expression'
|
3
|
+
require 'glimmer/opal/list_proxy'
|
4
|
+
|
5
|
+
module Glimmer
|
6
|
+
module DSL
|
7
|
+
module Opal
|
8
|
+
class ListExpression < StaticExpression
|
9
|
+
include ParentExpression
|
10
|
+
|
11
|
+
def interpret(parent, keyword, *args, &block)
|
12
|
+
Glimmer::Opal::ListProxy.new(parent, args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'glimmer/dsl/expression'
|
2
|
+
require 'glimmer/data_binding/model_binding'
|
3
|
+
require 'glimmer/data_binding/element_binding'
|
4
|
+
require 'glimmer/data_binding/list_selection_binding'
|
5
|
+
require 'glimmer/opal/list_proxy'
|
6
|
+
|
7
|
+
module Glimmer
|
8
|
+
module DSL
|
9
|
+
module Opal
|
10
|
+
class ListSelectionDataBindingExpression < Expression
|
11
|
+
def can_interpret?(parent, keyword, *args, &block)
|
12
|
+
keyword == 'selection' and
|
13
|
+
block.nil? and
|
14
|
+
parent.is_a?(Glimmer::Opal::ListProxy) and
|
15
|
+
args.size == 1 and
|
16
|
+
args[0].is_a?(DataBinding::ModelBinding) and
|
17
|
+
args[0].evaluate_options_property.is_a?(Array)
|
18
|
+
end
|
19
|
+
|
20
|
+
def interpret(parent, keyword, *args, &block)
|
21
|
+
model_binding = args[0]
|
22
|
+
element_binding = DataBinding::ElementBinding.new(parent, 'items')
|
23
|
+
element_binding.call(model_binding.evaluate_options_property)
|
24
|
+
model = model_binding.base_model
|
25
|
+
#TODO make this options observer dependent and all similar observers in widget specific data binding interpretrs
|
26
|
+
element_binding.observe(model, model_binding.options_property_name)
|
27
|
+
|
28
|
+
property_type = :string
|
29
|
+
property_type = :array if parent.has_style?(:multi)
|
30
|
+
list_selection_binding = DataBinding::ListSelectionBinding.new(parent, property_type)
|
31
|
+
list_selection_binding.call(model_binding.evaluate_property)
|
32
|
+
#TODO check if nested data binding works for list widget and other widgets that need custom data binding
|
33
|
+
list_selection_binding.observe(model, model_binding.property_name_expression)
|
34
|
+
|
35
|
+
parent.on_widget_selected do
|
36
|
+
model_binding.call(list_selection_binding.evaluate_property)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -28,16 +28,26 @@ module Glimmer
|
|
28
28
|
# TODO make grid-layout support grab excess space false
|
29
29
|
@head_dom ||= DOM {
|
30
30
|
head {
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
<<~CSS
|
32
|
+
<style>
|
33
|
+
ul {
|
34
|
+
list-style: none;
|
35
|
+
padding: 0;
|
36
|
+
}
|
37
|
+
li {
|
38
|
+
cursor: default;
|
39
|
+
padding-left: 10px;
|
40
|
+
padding-right: 10px;
|
41
|
+
}
|
42
|
+
li.selected-list-item {
|
43
|
+
background: rgb(80, 116, 211);
|
44
|
+
color: white;
|
45
|
+
}
|
46
|
+
li.empty-list-item {
|
47
|
+
color: transparent;
|
48
|
+
}
|
49
|
+
</style>
|
50
|
+
CSS
|
41
51
|
}
|
42
52
|
}
|
43
53
|
end
|
@@ -70,18 +70,28 @@ module Glimmer
|
|
70
70
|
redraw
|
71
71
|
end
|
72
72
|
|
73
|
+
# TODO rename to avoid conflict with SWT Style verbiage
|
74
|
+
|
73
75
|
def style=(css)
|
74
76
|
@style = css
|
75
77
|
redraw
|
76
78
|
end
|
77
79
|
|
80
|
+
def has_style?(symbol)
|
81
|
+
@args.include?(symbol) # not a very solid implementation. Bring SWT constants eventually
|
82
|
+
end
|
83
|
+
|
78
84
|
def handle_observation_request(keyword, &event_listener)
|
79
85
|
return unless observation_request_to_event_mapping.keys.include?(keyword)
|
80
|
-
event =
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
86
|
+
event = nil
|
87
|
+
delegate = nil
|
88
|
+
[observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
|
89
|
+
event = mapping[:event]
|
90
|
+
event_handler = mapping[:event_handler]
|
91
|
+
potential_event_listener = event_handler&.call(event_listener)
|
92
|
+
event_listener = event_handler&.call(event_listener) || event_listener
|
93
|
+
delegate = $document.on(event, selector, &event_listener)
|
94
|
+
end
|
85
95
|
EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
|
86
96
|
end
|
87
97
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'glimmer/opal/element_proxy'
|
2
|
+
|
3
|
+
module Glimmer
|
4
|
+
module Opal
|
5
|
+
class ListProxy < ElementProxy
|
6
|
+
ITEM_EMPTY = '_____'
|
7
|
+
attr_reader :items, :selection
|
8
|
+
|
9
|
+
def initialize(parent, args)
|
10
|
+
super(parent, args)
|
11
|
+
@selection = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def items=(items)
|
15
|
+
@items = items.map {|item| item.strip == '' ? ITEM_EMPTY : item}
|
16
|
+
redraw
|
17
|
+
end
|
18
|
+
|
19
|
+
def index_of(item)
|
20
|
+
@items.index(item)
|
21
|
+
end
|
22
|
+
|
23
|
+
# used for multi-selection taking an array
|
24
|
+
def selection=(selection)
|
25
|
+
@selection = selection
|
26
|
+
redraw
|
27
|
+
end
|
28
|
+
|
29
|
+
# used for single selection taking an index
|
30
|
+
def select(index, meta = false)
|
31
|
+
selected_item = @items[index]
|
32
|
+
if @selection.include?(selected_item)
|
33
|
+
@selection.delete(selected_item) if meta
|
34
|
+
else
|
35
|
+
@selection = [] if !meta || (!has_style?(:multi) && @selection.to_a.size >= 1)
|
36
|
+
@selection << selected_item
|
37
|
+
end
|
38
|
+
self.selection = @selection
|
39
|
+
end
|
40
|
+
|
41
|
+
def observation_request_to_event_mapping
|
42
|
+
{
|
43
|
+
'on_widget_selected' => {
|
44
|
+
event: 'click',
|
45
|
+
event_handler: -> (event_listener) {
|
46
|
+
-> (event) {
|
47
|
+
selected_item = event.target.text
|
48
|
+
select(index_of(selected_item), event.meta?)
|
49
|
+
event_listener.call(event)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def name
|
57
|
+
'ul'
|
58
|
+
end
|
59
|
+
|
60
|
+
def dom
|
61
|
+
list_items = @items
|
62
|
+
list_id = id
|
63
|
+
list_style = style
|
64
|
+
list_selection = selection
|
65
|
+
@dom ||= DOM {
|
66
|
+
ul(id: list_id, style: list_style) {
|
67
|
+
list_items.to_a.each_with_index do |item, index|
|
68
|
+
li_class = ''
|
69
|
+
li_class += ' selected-list-item' if list_selection.include?(item)
|
70
|
+
li_class += ' empty-list-item' if item == ITEM_EMPTY
|
71
|
+
li(class: li_class) {
|
72
|
+
item
|
73
|
+
}
|
74
|
+
end
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-opal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glimmer
|
@@ -170,6 +170,7 @@ files:
|
|
170
170
|
- lib/glimmer-dsl-opal.rb
|
171
171
|
- lib/glimmer/config.rb
|
172
172
|
- lib/glimmer/data_binding/element_binding.rb
|
173
|
+
- lib/glimmer/data_binding/list_selection_binding.rb
|
173
174
|
- lib/glimmer/data_binding/observable_element.rb
|
174
175
|
- lib/glimmer/dsl/engine.rb
|
175
176
|
- lib/glimmer/dsl/expression.rb
|
@@ -184,6 +185,8 @@ files:
|
|
184
185
|
- lib/glimmer/dsl/opal/grid_layout_expression.rb
|
185
186
|
- lib/glimmer/dsl/opal/label_expression.rb
|
186
187
|
- lib/glimmer/dsl/opal/layout_data_expression.rb
|
188
|
+
- lib/glimmer/dsl/opal/list_expression.rb
|
189
|
+
- lib/glimmer/dsl/opal/list_selection_data_binding_expression.rb
|
187
190
|
- lib/glimmer/dsl/opal/property_expression.rb
|
188
191
|
- lib/glimmer/dsl/opal/shell_expression.rb
|
189
192
|
- lib/glimmer/dsl/opal/text_expression.rb
|
@@ -201,6 +204,7 @@ files:
|
|
201
204
|
- lib/glimmer/opal/input_proxy.rb
|
202
205
|
- lib/glimmer/opal/label_proxy.rb
|
203
206
|
- lib/glimmer/opal/layout_data_proxy.rb
|
207
|
+
- lib/glimmer/opal/list_proxy.rb
|
204
208
|
- lib/glimmer/opal/property_owner.rb
|
205
209
|
- lib/glimmer/opal/select_proxy.rb
|
206
210
|
- lib/samples/elaborate/contact_manager.rb
|