glimmer-dsl-opal 0.0.4 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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