glimmer-dsl-opal 0.0.7 → 0.0.8

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