glimmer-dsl-opal 0.7.5 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/README.md +756 -139
  4. data/VERSION +1 -1
  5. data/lib/glimmer-dsl-opal.rb +16 -4
  6. data/lib/glimmer-dsl-opal/ext/class.rb +10 -0
  7. data/lib/{file.rb → glimmer-dsl-opal/ext/file.rb} +0 -0
  8. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +46 -0
  9. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +1 -1
  10. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +23 -0
  11. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +27 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_computed/contact.rb +21 -0
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +62 -32
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +47 -22
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_menu_bar.rb +241 -0
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_message_box.rb +37 -0
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu.rb +84 -0
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_world.rb +3 -3
  19. data/lib/glimmer/config/opal_logger.rb +16 -0
  20. data/lib/glimmer/data_binding/observable_element.rb +1 -1
  21. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +1 -1
  22. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  23. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +54 -0
  24. data/lib/glimmer/dsl/opal/menu_expression.rb +61 -0
  25. data/lib/glimmer/dsl/opal/shell_expression.rb +0 -4
  26. data/lib/glimmer/dsl/opal/widget_expression.rb +3 -2
  27. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +2 -2
  28. data/lib/glimmer/swt/custom/checkbox_group.rb +2 -2
  29. data/lib/glimmer/swt/custom/radio_group.rb +2 -2
  30. data/lib/glimmer/swt/display_proxy.rb +4 -4
  31. data/lib/glimmer/swt/event_listener_proxy.rb +14 -4
  32. data/lib/glimmer/swt/grid_layout_proxy.rb +1 -0
  33. data/lib/glimmer/swt/label_proxy.rb +27 -3
  34. data/lib/{glimmer-dsl-opal/ext/struct.rb → glimmer/swt/latest_message_box_proxy.rb} +16 -11
  35. data/lib/glimmer/swt/latest_shell_proxy.rb +55 -0
  36. data/lib/glimmer/swt/list_proxy.rb +15 -0
  37. data/lib/glimmer/swt/menu_item_proxy.rb +174 -0
  38. data/lib/glimmer/swt/menu_proxy.rb +273 -0
  39. data/lib/glimmer/swt/message_box_proxy.rb +79 -63
  40. data/lib/glimmer/swt/property_owner.rb +2 -0
  41. data/lib/glimmer/swt/radio_proxy.rb +1 -1
  42. data/lib/glimmer/swt/shell_proxy.rb +34 -189
  43. data/lib/glimmer/swt/tab_folder_proxy.rb +43 -0
  44. data/lib/glimmer/swt/table_column_proxy.rb +3 -2
  45. data/lib/glimmer/swt/table_editor.rb +1 -1
  46. data/lib/glimmer/swt/table_item_proxy.rb +7 -5
  47. data/lib/glimmer/swt/table_proxy.rb +10 -0
  48. data/lib/glimmer/swt/widget_proxy.rb +325 -31
  49. data/lib/glimmer/ui/custom_shell.rb +2 -2
  50. data/lib/glimmer/ui/custom_widget.rb +3 -3
  51. data/lib/net/http.rb +30 -2
  52. metadata +45 -8
@@ -1,3 +1,24 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'glimmer/swt/widget_proxy'
2
23
  # require 'glimmer/swt/image_proxy'
3
24
 
@@ -12,7 +33,11 @@ module Glimmer
12
33
 
13
34
  def text=(value)
14
35
  @text = value
15
- dom_element.html(value)
36
+ dom_element.html(html_text)
37
+ end
38
+
39
+ def html_text
40
+ text&.gsub("\n", '<br />')
16
41
  end
17
42
 
18
43
  def background_image=(*image_options)
@@ -48,12 +73,11 @@ module Glimmer
48
73
  end
49
74
 
50
75
  def dom
51
- label_text = @text
52
76
  label_id = id
53
77
  label_class = name
54
78
  @dom ||= html {
55
79
  label(id: label_id, class: label_class, style: "text-align: #{alignment};") {
56
- label_text
80
+ html_text
57
81
  }
58
82
  }.to_s
