glimmer-dsl-opal 0.0.4 → 0.0.9

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +983 -34
  4. data/VERSION +1 -1
  5. data/lib/glimmer-dsl-opal.rb +5 -2
  6. data/lib/glimmer/data_binding/ext/observable_model.rb +40 -0
  7. data/lib/glimmer/data_binding/table_items_binding.rb +70 -0
  8. data/lib/glimmer/dsl/opal/async_exec_expression.rb +17 -0
  9. data/lib/glimmer/dsl/opal/browser_expression.rb +17 -0
  10. data/lib/glimmer/dsl/opal/column_properties_expression.rb +22 -0
  11. data/lib/glimmer/dsl/opal/dsl.rb +11 -0
  12. data/lib/glimmer/dsl/opal/message_box_expression.rb +20 -0
  13. data/lib/glimmer/dsl/opal/observe_expression.rb +32 -0
  14. data/lib/glimmer/dsl/opal/tab_folder_expression.rb +17 -0
  15. data/lib/glimmer/dsl/opal/tab_item_expression.rb +17 -0
  16. data/lib/glimmer/dsl/opal/table_column_expression.rb +17 -0
  17. data/lib/glimmer/dsl/opal/table_expression.rb +17 -0
  18. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +29 -0
  19. data/lib/glimmer/opal/display_proxy.rb +23 -0
  20. data/lib/glimmer/opal/div_proxy.rb +11 -2
  21. data/lib/glimmer/opal/document_proxy.rb +124 -4
  22. data/lib/glimmer/opal/element_proxy.rb +45 -14
  23. data/lib/glimmer/opal/grid_layout_proxy.rb +3 -1
  24. data/lib/glimmer/opal/iframe_proxy.rb +23 -0
  25. data/lib/glimmer/opal/input_proxy.rb +8 -4
  26. data/lib/glimmer/opal/label_proxy.rb +1 -1
  27. data/lib/glimmer/opal/layout_data_proxy.rb +23 -2
  28. data/lib/glimmer/opal/list_proxy.rb +2 -2
  29. data/lib/glimmer/opal/modal.rb +94 -0
  30. data/lib/glimmer/opal/point.rb +5 -0
  31. data/lib/glimmer/opal/select_proxy.rb +1 -1
  32. data/lib/glimmer/opal/tab_folder.rb +53 -0
  33. data/lib/glimmer/opal/tab_item.rb +98 -0
  34. data/lib/glimmer/opal/table_column.rb +50 -0
  35. data/lib/glimmer/opal/table_item.rb +136 -0
  36. data/lib/glimmer/opal/table_proxy.rb +149 -0
  37. data/lib/samples/elaborate/contact_manager.rb +1 -2
  38. data/lib/samples/elaborate/login.rb +0 -1
  39. data/lib/samples/elaborate/tic_tac_toe.rb +5 -5
  40. data/lib/samples/hello/hello_tab.rb +2 -2
  41. metadata +28 -16
  42. data/lib/glimmer/config.rb +0 -22
  43. data/lib/glimmer/dsl/engine.rb +0 -193
  44. data/lib/glimmer/dsl/expression.rb +0 -42
  45. data/lib/glimmer/dsl/expression_handler.rb +0 -48
  46. data/lib/glimmer/dsl/parent_expression.rb +0 -12
  47. data/lib/glimmer/dsl/static_expression.rb +0 -36
  48. data/lib/glimmer/dsl/top_level_expression.rb +0 -7
  49. data/lib/glimmer/error.rb +0 -6
  50. data/lib/glimmer/invalid_keyword_error.rb +0 -6
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.9
@@ -1,5 +1,7 @@
1
1
  require 'opal'
2
- require 'opal-parser'
2
+ require 'opal-async'
3
+ require 'browser'
4
+ require 'browser/effects'
3
5
  require 'glimmer'
4
6
 
5
7
  GLIMMER_DSL_OPAL_ROOT = File.expand_path('../..', __FILE__)
@@ -7,4 +9,5 @@ GLIMMER_DSL_OPAL_LIB = File.join(GLIMMER_DSL_OPAL_ROOT, 'lib')
7
9
 
