sirens 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/components/component.rb +46 -0
- data/lib/components/component_behaviour.rb +160 -0
- data/lib/components/containers/splitter.rb +98 -0
- data/lib/components/containers/stack.rb +42 -0
- data/lib/components/containers/tabs.rb +24 -0
- data/lib/components/containers/window.rb +20 -0
- data/lib/components/primitive_component.rb +155 -0
- data/lib/components/widgets/button.rb +16 -0
- data/lib/components/widgets/checkbox.rb +39 -0
- data/lib/components/widgets/column_props.rb +23 -0
- data/lib/components/widgets/input_text.rb +27 -0
- data/lib/components/widgets/list.rb +68 -0
- data/lib/components/widgets/list_choice.rb +89 -0
- data/lib/components/widgets/radio_button.rb +33 -0
- data/lib/components/widgets/text.rb +31 -0
- data/lib/components/widgets/tree_choice.rb +82 -0
- data/lib/layouts/columns_builder.rb +63 -0
- data/lib/layouts/layout_builder.rb +203 -0
- data/lib/layouts/radio_button_group_builder.rb +28 -0
- data/lib/models/choice_model.rb +41 -0
- data/lib/models/list_model.rb +173 -0
- data/lib/models/tree_choice_model.rb +102 -0
- data/lib/models/value_model.rb +56 -0
- data/lib/models/virtual_tree_model.rb +150 -0
- data/lib/sirens/browsers/module_browser.rb +67 -0
- data/lib/sirens/browsers/object_browser.rb +103 -0
- data/lib/sirens/components/ancestors_list.rb +22 -0
- data/lib/sirens/components/class_browser.rb +38 -0
- data/lib/sirens/components/constants_list.rb +22 -0
- data/lib/sirens/components/method_source_code.rb +22 -0
- data/lib/sirens/components/methods_list.rb +63 -0
- data/lib/sirens/components/modules_list.rb +23 -0
- data/lib/sirens/components/namespaces_list.rb +21 -0
- data/lib/sirens/models/constant_model.rb +21 -0
- data/lib/sirens/models/method_model.rb +111 -0
- data/lib/sirens/models/module_browser_model.rb +225 -0
- data/lib/sirens/models/object_browser_model.rb +125 -0
- data/lib/sirens.rb +90 -0
- data/lib/views/button_view.rb +56 -0
- data/lib/views/check_button_view.rb +65 -0
- data/lib/views/entry_view.rb +25 -0
- data/lib/views/list_view.rb +230 -0
- data/lib/views/menu_view.rb +46 -0
- data/lib/views/notebook_view.rb +27 -0
- data/lib/views/paned_view.rb +26 -0
- data/lib/views/radio_button_view.rb +67 -0
- data/lib/views/stack_view.rb +26 -0
- data/lib/views/text_view.rb +112 -0
- data/lib/views/tree_view.rb +314 -0
- data/lib/views/view.rb +206 -0
- data/lib/views/window_view.rb +51 -0
- metadata +110 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class List < PrimitiveComponent
|
|
3
|
+
##
|
|
4
|
+
# Returns a ListView.
|
|
5
|
+
#
|
|
6
|
+
def create_view()
|
|
7
|
+
ListView.new
|
|
8
|
+
.on_selection_changed_block { |selection_items:, selection_indices:|
|
|
9
|
+
on_selection_changed(selection_items: selection_items, selection_indices: selection_indices)
|
|
10
|
+
}
|
|
11
|
+
.get_item_block { |index| model.item_at(index: index) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Defines the columns in the list with the given columns_props.
|
|
16
|
+
# column_props is an Array of props Hashes, one Hash for each column to define.
|
|
17
|
+
#
|
|
18
|
+
def define_columns(columns_props_array)
|
|
19
|
+
view.define_columns(columns_props_array)
|
|
20
|
+
|
|
21
|
+
# Sync again after adding the columns.
|
|
22
|
+
sync_ui_from_model
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Returns a default model if none is given during the initialization of this component.
|
|
27
|
+
#
|
|
28
|
+
def default_model()
|
|
29
|
+
ListModel.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Hook method called when the model value changes.
|
|
34
|
+
#
|
|
35
|
+
def on_value_changed(announcement)
|
|
36
|
+
if announcement.kind_of?(ListChanged)
|
|
37
|
+
sync_ui_from_model
|
|
38
|
+
elsif announcement.kind_of?(ItemsAdded)
|
|
39
|
+
view.add_items(items: announcement.items, index: announcement.index)
|
|
40
|
+
elsif announcement.kind_of?(ItemsUpdated)
|
|
41
|
+
view.update_items(items: announcement.items, indices: announcement.indices)
|
|
42
|
+
elsif announcement.kind_of?(ItemsRemoved)
|
|
43
|
+
view.remove_items(items: announcement.items, indices: announcement.indices)
|
|
44
|
+
else
|
|
45
|
+
raise RuntimeError.new("Unknown announcement #{announcement.class.name}.")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def sync_ui_from_model()
|
|
50
|
+
return if view.nil?
|
|
51
|
+
|
|
52
|
+
view.clear_items
|
|
53
|
+
view.add_items(items: model.list, index: 0)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Events
|
|
57
|
+
|
|
58
|
+
def on_selection_changed(selection_items:, selection_indices:)
|
|
59
|
+
return if props[:on_selection_changed].nil?
|
|
60
|
+
|
|
61
|
+
props[:on_selection_changed].call(
|
|
62
|
+
selection: selection_items,
|
|
63
|
+
indices: selection_indices,
|
|
64
|
+
list: self
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class ListChoice < PrimitiveComponent
|
|
3
|
+
##
|
|
4
|
+
# Returns a WindowView.
|
|
5
|
+
#
|
|
6
|
+
def create_view()
|
|
7
|
+
ListView.new
|
|
8
|
+
.on_selection_changed_block { |selection_items:, selection_indices:|
|
|
9
|
+
on_selection_changed(selection_items: selection_items, selection_indices: selection_indices)
|
|
10
|
+
}
|
|
11
|
+
.get_item_block { |index| model.item_at(index: index) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Defines the columns in the list with the given columns_props.
|
|
16
|
+
# column_props is an Array of props Hashes, one Hash for each column to define.
|
|
17
|
+
#
|
|
18
|
+
def define_columns(columns_props_array)
|
|
19
|
+
view.define_columns(columns_props_array)
|
|
20
|
+
|
|
21
|
+
# Sync again after adding the columns.
|
|
22
|
+
sync_ui_from_model
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Returns a default model if none is given during the initialization of this component.
|
|
27
|
+
#
|
|
28
|
+
def default_model()
|
|
29
|
+
ChoiceModel.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Returns the choices list
|
|
34
|
+
#
|
|
35
|
+
def choices()
|
|
36
|
+
model.choices
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Subscribes this component to the model events
|
|
41
|
+
#
|
|
42
|
+
def subscribe_to_model_events()
|
|
43
|
+
model.choices.add_observer(self, :on_choices_changed)
|
|
44
|
+
model.selection.add_observer(self, :on_selected_value_changed)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# Method called when the choices list changes in the model.
|
|
49
|
+
#
|
|
50
|
+
def on_choices_changed(announcement)
|
|
51
|
+
if announcement.kind_of?(ListChanged)
|
|
52
|
+
sync_ui_from_model
|
|
53
|
+
elsif announcement.kind_of?(ItemsAdded)
|
|
54
|
+
view.add_items(items: announcement.items, index: announcement.index)
|
|
55
|
+
elsif announcement.kind_of?(ItemsUpdated)
|
|
56
|
+
view.update_items(items: announcement.items, indices: announcement.indices)
|
|
57
|
+
elsif announcement.kind_of?(ItemsRemoved)
|
|
58
|
+
view.remove_items(items: announcement.items, indices: announcement.indices)
|
|
59
|
+
else
|
|
60
|
+
raise RuntimeError.new("Unknown announcement #{announcement.class.name}.")
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Method called when the selected choice changes in the model.
|
|
66
|
+
#
|
|
67
|
+
def on_selected_value_changed(announcement)
|
|
68
|
+
selected_value = announcement.new_value
|
|
69
|
+
|
|
70
|
+
selection = choices.list.index(selected_value)
|
|
71
|
+
|
|
72
|
+
selection.nil? ?
|
|
73
|
+
view.set_selection_indices([]) : view.set_selection_indices([selection])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def sync_ui_from_model()
|
|
77
|
+
return if view.nil?
|
|
78
|
+
|
|
79
|
+
view.clear_items
|
|
80
|
+
view.add_items(items: choices.list, index: 0)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Events
|
|
84
|
+
|
|
85
|
+
def on_selection_changed(selection_items:, selection_indices:)
|
|
86
|
+
model.set_selection(selection_items.first) unless model.nil?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class RadioButton < PrimitiveComponent
|
|
3
|
+
##
|
|
4
|
+
# Returns a ButtonView.
|
|
5
|
+
#
|
|
6
|
+
def create_view()
|
|
7
|
+
previous_button = props[:previous_button].nil? ? nil : props[:previous_button].view
|
|
8
|
+
|
|
9
|
+
RadioButtonView.new(
|
|
10
|
+
previous_button: previous_button,
|
|
11
|
+
on_toggled: proc{ |state:| on_toggled(state: state) }
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Actions
|
|
16
|
+
|
|
17
|
+
def click()
|
|
18
|
+
view.click
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Events
|
|
22
|
+
|
|
23
|
+
def on_value_changed(announcement)
|
|
24
|
+
view.set_value(announcement.new_value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def on_toggled(state:)
|
|
28
|
+
return if model.nil?
|
|
29
|
+
|
|
30
|
+
model.set_selection(props[:id]) if state == true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class Text < PrimitiveComponent
|
|
3
|
+
##
|
|
4
|
+
# Returns a TextView.
|
|
5
|
+
#
|
|
6
|
+
def create_view()
|
|
7
|
+
TextView.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Returns a default model if none is given during the initialization of this component.
|
|
12
|
+
#
|
|
13
|
+
def default_model()
|
|
14
|
+
ValueModel.on('')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sync_ui_from_model()
|
|
18
|
+
view.set_text(model.value) unless view.nil?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def selected_text()
|
|
22
|
+
view.selected_text
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Events
|
|
26
|
+
|
|
27
|
+
def on_value_changed(announcement)
|
|
28
|
+
view.set_text(announcement.new_value)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class TreeChoice < PrimitiveComponent
|
|
3
|
+
##
|
|
4
|
+
# Returns a WindowView.
|
|
5
|
+
#
|
|
6
|
+
def create_view()
|
|
7
|
+
TreeView.new
|
|
8
|
+
.on_selection_changed_block { |selection_items:, selection_paths:|
|
|
9
|
+
on_selection_changed(selection_items: selection_items, selection_paths: selection_paths)
|
|
10
|
+
}
|
|
11
|
+
.get_item_block { |path:| model.item_at(path: path) }
|
|
12
|
+
.get_children_block { |path:| model.children_at(path: path) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Defines the columns in the list with the given columns_props.
|
|
17
|
+
# column_props is an Array of props Hashes, one Hash for each column to define.
|
|
18
|
+
#
|
|
19
|
+
def define_columns(columns_props_array)
|
|
20
|
+
raise RuntimeError.new("The #{self.class.name} must have at least one column.") if columns_props_array.empty?
|
|
21
|
+
|
|
22
|
+
view.define_columns(columns_props_array)
|
|
23
|
+
|
|
24
|
+
# Sync again after adding the columns.
|
|
25
|
+
sync_ui_from_model
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Returns a default model if none is given during the initialization of this component.
|
|
30
|
+
#
|
|
31
|
+
def default_model()
|
|
32
|
+
TreeChoiceModel.new(roots: [], get_children_block: nil)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Returns the root items of the tree.
|
|
37
|
+
#
|
|
38
|
+
def root_items()
|
|
39
|
+
model.tree.roots
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Syncs the ui from the model.
|
|
44
|
+
#
|
|
45
|
+
def sync_ui_from_model()
|
|
46
|
+
return if view.nil?
|
|
47
|
+
|
|
48
|
+
view.clear_items
|
|
49
|
+
view.add_items(parent_iter: nil, items: model.tree.roots, index: 0)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Events
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Subscribes this component to the model events
|
|
56
|
+
#
|
|
57
|
+
def subscribe_to_model_events()
|
|
58
|
+
model.tree.add_observer(self, :on_tree_changed)
|
|
59
|
+
model.selection.add_observer(self, :on_selected_value_changed)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def on_tree_changed(announcement)
|
|
63
|
+
sync_ui_from_model
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def on_selected_value_changed(announcement)
|
|
67
|
+
selection_hierarchy = announcement.new_value
|
|
68
|
+
|
|
69
|
+
indices_path = model.path_of(selection_hierarchy)
|
|
70
|
+
|
|
71
|
+
while_updating_view do
|
|
72
|
+
view.set_selection_indices(indices_path)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def on_selection_changed(selection_items:, selection_paths:)
|
|
77
|
+
return if is_updating_view?
|
|
78
|
+
|
|
79
|
+
model.set_selection_from_path(path: selection_paths.first) unless model.nil?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class ColumnsBuilder
|
|
3
|
+
# Initializing
|
|
4
|
+
|
|
5
|
+
def initialize()
|
|
6
|
+
@props = Hash[]
|
|
7
|
+
@columns = []
|
|
8
|
+
@popup_menu = nil
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def columns()
|
|
12
|
+
@columns
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get_popup_menu()
|
|
16
|
+
@popup_menu
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Evaluating
|
|
20
|
+
|
|
21
|
+
def render(props, &block)
|
|
22
|
+
@props = props
|
|
23
|
+
@columns = []
|
|
24
|
+
@popup_menu = nil
|
|
25
|
+
|
|
26
|
+
instance_exec(self, &block)
|
|
27
|
+
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Current component model
|
|
32
|
+
|
|
33
|
+
def model(object)
|
|
34
|
+
@props[:model] = object
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Current component props
|
|
38
|
+
|
|
39
|
+
def styles(props)
|
|
40
|
+
@props.merge!(props)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def props(props)
|
|
44
|
+
@props.merge!(props)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def handlers(props)
|
|
48
|
+
@props.merge!(props)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# List columns
|
|
52
|
+
|
|
53
|
+
def column(props = Hash[])
|
|
54
|
+
@columns << ColumnProps.new(props)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Popup menu
|
|
58
|
+
|
|
59
|
+
def popup_menu(&block)
|
|
60
|
+
@popup_menu = block
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class LayoutBuilder
|
|
3
|
+
|
|
4
|
+
# Initializing
|
|
5
|
+
|
|
6
|
+
def initialize(main_component:)
|
|
7
|
+
@main_component = main_component
|
|
8
|
+
|
|
9
|
+
@props = Hash[]
|
|
10
|
+
@components = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Evaluating
|
|
14
|
+
|
|
15
|
+
def render(props = Hash[], &build_block)
|
|
16
|
+
instance_exec(@main_component, &build_block)
|
|
17
|
+
|
|
18
|
+
@main_component.add_all_components(@components)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Accessing
|
|
22
|
+
|
|
23
|
+
def merge_to_current_props(props)
|
|
24
|
+
@props.merge!(props)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Current component model
|
|
28
|
+
|
|
29
|
+
def model(object)
|
|
30
|
+
merge_to_current_props(model: object)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Current component props
|
|
34
|
+
|
|
35
|
+
def styles(props)
|
|
36
|
+
merge_to_current_props(props)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def props(props)
|
|
40
|
+
merge_to_current_props(props)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handlers(props)
|
|
44
|
+
merge_to_current_props(props)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Components
|
|
48
|
+
|
|
49
|
+
def component(component, &build_block)
|
|
50
|
+
build_component_from(Hash[], build_block) { |props, child_components|
|
|
51
|
+
component.set_props(props)
|
|
52
|
+
|
|
53
|
+
component.add_all_components(child_components)
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Containers
|
|
58
|
+
|
|
59
|
+
def window(props = Hash[], &build_block)
|
|
60
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
61
|
+
Window.new(props)
|
|
62
|
+
.add_all_components(child_components)
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def horizontal_stack(props = Hash[], &build_block)
|
|
67
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
68
|
+
Stack.horizontal(props)
|
|
69
|
+
.add_all_components(child_components)
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def vertical_stack(props = Hash[], &build_block)
|
|
74
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
75
|
+
Stack.vertical(props)
|
|
76
|
+
.add_all_components(child_components)
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def horizontal_splitter(props = Hash[], &build_block)
|
|
81
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
82
|
+
Splitter.horizontal(props)
|
|
83
|
+
.add_all_components(child_components)
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def vertical_splitter(props = Hash[], &build_block)
|
|
88
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
89
|
+
Splitter.vertical(props)
|
|
90
|
+
.add_all_components(child_components)
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def tabs(props = Hash[], &build_block)
|
|
95
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
96
|
+
Tabs.new(props)
|
|
97
|
+
.add_all_components(child_components)
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Widgets
|
|
102
|
+
|
|
103
|
+
def button(props = Hash[], &build_block)
|
|
104
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
105
|
+
Button.new(props)
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def checkbox(props = Hash[], &build_block)
|
|
110
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
111
|
+
Checkbox.new(props)
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def radio_button(props = Hash[], &build_block)
|
|
116
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
117
|
+
RadioButton.new(props)
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def radio_buttons_group(props = Hash[], &build_block)
|
|
122
|
+
buttons = RadioButtonGroupBuilder.new.render(&build_block)
|
|
123
|
+
|
|
124
|
+
@components.concat(buttons)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def list(props = Hash[], &build_block)
|
|
128
|
+
columns_builder = ColumnsBuilder.new.render(props, &build_block)
|
|
129
|
+
|
|
130
|
+
props[:popup_menu] = columns_builder.popup_menu unless columns_builder.popup_menu.nil?
|
|
131
|
+
|
|
132
|
+
List.new(props).tap { |list|
|
|
133
|
+
list.define_columns(columns_builder.columns)
|
|
134
|
+
|
|
135
|
+
@components << list
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def choices_list(props = Hash[], &build_block)
|
|
140
|
+
columns_builder = ColumnsBuilder.new.render(props, &build_block)
|
|
141
|
+
|
|
142
|
+
props[:popup_menu] = columns_builder.popup_menu unless columns_builder.popup_menu.nil?
|
|
143
|
+
|
|
144
|
+
ListChoice.new(props).tap { |list|
|
|
145
|
+
list.define_columns(columns_builder.columns)
|
|
146
|
+
|
|
147
|
+
@components << list
|
|
148
|
+
}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def input_text(props = Hash[], &build_block)
|
|
152
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
153
|
+
InputText.new(props)
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def text(props = Hash[], &build_block)
|
|
158
|
+
build_component_from(props, build_block) { |props, child_components|
|
|
159
|
+
Text.new(props)
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def choices_tree(props = Hash[], &build_block)
|
|
164
|
+
columns_builder = ColumnsBuilder.new.render(props, &build_block)
|
|
165
|
+
|
|
166
|
+
props[:popup_menu] = columns_builder.get_popup_menu unless columns_builder.get_popup_menu.nil?
|
|
167
|
+
|
|
168
|
+
TreeChoice.new(props).tap { |tree|
|
|
169
|
+
tree.define_columns(columns_builder.columns)
|
|
170
|
+
|
|
171
|
+
@components << tree
|
|
172
|
+
}
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def popup_menu(&block)
|
|
176
|
+
merge_to_current_props(popup_menu: block)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Utility methods
|
|
180
|
+
|
|
181
|
+
def build_component_from(props = Hash[], build_block, &after_build_block)
|
|
182
|
+
current_props = @props
|
|
183
|
+
current_components = @components
|
|
184
|
+
|
|
185
|
+
built_component = nil
|
|
186
|
+
|
|
187
|
+
begin
|
|
188
|
+
@props = props.clone
|
|
189
|
+
@components = []
|
|
190
|
+
|
|
191
|
+
build_block.call unless build_block.nil?
|
|
192
|
+
|
|
193
|
+
built_component = after_build_block.call(@props, @components)
|
|
194
|
+
ensure
|
|
195
|
+
@props = current_props
|
|
196
|
+
@components = current_components
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
@components << built_component unless built_component.nil?
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class RadioButtonGroupBuilder
|
|
3
|
+
|
|
4
|
+
# Initializing
|
|
5
|
+
|
|
6
|
+
def initialize()
|
|
7
|
+
@buttons = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Evaluating
|
|
11
|
+
|
|
12
|
+
def render(&block)
|
|
13
|
+
@buttons = []
|
|
14
|
+
|
|
15
|
+
instance_exec(self, &block)
|
|
16
|
+
|
|
17
|
+
@buttons
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# List columns
|
|
21
|
+
|
|
22
|
+
def radio_button(props = Hash[])
|
|
23
|
+
props[:previous_button] = @buttons.last
|
|
24
|
+
|
|
25
|
+
@buttons << RadioButton.new(props)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class ChoiceModel
|
|
3
|
+
|
|
4
|
+
# Initializing
|
|
5
|
+
|
|
6
|
+
def initialize(selection: nil, choices: [])
|
|
7
|
+
super()
|
|
8
|
+
|
|
9
|
+
@selection = ValueModel.on(selection)
|
|
10
|
+
@choices = ListModel.on(choices)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Accessing
|
|
14
|
+
|
|
15
|
+
def selection()
|
|
16
|
+
@selection
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def set_selection(new_value)
|
|
20
|
+
@selection.set_value(new_value)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def choices()
|
|
24
|
+
@choices
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def set_choices(list)
|
|
28
|
+
@choices.set_list(list)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def item_at(index:)
|
|
32
|
+
@choices[index]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Asking
|
|
36
|
+
|
|
37
|
+
def has_selection()
|
|
38
|
+
! @selection.value.nil?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|