glimmer-dsl-opal 0.0.7 → 0.0.8

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.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.7
1
+ 0.0.8
@@ -1,5 +1,7 @@
1
1
  require 'opal'
2
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__)
@@ -9,7 +9,7 @@ module Glimmer
9
9
  # TODO prefix utility methods with double-underscore
10
10
  module ObservableModel
11
11
  include Observable
12
- include Glimmer
12
+ # include Glimmer
13
13
 
14
14
  def add_property_writer_observers(property_name)
15
15
  property_writer_name = "#{property_name}="
@@ -0,0 +1,67 @@
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
+ parent.remove_all
47
+ model_collection.each do |model|
48
+ table_item = Glimmer::Opal::TableItem.new(parent)
49
+ for index in 0..(column_properties.size-1)
50
+ table_item.set_text(index, model.send(column_properties[index]).to_s)
51
+ end
52
+ table_item.set_data(model)
53
+ end
54
+ selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
55
+ selected_table_items = [parent.items.first] if selected_table_items.empty? && !parent.items.empty?
56
+ parent.selection = selected_table_items unless selected_table_items.empty?
57
+ parent.redraw
58
+ end
59
+
60
+ def sort_table(model_collection, parent, column_properties)
61
+ return if model_collection == @last_model_collection
62
+ parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
63
+ @last_model_collection = model_collection
64
+ end
65
+ end
66
+ end
67
+ 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
@@ -24,6 +24,10 @@ require 'glimmer/dsl/opal/async_exec_expression'
24
24
  require 'glimmer/dsl/opal/observe_expression'
25
25
  require 'glimmer/dsl/opal/layout_data_expression'
26
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'
27
31
 
28
32
  module Glimmer
29
33
  module DSL
@@ -32,6 +36,7 @@ module Glimmer
32
36
  Opal,
33
37
  %w[
34
38
  widget_listener
39
+ table_items_data_binding
35
40
  combo_selection_data_binding
36
41
  list_selection_data_binding
37
42
  data_binding
@@ -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
@@ -10,6 +10,13 @@ module Glimmer
10
10
  @layout = GridLayoutProxy.new(self, [])
11
11
  end
12
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
18
+ end
19
+
13
20
  def dom
14
21
  div_id = id
15
22
  div_style = css
@@ -59,10 +59,6 @@ module Glimmer
59
59
  padding-left: 10px;
60
60
  padding-right: 10px;
61
61
  }
62
- li.selected-list-item {
63
- background: rgb(80, 116, 211);
64
- color: white;
65
- }
66
62
  li.empty-list-item {
67
63
  color: transparent;
68
64
  }
@@ -143,7 +139,6 @@ module Glimmer
143
139
  .close {
144
140
  color: #aaaaaa;
145
141
  float: right;
146
- # font-size: 18px;
147
142
  font-weight: bold;
148
143
  margin: 5px;
149
144
  }
@@ -153,7 +148,21 @@ module Glimmer
153
148
  color: #000;
154
149
  text-decoration: none;
155
150
  cursor: pointer;
156
- }
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
+
157
166
  </style>
158
167
  CSS
159
168
  }
@@ -18,7 +18,7 @@ module Glimmer
18
18
  end
19
19
 
20
20
  def add_child(child)
21
- # return if @children.include?(child) # TODO consider adding an option to enable this if needed to prevent dom repetition
21
+ # return if @children.include?(child) # TODO consider adding an option to enable this if needed to prevent dom repetition
22
22
  @children << child
23
23
  dom << child.dom
24
24
  end
@@ -36,9 +36,8 @@ module Glimmer
36
36
  else
37
37
  dom
38
38
  end
39
- @children.each do |child|
39
+ @children.each do |child|
40
40
  child.redraw
41
- add_child(child)
42
41
  end
43
42
  end
44
43
 
@@ -4,7 +4,13 @@ module Glimmer
4
4
  module Opal
5
5
  class LayoutDataProxy
6
6
  include PropertyOwner