59
83
  end
@@ -19,19 +19,24 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- class Struct
23
- class << self
24
- alias new_original new
25
- def new(*args, &block)
26
- new_original(*args, &block).tap do |struct_class|
27
- if args.size >= 2 && args.last.is_a?(Hash) && args.last[:keyword_init]
28
- struct_class.define_method(:initialize) do |struct_class_keyword_args|
29
- struct_class_keyword_args.each do |attribute, value|
30
- send("#{attribute}=", value)
31
- end
32
- end
22
+ module Glimmer
23
+ module SWT
24
+ class LatestMessageBoxProxy #< MessageBoxProxy
25
+ # TODO consider overriding all methods from MessageBoxProxy and proxying to them
26
+ # TODO consider the idea of promoting this object into the real message_box once Document is ready
27
+
28
+ def initialize(parent, args, block)
29
+ # No Op
30
+ end
31
+
32
+ def open
33
+ Document.ready? do
34
+ DisplayProxy.instance.message_boxes.last&.open
33
35
  end
34
36
  end
37
+
35
38
  end
39
+
36
40
  end
41
+
37
42
  end
@@ -0,0 +1,55 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ module Glimmer
23
+ module SWT
24
+ class LatestShellProxy #< ShellProxy
25
+ def initialize(parent, args, block)
26
+ # No Op
27
+ end
28
+
29
+ def method_missing(method, *args, &block)
30
+ if latest_shell.nil?
31
+ super(method, *args, &block)
32
+ else
33
+ latest_shell.send(method, *args, &block)
34
+ end
35
+ end
36
+
37
+ def respond_to?(method, *args, &block)
38
+ super || latest_shell&.respond_to?(method, *args, &block)
39
+ end
40
+
41
+ def open
42
+ Document.ready? do
43
+ latest_shell&.open
44
+ end
45
+ end
46
+
47
+ def latest_shell
48
+ @latest_shell ||= DisplayProxy.instance.shells.last
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -3,6 +3,21 @@ require 'glimmer/swt/widget_proxy'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class ListProxy < WidgetProxy
6
+ STYLE = <<~CSS
7
+ ul {
8
+ list-style: none;
9
+ padding: 0;
10
+ }
11
+ li {
12
+ cursor: default;
13
+ padding-left: 10px;
14
+ padding-right: 10px;
15
+ }
16
+ li.empty-list-item {
17
+ color: transparent;
18
+ }
19
+ CSS
20
+
6
21
  ITEM_EMPTY = '_____'
7
22
  attr_reader :items, :selection
8
23
 