8
10
  $LOAD_PATH.unshift(GLIMMER_DSL_OPAL_LIB)
9
11
 
10
- require 'glimmer/dsl/opal/dsl'
12
+ require 'glimmer/dsl/opal/dsl'
13
+ require 'glimmer/data_binding/ext/observable_model'
@@ -0,0 +1,40 @@
1
+ require 'glimmer/data_binding/observable'
2
+ require 'glimmer/data_binding/observer'
3
+ require 'glimmer/opal/display_proxy'
4
+
5
+ # This ensures all data-binding events happen async and block on modal display
6
+
7
+ module Glimmer
8
+ module DataBinding
9
+ # TODO prefix utility methods with double-underscore
10
+ module ObservableModel
11
+ include Observable
12
+ # include Glimmer
13
+
14
+ def add_property_writer_observers(property_name)
15
+ property_writer_name = "#{property_name}="
16
+ method(property_writer_name)
17
+ ensure_array_object_observer(property_name, send(property_name))
18
+ begin
19
+ method("__original_#{property_writer_name}")
20
+ rescue
21
+ old_method = self.class.instance_method(property_writer_name)
22
+ define_singleton_method("__original_#{property_writer_name}", old_method)
23
+ define_singleton_method(property_writer_name) do |value|
24
+ old_value = self.send(property_name)
25
+ unregister_dependent_observers(property_name, old_value)
26
+ self.send("__original_#{property_writer_name}", value)
27
+ Glimmer::Opal::DisplayProxy.instance.async_exec do
28
+ notify_observers(property_name)
29
+ ensure_array_object_observer(property_name, value, old_value)
30
+ end
31
+ end
32
+ end
33
+ rescue => e
34
+ # ignore writing if no property writer exists
35
+ Glimmer::Config.logger&.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ require 'glimmer/data_binding/observable_array'
2
+ require 'glimmer/data_binding/observable_model'
3
+ require 'glimmer/data_binding/observable'
4
+ require 'glimmer/data_binding/observer'
5
+ require 'glimmer/opal/table_proxy'
6
+ require 'glimmer/opal/table_item'
7
+
8
+ module Glimmer
9
+ module DataBinding
10
+ class TableItemsBinding
11
+ include DataBinding::Observable
12
+ include DataBinding::Observer
13
+
14
+ def initialize(parent, model_binding, column_properties)
15
+ @last_model_collection = nil
16
+ @table = parent
17
+ @model_binding = model_binding
18
+ @column_properties = column_properties
19
+ if @table.respond_to?(:column_properties=)
20
+ @table.column_properties = @column_properties
21
+ ##else # assume custom widget
22
+ ## @table.body_root.column_properties = @column_properties
23
+ end
24
+ call(@model_binding.evaluate_property)
25
+ model = model_binding.base_model
26
+ observe(model, model_binding.property_name_expression)
27
+ ##@table.on_widget_disposed do |dispose_event| # doesn't seem needed within Opal
28
+ ## unregister_all_observables
29
+ ##end
30
+ end
31
+
32
+ def call(new_model_collection=nil)
33
+ if new_model_collection and new_model_collection.is_a?(Array)
34
+ observe(new_model_collection, @column_properties)
35
+ @model_collection = new_model_collection
36
+ end
37
+ populate_table(@model_collection, @table, @column_properties)
38
+ sort_table(@model_collection, @table, @column_properties)
39
+ end
40
+
41
+ def populate_table(model_collection, parent, column_properties)
42
+ return if model_collection&.sort_by(&:hash) == @last_model_collection&.sort_by(&:hash)
43
+ @last_model_collection = model_collection
44
+ # TODO improve performance
45
+ selected_table_item_models = parent.selection.map(&:get_data)
46
+ old_items = parent.items
47
+ old_item_ids_per_model = old_items.reduce({}) {|hash, item| hash.merge(item.get_data.hash => item.id) }
48
+ parent.remove_all
49
+ model_collection.each do |model|
50
+ table_item = Glimmer::Opal::TableItem.new(parent)
51
+ for index in 0..(column_properties.size-1)
52
+ table_item.set_text(index, model.send(column_properties[index]).to_s)
53
+ end
54
+ table_item.set_data(model)
55
+ table_item.id = old_item_ids_per_model[model.hash] if old_item_ids_per_model[model.hash]
56
+ end
57
+ selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
58
+ selected_table_items = [parent.items.first] if selected_table_items.empty? && !parent.items.empty?
59
+ parent.selection = selected_table_items unless selected_table_items.empty?
60
+ parent.redraw
61
+ end
62
+
63
+ def sort_table(model_collection, parent, column_properties)
64
+ return if model_collection == @last_model_collection
65
+ parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
66
+ @last_model_collection = model_collection
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/top_level_expression'
3
+ require 'glimmer/opal/display_proxy'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class AsyncExecExpression < StaticExpression
9
+ include TopLevelExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::DisplayProxy.instance.async_exec(&block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/opal/iframe_proxy'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class BrowserExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::IframeProxy.new(parent, args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/opal/table_proxy'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module Opal
7
+ # Responsible for providing a readable keyword (command symbol) to capture
8
+ # and return column properties for use in TreeItemsDataBindingCommandHandler
9
+ class ColumnPropertiesExpression < StaticExpression
10
+ def can_interpret?(parent, keyword, *args, &block)
11
+ keyword == 'column_properties' and
12
+ block.nil? and
13
+ parent.is_a?(Glimmer::Opal::TableProxy)
14
+ end
15
+
16
+ def interpret(parent, keyword, *args, &block)
17
+ args
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -16,8 +16,18 @@ 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
18
  require 'glimmer/dsl/opal/list_expression'
19
+ require 'glimmer/dsl/opal/browser_expression'
20
+ require 'glimmer/dsl/opal/tab_folder_expression'
21
+ require 'glimmer/dsl/opal/tab_item_expression'
22
+ require 'glimmer/dsl/opal/message_box_expression'
23
+ require 'glimmer/dsl/opal/async_exec_expression'
24
+ require 'glimmer/dsl/opal/observe_expression'
19
25
  require 'glimmer/dsl/opal/layout_data_expression'
20
26
  require 'glimmer/dsl/opal/list_selection_data_binding_expression'
27
+ require 'glimmer/dsl/opal/table_expression'
28
+ require 'glimmer/dsl/opal/table_column_expression'
29
+ require 'glimmer/dsl/opal/table_items_data_binding_expression'
30
+ require 'glimmer/dsl/opal/column_properties_expression'
21
31
 
22
32
  module Glimmer
23
33
  module DSL
@@ -26,6 +36,7 @@ module Glimmer
26
36
  Opal,
27
37
  %w[
28
38
  widget_listener
39
+ table_items_data_binding
29
40
  combo_selection_data_binding
30
41
  list_selection_data_binding
31
42
  data_binding
@@ -0,0 +1,20 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/top_level_expression'
3
+ require 'glimmer/dsl/parent_expression'
4
+ require 'glimmer/opal/modal'
5
+
6
+ module Glimmer
7
+ module DSL
8
+ module Opal
9
+ class MessageBoxExpression < StaticExpression
10
+ include TopLevelExpression
11
+ include ParentExpression
12
+
13
+ def interpret(parent, keyword, *args, &block)
14
+ parent = args.delete_at(0)
15
+ Glimmer::Opal::Modal.new(parent, args)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/top_level_expression'
3
+ require 'glimmer/data_binding/observer'
4
+ require 'glimmer/data_binding/model_binding'
5
+
6
+ module Glimmer
7
+ module DSL
8
+ module Opal
9
+ class ObserveExpression < StaticExpression
10
+ include TopLevelExpression
11
+
12
+ REGEX_NESTED_OR_INDEXED_PROPERTY = /([^\[]+)(\[[^\]]+\])?/
13
+
14
+ def can_interpret?(parent, keyword, *args, &block)
15
+ keyword == 'observe' and
16
+ block_given? and
17
+ (args.size == 2) and
18
+ textual?(args[1])
19
+ end
20
+
21
+ def interpret(parent, keyword, *args, &block)
22
+ observer = DataBinding::Observer.proc(&block)
23
+ if args[1].to_s.match(REGEX_NESTED_OR_INDEXED_PROPERTY)
24
+ observer.observe(DataBinding::ModelBinding.new(args[0], args[1]))
25
+ else
26
+ observer.observe(args[0], args[1])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/opal/tab_folder'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class TabFolderExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::TabFolder.new(parent, args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/opal/tab_item'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class TabItemExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::TabItem.new(parent, args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/opal/table_column'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class TableColumnExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::TableColumn.new(parent, args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/opal/table_proxy'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Opal
8
+ class TableExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ Glimmer::Opal::TableProxy.new(parent, args)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/data_binding/model_binding'
3
+ require 'glimmer/data_binding/table_items_binding'
4
+ require 'glimmer/opal/table_proxy'
5
+
6
+ module Glimmer
7
+ module DSL
8
+ module Opal
9
+ #Depends on BindCommandHandler and TableColumnPropertiesDataBindingCommandHandler
10
+ class TableItemsDataBindingExpression < Expression
11
+ def can_interpret?(parent, keyword, *args, &block)
12
+ keyword == "items" and
13
+ block.nil? and
14
+ parent.is_a?(Glimmer::Opal::TableProxy) and
15
+ args.size == 2 and
16
+ args[0].is_a?(DataBinding::ModelBinding) and
17
+ args[0].evaluate_property.is_a?(Array) and
18
+ args[1].is_a?(Array)
19
+ end
20
+
21
+ def interpret(parent, keyword, *args, &block)
22
+ model_binding = args[0]
23
+ column_properties = args[1]
24
+ DataBinding::TableItemsBinding.new(parent, model_binding, column_properties)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module Glimmer
2
+ module Opal
3
+ class DisplayProxy
4
+ class << self
5
+ def instance
6
+ @instance ||= new
7
+ end
8
+ end
9
+
10
+ def async_exec(&block)
11
+ executer = lambda do
12
+ if $document.at_css('.modal')
13
+ sleep(0.05)
14
+ Async::Task.new(&executer)
15
+ else
16
+ block.call
17
+ end
18
+ end
19
+ Async::Task.new(&executer)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -3,14 +3,23 @@ require 'glimmer/opal/element_proxy'
3
3
  module Glimmer
4
4
  module Opal
5
5
  class DivProxy < ElementProxy
6
+ attr_reader :layout
7
+
6
8
  def initialize(parent, args)
7
9
  super(parent, args)
8
- GridLayoutProxy.new(self, [])
10
+ @layout = GridLayoutProxy.new(self, [])
11
+ end
12
+
13
+ def redraw
14
+ super()
15
+ @children.each do |child|
16
+ add_child(child) # TODO think of impact of this on performance, and of other alternatives
17
+ end
9
18
  end
10
19
 
11
20
  def dom
12
21
  div_id = id
13
- div_style = style
22
+ div_style = css
14
23
  @dom ||= DOM {
15
24
  div(id: div_id, class: 'grid-layout', style: div_style)
16
25
  }
@@ -1,9 +1,11 @@
1
1
  require 'glimmer/opal/element_proxy'
2
+ require 'glimmer/opal/point'
2
3
 
3
4
  module Glimmer
4
5
  module Opal
5
6
  class DocumentProxy < ElementProxy
6
7
  # TODO consider renaming to ShellProxy to match SWT API
8
+ attr_reader :minimum_size
7
9
 
8
10
  def initialize(args)
9
11
  @args = args
@@ -23,6 +25,11 @@ module Glimmer
23
25
  $document.title = value
24
26
  end
25
27
  end
28
+
29
+ def minimum_size=(width_or_minimum_size, height = nil)
30
+ @minimum_size = height.nil? ? width_or_minimum_size : Point.new(width_or_minimum_size, height)
31
+ redraw
32
+ end
26
33
 
27
34
  def head_dom
28
35
  # TODO make grid-layout support grab excess space false
@@ -30,6 +37,19 @@ module Glimmer
30
37
  head {
31
38
  <<~CSS
32
39
  <style>
40
+ html {
41
+ width: 100%;
42
+ height: 100%;
43
+ }
44
+ body {
45
+ width: 100%;
46
+ height: 100%;
47
+ margin: 0;
48
+ }
49
+ body > iframe {
50
+ width: 100%;
51
+ height: 100%;
52
+ }
33
53
  ul {
34
54
  list-style: none;
35
55
  padding: 0;
@@ -39,13 +59,110 @@ module Glimmer
39
59
  padding-left: 10px;
40
60
  padding-right: 10px;
41
61
  }
42
- li.selected-list-item {
62
+ li.empty-list-item {
63
+ color: transparent;
64
+ }
65
+ .tabs {
66
+ overflow: hidden;
67
+ border: 1px solid #ccc;
68
+ background-color: #f1f1f1;
69
+ }
70
+
71
+ /* Style the buttons inside the tab */
72
+ .tabs .tab {
73
+ background-color: inherit;
74
+ float: left;
75
+ border: none;
76
+ outline: none;
77
+ cursor: pointer;
78
+ padding: 14px 16px;
79
+ transition: 0.3s;
80
+ font-size: 17px;
81
+ }
82
+
83
+ /* Change background color of buttons on hover */
84
+ .tabs .tab:hover {
85
+ background-color: #ddd;
86
+ }
87
+
88
+ /* Create an active/current tablink class */
89
+ .tabs .tab.active {
90
+ background-color: #ccc;
91
+ }
92
+
93
+ /* Style the tab content */
94
+ .tab-item {
95
+ padding: 6px 12px;
96
+ border: 1px solid #ccc;
97
+ border-top: none;
98
+ }
99
+
100
+ .hide {
101
+ display: none !important;
102
+ }
103
+
104
+ /* The Modal (background) */
105
+ .modal {
106
+ position: fixed; /* Stay in place */
107
+ z-index: 1; /* Sit on top */
108
+ padding-top: 100px; /* Location of the box */
109
+ left: 0;
110
+ top: 0;
111
+ width: 100%; /* Full width */
112
+ height: 100%; /* Full height */
113
+ overflow: auto; /* Enable scroll if needed */
114
+ background-color: rgb(0,0,0); /* Fallback color */
115
+ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
116
+ text-align: center;
117
+ }
118
+
119
+ /* Modal Content */
120
+ .modal-content {
121
+ background-color: #fefefe;
122
+ margin: auto;
123
+ border: 1px solid #888;
124
+ display: inline-block;
125
+ min-width: 200px;
126
+ }
127
+
128
+ .modal-content .text {
43
129
  background: rgb(80, 116, 211);
44
130
  color: white;
131
+ padding: 5px;
45
132
  }
46
- li.empty-list-item {
47
- color: transparent;
133
+
134
+ .modal-content .message {
135
+ padding: 20px;
48
136
  }
137
+
138
+ /* The Close Button */
139
+ .close {
140
+ color: #aaaaaa;
141
+ float: right;
142
+ font-weight: bold;
143
+ margin: 5px;
144
+ }
145
+
146
+ .close:hover,
147
+ .close:focus {
148
+ color: #000;
149
+ text-decoration: none;
150
+ cursor: pointer;
151
+ }
152
+
153
+ .selected {
154
+ background: rgb(80, 116, 211);
155
+ color: white;
156
+ }
157
+
158
+ table {
159
+ border-spacing: 0;
160
+ }
161
+
162
+ table tr th,td {
163
+ cursor: default;
164
+ }
165
+
49
166
  </style>
50
167
  CSS
51
168
  }
@@ -53,8 +170,11 @@ module Glimmer
53
170
  end
54
171
 
55
172
  def dom
173
+ i = 0
174
+ body_style = ''
175
+ body_style += "min-width: #{@minimum_size.x}px; min-height: #{@minimum_size.y}px;" if @minimum_size
56
176
  @dom ||= DOM {
57
- body {
177
+ body(style: body_style) {
58
178
  }
59
179
  }
60
180
  end