7
- attr_reader :parent, :args, :horizontal_alignment, :grab_excess_horizontal_space
7
+ attr_reader :parent,
8
+ :args,
9
+ :horizontal_alignment,
10
+ :vertical_alignment,
11
+ :grab_excess_horizontal_space,
12
+ :grab_excess_vertical_space,
13
+ :height_hint
8
14
 
9
15
  def initialize(parent, args)
10
16
  @parent = parent
@@ -12,16 +18,31 @@ module Glimmer
12
18
  reapply
13
19
  end
14
20
 
21
+ def height_hint=(height_hint)
22
+ @height_hint = height_hint
23
+ reapply
24
+ end
25
+
15
26
  def horizontal_alignment=(horizontal_alignment)
16
27
  @horizontal_alignment = horizontal_alignment
17
28
  reapply
18
29
  end
19
30
 
31
+ def vertical_alignment=(vertical_alignment)
32
+ @vertical_alignment = vertical_alignment
33
+ reapply
34
+ end
35
+
20
36
  def grab_excess_horizontal_space=(grab_excess_horizontal_space)
21
37
  @grab_excess_horizontal_space = grab_excess_horizontal_space
22
38
  reapply
23
39
  end
24
40
 
41
+ def grab_excess_vertical_space=(grab_excess_vertical_space)
42
+ @grab_excess_vertical_space = grab_excess_vertical_space
43
+ reapply
44
+ end
45
+
25
46
  def reapply
26
47
  # @parent.css = <<~CSS
27
48
  # CSS