@@ -0,0 +1,174 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ # TODO implement set_menu or self.menu=
23
+
24
+ require 'glimmer/swt/widget_proxy'
25
+
26
+ module Glimmer
27
+ module SWT
28
+ # Proxy for org.eclipse.swt.widgets.MenuItem
29
+ #
30
+ # Follows the Proxy Design Pattern since it's a proxy for an HTML based menu
31
+ # Follows the Adapter Design Pattern since it's adapting a Glimmer DSL for SWT widget
32
+ class MenuItemProxy < WidgetProxy
33
+ STYLE = <<~CSS
34
+ .menu-item.disabled {
35
+ background-color: lightgrey;
36
+ color: grey;
37
+ }
38
+ .menu-item.disabled .menu, .menu-item.disabled .menu * {
39
+ display: none;
40
+ opacity: 0;
41
+ }
42
+ CSS
43
+
44
+ attr_accessor :accelerator # TODO consider doing something with it
45
+
46
+ def initialize(parent, args)
47
+ args.push(:push) if args.empty?
48
+ super(parent, args)
49
+ # TODO do not add the following event till post_add_content to avoid adding if already one on_widget_selected event existed
50
+ on_widget_selected {
51
+ # No Op, just trigger selection
52
+ }
53
+ end
54
+
55
+ def post_initialize_child(child)
56
+ @children << child
57
+ end
58
+
59
+ def cascade?
60
+ args.include?(:cascade)
61
+ end
62
+
63
+ def push?
64
+ args.include?(:push)
65
+ end
66
+
67
+ def radio?
68
+ args.include?(:radio)
69
+ end
70
+
71
+ def check?
72
+ args.include?(:check)
73
+ end
74
+
75
+ def separator?
76
+ args.include?(:separator)
77
+ end
78
+
79
+ def text
80
+ @text
81
+ end
82
+
83
+ def text=(value)
84
+ @text = value
85
+ dom_element.find('.menu-item-text').html(@text)
86
+ @text
87
+ end
88
+
89
+ def selection
90
+ @selection
91
+ end
92
+
93
+ def selection=(value)
94
+ @selection = value
95
+ icon_suffix = check? ? 'check' : 'bullet'
96
+ dom_element.find('.menu-item-selection').toggle_class("ui-icon ui-icon-#{icon_suffix}", @selection)
97
+ @selection
98
+ end
99
+
100
+ def toggle_selection!
101
+ self.selection = !selection
102
+ end
103
+
104
+ def enabled=(value)
105
+ @enabled = value
106
+ dom_element.toggle_class('disabled', !@enabled)
107
+ @enabled
108
+ end
109
+
110
+ def div_content
111
+ div_attributes = {}
112
+ icon_suffix = check? ? 'check' : 'bullet'
113
+ div(div_attributes) {
114
+ unless separator? # empty content automatically gets a separator style in jQuery-UI
115
+ span(class: "menu-item-selection #{"ui-icon ui-icon-#{icon_suffix}" if selection}") {}
116
+ span(class: 'ui-menu-icon ui-icon ui-icon-caret-1-e') {} if cascade? && !parent.bar?
117
+ span(class: 'menu-item-text') {
118
+ @text
119
+ }
120
+ ''
121
+ end
122
+ }
123
+ end
124
+
125
+ def root_menu
126
+ the_menu = parent
127
+ the_menu = the_menu.parent_menu until the_menu.root_menu?
128
+ the_menu
129
+ end
130
+
131
+ def skip_content_on_render_blocks?
132
+ true
133
+ end
134
+
135
+ def observation_request_to_event_mapping
136
+ {
137
+ 'on_widget_selected' => {
138
+ event: 'mouseup',
139
+ event_handler: -> (event_listener) {
140
+ -> (event) {
141
+ if enabled && (push? || radio? || check?)
142
+ if check?
143
+ self.toggle_selection!
144
+ elsif radio? && !selection
145
+ parent.children.detect(&:selection)&.selection = false
146
+ self.selection = true
147
+ end
148
+ if !root_menu.bar?
149
+ remove_event_listener_proxies
150
+ root_menu.close
151
+ end
152
+ event_listener.call(event)
153
+ end
154
+ }
155
+ },
156
+ },
157
+ }
158
+ end
159
+
160
+ def element
161
+ 'li'
162
+ end
163
+
164
+ def dom
165
+ # TODO support rendering image
166
+ @dom ||= html {
167
+ li(id: id, class: "#{name} #{'disabled' unless enabled}") {
168
+ div_content
169
+ }
170
+ }.to_s
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,273 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/swt/widget_proxy'
23
+ require 'glimmer/swt/menu_item_proxy'
24
+
25
+ module Glimmer
26
+ module SWT
27
+ # Proxy for org.eclipse.swt.widgets.Menu
28
+ #
29
+ # Functions differently from other widget proxies.
30
+ #
31
+ # Glimmer automatically detects if this is a drop down menu
32
+ # or pop up menu from its parent if no SWT style is passed in.
33
+ #
34
+ # There are 3 possibilities:
35
+ # - SWT :bar style is passed in: Menu Bar
36
+ # - Parent is ShellProxy: Pop Up Menu (having style :pop_up)
37
+ # - Parent is another Menu: Drop Down Menu (having style :drop_down)
38
+ #
39
+ # In order to get the SWT Menu object, one must call `#swt_widget`.
40
+ #
41
+ # In the case of a Drop Down menu, this automatically creates an
42
+ # SWT MenuItem object with style :cascade
43
+ #
44
+ # In order to retrieve the menu item widget proxy, one must call `#menu_item_proxy`
45
+ #
46
+ # Follows the Proxy Design Pattern
47
+ class MenuProxy < WidgetProxy
48
+ STYLE = <<~CSS
49
+ .menu.menu-bar {
50
+ position: absolute;
51
+ top: -30px;
52
+ border-radius: 0;
53
+ width: 100%;
54
+ }
55
+ .menu.menu-bar .menu {
56
+ border-radius: 0;
57
+ }
58
+ .menu-bar .menu-item {
59
+ width: 180px;
60
+ }
61
+ .menu-bar > .menu-item {
62
+ display: inline-block;
63
+ width: 150px;
64
+ }
65
+ li.menu-item {
66
+ padding-left: initial;
67
+ padding-right: initial;
68
+ }
69
+ .menu {
70
+ /* TODO consider auto-sizing in the future */
71
+ font-size: initial;
72
+ border-radius: 5px;
73
+ }
74
+ .menu:not(.menu-bar) {
75
+ width: 150px;
76
+ }
77
+ .menu-bar .ui-menu:not(.menu-bar) {
78
+ width: 180px;
79
+ }
80
+ .ui-menu-item:first-child > .ui-menu-item-wrapper {
81
+ border-top-left-radius: 5px;
82
+ border-top-right-radius: 5px;
83
+ }
84
+ .ui-menu-item:last-child > .ui-menu-item-wrapper {
85
+ border-bottom-left-radius: 5px;
86
+ border-bottom-right-radius: 5px;
87
+ }
88
+ .menu-bar .ui-menu-item:first-child > .ui-menu-item-wrapper {
89
+ border-top-left-radius: 0;
90
+ border-top-right-radius: 0;
91
+ }
92
+ .menu-bar .ui-menu-item:last-child > .ui-menu-item-wrapper {
93
+ border-bottom-left-radius: 0;
94
+ border-bottom-right-radius: 0;
95
+ }
96
+
97
+ CSS
98
+
99
+ attr_reader :menu_item_proxy, :menu_parent
100
+
101
+ def initialize(parent, args)
102
+ # TODO refactor/simplify code below
103
+ @children = []
104
+ index = args.delete(args.last) if args.last.is_a?(Numeric)
105
+ args = args.map {|arg| arg.is_a?(String) ? arg.to_sym : arg}
106
+ if parent.is_a?(ShellProxy)
107
+ args = args.unshift(:bar)
108
+ elsif parent.is_a?(MenuProxy)
109
+ args = args.unshift(:drop_down)
110
+ else
111
+ args = args.unshift(:pop_up)
112
+ end
113
+ if parent.is_a?(MenuProxy)
114
+ @menu_item_proxy = SWT::WidgetProxy.for('menu_item', parent, [:cascade] + [index].compact)
115
+ super(@menu_item_proxy, args)
116
+ @menu_item_proxy.menu = self
117
+ elsif parent.is_a?(ShellProxy)
118
+ super(parent, args)
119
+ else # widget pop up
120
+ super(parent, args)
121
+ end
122
+
123
+ if bar?
124
+ # Assumes a parent shell
125
+ parent.menu_bar = self
126
+ elsif pop_up?
127
+ parent.menu = self
128
+ end
129
+ # TODO IMPLEMENT PROPERLY
130
+ # on_focus_lost {
131
+ # dispose
132
+ # }
133
+ end
134
+
135
+ def bar?
136
+ args.include?(:bar)
137
+ end
138
+
139
+ def pop_up?
140
+ args.include?(:pop_up)
141
+ end
142
+
143
+ def drop_down?
144
+ args.include?(:drop_down)
145
+ end
146
+
147
+ def text
148
+ @menu_item_proxy&.text
149
+ end
150
+
151
+ def text=(text_value)
152
+ @menu_item_proxy&.text = text_value
153
+ end
154
+
155
+ def enabled
156
+ if drop_down?
157
+ menu_item_proxy.enabled
158
+ else
159
+ true
160
+ end
161
+ end
162
+
163
+ def enabled=(value)
164
+ if drop_down?
165
+ menu_item_proxy.enabled = value
166
+ end
167
+ end
168
+
169
+ def can_handle_observation_request?(observation_request, super_only: false)
170
+ super_result = super(observation_request)
171
+ if observation_request.start_with?('on_') && !super_result && !super_only
172
+ return menu_item_proxy.can_handle_observation_request?(observation_request)
173
+ else
174
+ super_result
175
+ end
176
+ end
177
+
178
+ def handle_observation_request(observation_request, block)
179
+ if can_handle_observation_request?(observation_request, super_only: true)
180
+ super
181
+ else
182
+ menu_item_proxy.handle_observation_request(observation_request, block)
183
+ end
184
+ end
185
+
186
+ def post_initialize_child(child)
187
+ if child && !@children.include?(child)
188
+ if child.is_a?(MenuItemProxy)
189
+ @children << child
190
+ else
191
+ @children << child.menu_item_proxy
192
+ end
193
+ end
194
+ end
195
+
196
+ def post_add_content
197
+ if bar?
198
+ # delay this till all children rendered (perhaps post_add_content block)
199
+ parent_dom_element.css('position', 'relative')
200
+ parent_dom_element.css('margin-top', '30px')
201
+ redraw
202
+ `$(#{path}).menu({
203
+ position: { my: "top", at: "bottom" },
204
+ icons: { submenu: "ui-icon-blank" }
205
+ });`
206
+ the_element = dom_element
207
+ the_element.on('mouseover') { |event|
208
+ if event.page_x.between?(the_element.offset.left, the_element.offset.left + the_element.width) and
209
+ event.page_y.between?(the_element.offset.top, the_element.offset.top + the_element.height)
210
+ `$(#{path}).menu('option', 'position', { my: 'left top', at: 'left bottom' })`
211
+ end
212
+ }
213
+ the_element.on('menublur') {
214
+ `$(#{path}).menu('option', 'position', { my: 'left top', at: 'right top' })`
215
+ }
216
+ minimum_width = children.to_a.map(&:dom_element).map(&:width).reduce(:+)
217
+ the_element.css('min-width', minimum_width)
218
+ end
219
+ end
220
+
221
+ def render(custom_parent_dom_element: nil, brand_new: false)
222
+ # TODO attach to top nav bar if parent is shell
223
+ # TODO attach listener to parent to display on right click
224
+ if parent.is_a?(MenuProxy) || parent.is_a?(MenuItemProxy) || parent.menu_requested? || parent.is_a?(ShellProxy)
225
+ super(custom_parent_dom_element: custom_parent_dom_element, brand_new: brand_new)
226
+ if root_menu? && !bar?
227
+ `$(#{path}).menu();`
228
+ @close_event_handler = lambda do |event|
229
+ close if event.target.parents('.ui-menu').empty?
230
+ end
231
+ Element['body'].on('click', &@close_event_handler)
232
+ end
233
+ end
234
+ end
235
+
236
+ def close
237
+ dom_element.remove
238
+ Element['body'].off('click', &@close_event_handler)
239
+ end
240
+
241
+ def root_menu?
242
+ !parent.is_a?(MenuProxy) && !parent.is_a?(MenuItemProxy)
243
+ end
244
+
245
+ def root_menu
246
+ the_menu = self
247
+ the_menu = the_menu.parent_menu until the_menu.root_menu?
248
+ the_menu
249
+ end
250
+
251
+ def parent_menu
252
+ parent.parent unless root_menu?
253
+ end
254
+
255
+ def element
256
+ 'ul'
257
+ end
258
+
259
+ def dom
260
+ css_class = name
261
+ css_class += ' menu-bar' if bar?
262
+ css_class += ' menu-drop-down' if drop_down?
263
+ css_class += ' menu-pop-up' if pop_up?
264
+ @dom ||= html {
265
+ ul(id: id, class: css_class) {
266
+ }
267
+ }.to_s
268
+ end
269
+ end
270
+
271
+ MenuBarProxy = MenuProxy
272
+ end
273
+ end