@@ -66,7 +66,7 @@ module Glimmer
66
66
  ul(id: list_id, style: list_style) {
67
67
  list_items.to_a.each_with_index do |item, index|
68
68
  li_class = ''
69
- li_class += ' selected-list-item' if list_selection.include?(item)
69
+ li_class += ' selected' if list_selection.include?(item)
70
70
  li_class += ' empty-list-item' if item == ITEM_EMPTY
71
71
  li(class: li_class) {
72
72
  item
@@ -1,4 +1,4 @@
1
- require 'glimmer/opal/div_proxy'
1
+ require 'glimmer/opal/element_proxy'
2
2
 
3
3
  module Glimmer
4
4
  module Opal
@@ -15,7 +15,14 @@ module Glimmer
15
15
  if @children.size == 1
16
16
  child.show
17
17
  end
18
- end
18
+ end
19
+
20
+ def redraw
21
+ super()
22
+ @children.each do |child|
23
+ add_child(child) # TODO think of impact of this on performance
24
+ end
25
+ end
19
26
 
20
27
  def hide_all_tab_content
21
28
  @children.each(&:hide)
@@ -1,4 +1,4 @@
1
- require 'glimmer/opal/input_proxy'
1
+ require 'glimmer/opal/div_proxy'
2
2
 
3
3
  module Glimmer
4
4
  module Opal
@@ -0,0 +1,50 @@
1
+ require 'glimmer/opal/element_proxy'
2
+
3
+ module Glimmer
4
+ module Opal
5
+ class TableColumn < ElementProxy
6
+ include Glimmer
7
+ attr_reader :text, :width
8
+
9
+ def text=(value)
10
+ @text = value
11
+ redraw
12
+ end
13
+
14
+ def width=(value)
15
+ @width = value
16
+ redraw
17
+ end
18
+
19
+ def css
20
+ <<~CSS
21
+ width: #{width};
22
+ CSS
23
+ end
24
+
25
+ def name
26
+ 'th'
27
+ end
28
+
29
+ def observation_request_to_event_mapping
30
+ {
31
+ 'on_widget_selected' => {
32
+ event: 'click'
33
+ },
34
+ }
35
+ end
36
+
37
+ def dom
38
+ table_column_text = text
39
+ table_column_id = id
40
+ table_column_id_style = css
41
+ table_column_css_classes = css_classes
42
+ @dom ||= DOM {
43
+ th(id: table_column_id, style: table_column_id_style, class: table_column_css_classes.to_a.join(' ')) {
44
+ table_column_text
45
+ }
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,136 @@
1
+ require 'glimmer/opal/element_proxy'
2
+
3
+ module Glimmer
4
+ module Opal
5
+ class TableItem < ElementProxy
6
+ attr_reader :data
7
+
8
+ def initialize(parent, args)
9
+ super(parent, args)
10
+ on_widget_selected { |event|
11
+ parent.select(parent.index_of(self), event.meta?)
12
+ }
13
+ end
14
+
15
+ def get_text(index)
16
+ text_array[index]
17
+ end
18
+
19
+ def set_text(index, text_value)
20
+ text_array[index] = text_value
21
+ redraw
22
+ end
23
+
24
+ def text_array
25
+ @text_array ||= []
26
+ end
27
+
28
+ def get_data(key = nil)
29
+ if key.nil?
30
+ @data
31
+ else
32
+ data_hash[key]
33
+ end
34
+ end
35
+
36
+ def set_data(key = nil, data_value)
37
+ if key.nil?
38
+ @data = data_value
39
+ else
40
+ data_hash[key] = data_value
41
+ end
42
+ end
43
+
44
+ def data_hash
45
+ @data_hash ||= {}
46
+ end
47
+
48
+ def name
49
+ 'tr'
50
+ end
51
+
52
+ def edit(column_index)
53
+ return if @edit_column_index == column_index.to_i
54
+ parent.select(parent.index_of(self), false)
55
+ @edit_column_index = column_index.to_i
56
+ redraw
57
+ end
58
+
59
+ def on_widget_selected(&block)
60
+ event = 'click'
61
+ delegate = $document.on(event, selector, &block)
62
+ EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
63
+ end
64
+
65
+ def max_column_width(column_index)
66
+ parent.dom.css("tr td:nth-child(#{column_index + 1})").first.width
67
+ end
68
+
69
+ def dom
70
+ table_item_id = id
71
+ table_item_id_style = css
72
+ table_item_css_classes = css_classes
73
+ table_item_selection = parent.selection.include?(self)
74
+ if table_item_selection
75
+ table_item_css_classes << 'selected'
76
+ else
77
+ table_item_css_classes.delete('selected')
78
+ end
79
+ table_item_text_array = text_array
80
+ table_item_edit_column_index = @edit_column_index
81
+ table_item_max_width = max_column_width(table_item_edit_column_index) if table_item_edit_column_index
82
+ table_item_edit_handler = lambda do |event, cancel = false|
83
+ Async::Task.new do
84
+ text_value = event.target.value
85
+ edit_property = parent.column_properties[table_item_edit_column_index]
86
+ edit_model = get_data
87
+ if !cancel && edit_model.send(edit_property) != text_value
88
+ edit_model.send("#{edit_property}=", text_value)
89
+ set_text(table_item_edit_column_index, text_value)
90
+ end
91
+ @edit_column_index = nil
92
+ redraw
93
+ end
94
+ end
95
+ table_item_edit_cancel_handler = lambda do |event|
96
+ Async::Task.new do
97
+ table_item_edit_handler.call(event, true)
98
+ end
99
+ end
100
+ table_item_edit_key_handler = lambda do |event|
101
+ Async::Task.new do
102
+ if event.code == 13
103
+ table_item_edit_handler.call(event)
104
+ elsif event.code == 27
105
+ table_item_edit_cancel_handler.call(event)
106
+ end
107
+ end
108
+ end
109
+ @dom ||= DOM {
110
+ tr(id: table_item_id, style: table_item_id_style, class: table_item_css_classes.to_a.join(' ')) {
111
+ table_item_text_array.each_with_index do |table_item_text, column_index|
112
+ td('data-column-index' => column_index) {
113
+ if table_item_edit_column_index == column_index
114
+ input(type: 'text', value: table_item_text, style: "max-width: #{table_item_max_width - 11}px;")
115
+ else
116
+ table_item_text
117
+ end
118
+ }
119
+ end
120
+ }
121
+ }.tap do |the_dom|
122
+ if table_item_edit_column_index
123
+ table_item_input = the_dom.css("td:nth-child(#{table_item_edit_column_index + 1}) input").first
124
+ if table_item_input
125
+ Async::Task.new do
126
+ table_item_input.focus
127
+ table_item_input.on('keyup', &table_item_edit_key_handler)
128
+ table_item_input.on('focusout', &table_item_edit_cancel_handler)
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end