opal-ferro 0.10.0 → 0.10.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.
@@ -0,0 +1,257 @@
1
+ module Ferro
2
+
3
+ # This module contains all form releated elements.
4
+ module Form
5
+ # Creates a form container.
6
+ # In the DOM creates a: <div>.
7
+ # A <div> is used instead of a <form> DOM element,
8
+ # so we don't have to prevent any default actions.
9
+ class Base < BaseElement
10
+
11
+ # Internal method.
12
+ def _before_create
13
+ @domtype = :div
14
+ end
15
+ end
16
+
17
+ # Creates an input element.
18
+ # In the DOM creates a: <input type="?">.
19
+ # Specify option :type to set the location.
20
+ # Use one of these values:
21
+ # :text, :password, :reset, :radio, :checkbox, :color,
22
+ # :date, :datetime-local, :email, :month, :number,
23
+ # :range, :search, :tel, :time, :url, :week.
24
+ # Or leave blank to create a :text input.
25
+ class Input < BaseElement
26
+
27
+ # Internal method.
28
+ def _before_create
29
+ @domtype = :input
30
+ @options[:type] ||= :text
31
+ @options[:value] = option_replace :content
32
+ @disabled = option_replace :disabled, false
33
+ end
34
+
35
+ # Setter method for input value.
36
+ def value
37
+ `#{@element}.value`
38
+ end
39
+
40
+ # Getter method for input value.
41
+ def value=(value)
42
+ `#{@element}.value = #{value}`
43
+ end
44
+
45
+ # Set input focus to this element.
46
+ # TODO: Find out why this doesn't seem to work.
47
+ def set_focus
48
+ `#{element}.focus()`
49
+ end
50
+
51
+ # Internal method.
52
+ def _after_create
53
+ disable if @disabled
54
+
55
+ if [:text, :password, :number].include?(@options[:type])
56
+ `#{@element}.addEventListener("keydown",function(e){self.$keydowned(e);})`
57
+ end
58
+ end
59
+
60
+ # Callback for :text, :password, :number types.
61
+ # Catches 'enter' key and calls `entered`.
62
+ def keydowned(event)
63
+ entered if Native(event).keyCode == 13
64
+ end
65
+
66
+ # Callback for :text, :password, :number types.
67
+ # Override this method to catch the 'enter' key.
68
+ def entered;end
69
+
70
+ # Disable this input.
71
+ def disable
72
+ set_attribute(:disabled, :disabled)
73
+ end
74
+
75
+ # Enable this input.
76
+ def enable
77
+ remove_attribute(:disabled)
78
+ end
79
+ end
80
+
81
+ # Creates a label element.
82
+ # In the DOM creates a: <label>.
83
+ # Specify option :for with the id of the input as value
84
+ # to set couple the label to an input.
85
+ class Label < BaseElement
86
+
87
+ # Internal method.
88
+ def _before_create
89
+ @domtype = :label
90
+ end
91
+ end
92
+
93
+ # Creates a fieldset element.
94
+ # In the DOM creates a: <fieldset>.
95
+ # Specify option :legend to set a title.
96
+ class Fieldset < BaseElement
97
+
98
+ # Internal method.
99
+ def _before_create
100
+ @domtype = :fieldset
101
+ # @legend = @options[:legend]
102
+ end
103
+ end
104
+
105
+ # Creates a textarea input element.
106
+ # In the DOM creates a: <textarea>.
107
+ # Specify option :size to set its size.
108
+ # Default size is: { rows: 40, cols: 5 }.
109
+ class Textarea < BaseElement
110
+
111
+ # Internal method.
112
+ def _before_create
113
+ @domtype = :textarea
114
+ @size = { rows: 40, cols: 5 }
115
+ end
116
+ end
117
+
118
+ # Creates a form output element.
119
+ # In the DOM creates a: <output>.
120
+ class Output < BaseElement
121
+
122
+ # Internal method.
123
+ def _before_create
124
+ @domtype = :output
125
+ end
126
+ end
127
+
128
+ # Container class for all clickable elements.
129
+ class Clickable < Input
130
+
131
+ # Internal method.
132
+ def _after_create
133
+ `#{@element}.addEventListener("click",function(e){#{clicked};document.activeElement.blur()})`
134
+ super
135
+ end
136
+
137
+ # Override this method to define what happens after
138
+ # element has been clicked.
139
+ def clicked;end
140
+ end
141
+
142
+ # Creates a form button.
143
+ # In the DOM creates a: <input type='button'>.
144
+ class Button < Clickable
145
+
146
+ # Internal method.
147
+ def _before_create
148
+ @options[:type] = :button
149
+ super
150
+ end
151
+ end
152
+
153
+ # Creates a form submit button.
154
+ # In the DOM creates a: <input type='submit'>.
155
+ class Submit < Clickable
156
+
157
+ # Internal method.
158
+ def _before_create
159
+ @options[:type] = :submit
160
+ super
161
+ end
162
+ end
163
+
164
+ # Creates a clickable block element.
165
+ # In the DOM creates a: <div>.
166
+ class Block < Clickable
167
+
168
+ # Internal method.
169
+ def _before_create
170
+ super
171
+ @domtype = :div
172
+ end
173
+ end
174
+
175
+ # Creates a clickable checkbox.
176
+ # In the DOM creates a: <input type='checkbox'>.
177
+ class CheckBox < Clickable
178
+
179
+ # Internal method.
180
+ def _before_create
181
+ @options[:type] = :checkbox
182
+ super
183
+ end
184
+
185
+ # Test if checkbox is checked.
186
+ #
187
+ # @return [Boolean] True if checkbox is checked
188
+ def checked?
189
+ `#{@element}.checked`
190
+ end
191
+ end
192
+
193
+ # Creates a select list with options.
194
+ # In the DOM creates a: <select> and <option>.
195
+ # Specify option :list = Hash to set selectable options.
196
+ class Select < BaseElement
197
+
198
+ # Internal method.
199
+ def _before_create
200
+ @domtype = :select
201
+ @list = option_replace :list, {}
202
+ super
203
+ end
204
+
205
+ # Internal method.
206
+ # TODO Use _after_create
207
+ def after_create
208
+ @list.each do |value, content|
209
+ add_option(value, content)
210
+ end
211
+
212
+ `#{@element}.addEventListener("change",function(e){#{changed};document.activeElement.blur()})`
213
+ super
214
+ end
215
+
216
+ # Override this method to specify what happens after the
217
+ # select elements value is changed.
218
+ def changed;end
219
+
220
+ # Manually add an option to the select element
221
+ #
222
+ # @param [String, Symbol] value Key of the option
223
+ # @param [String] content Content to be displayed in the select list
224
+ # @return [ElementVar] Returns the newly created element
225
+ def add_option(value, content)
226
+ add_child(
227
+ "opt_#{value}",
228
+ Element::Var,
229
+ domtype: :option,
230
+ value: value,
231
+ content: content
232
+ )
233
+ end
234
+
235
+ # Returns the currently selected option.
236
+ #
237
+ # @return [Hash] Returns a hash with keys: :option and :text
238
+ def selection
239
+ option = `#{element}.options[#{element}.selectedIndex].value`
240
+ text = `#{element}.options[#{element}.selectedIndex].text`
241
+ { option: option, text: text }
242
+ end
243
+
244
+ # Set the selected option.
245
+ #
246
+ # @param [String, Symbol] option Key of the option
247
+ def select(option)
248
+ `for(var i=0; i < #{element}.options.length; i++) {
249
+ if (#{element}.options[i].value === #{option}) {
250
+ #{element}.selectedIndex = i;
251
+ break;
252
+ }
253
+ }`
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,137 @@
1
+ module Ferro
2
+
3
+ # This module contains all 'inline' elements.
4
+ module Element
5
+ # Creates a block element. Alias for BaseElement.
6
+ # In the DOM creates a: <div>.
7
+ class Block < BaseElement
8
+ end
9
+
10
+ # Creates an inline text element.
11
+ # In the DOM creates a: <p> or <h1> .. <h6> depending on
12
+ # option :size. Set to 1..6 to create <h.> elements.
13
+ # Leave option blank or use 0 to create <p> elements.
14
+ class Text < BaseElement
15
+
16
+ # Internal method.
17
+ def _before_create
18
+ @size = option_replace :size, 0
19
+ @domtype = @size == 0 ? :p : "h#{@size}"
20
+ end
21
+ end
22
+
23
+ # Creates an inline element.
24
+ # In the DOM creates a: <ol> or <ul> depending on
25
+ # option :type. Set to '1', 'A', 'a', 'I' or 'i' to create <ol>
26
+ # elements. Leave option blank to create <ul> elements.
27
+ class List < BaseElement
28
+
29
+ # Internal method.
30
+ def _before_create
31
+ @domtype = @options.has_key?(:type) ? :ol : :ul
32
+ @items = []
33
+ @id = Sequence.new 'list_'
34
+ end
35
+
36
+ # Add an item to the (un)ordered list.
37
+ #
38
+ # @param [String] element_class Ruby classname for the new element
39
+ # @param [Hash] options Any options for the new element
40
+ def add_item(element_class, options = {})
41
+ @items << add_child(@id.next, element_class, options).sym
42
+ end
43
+
44
+ # The number of items in the list.
45
+ #
46
+ # @return [Integer] Returns the number of items in the list
47
+ def item_count
48
+ @items.length
49
+ end
50
+
51
+ # The first item in the list.
52
+ #
53
+ # @return [element] Returns first element in the list
54
+ def first_item
55
+ @children[@items.first]
56
+ end
57
+
58
+ # The last item in the list.
59
+ #
60
+ # @return [element] Returns last element in the list
61
+ def last_item
62
+ @children[@items.last]
63
+ end
64
+
65
+ # Remove an item from the (un)ordered list.
66
+ #
67
+ # @param [Symbol] sym The symbol of the item to be removed
68
+ def unlist_item(sym)
69
+ @items.delete_if { |item| item == sym }
70
+ end
71
+ end
72
+
73
+ # Creates an inline element.
74
+ # In the DOM creates a: <li>.
75
+ # To be used together with {List}.
76
+ class ListItem < BaseElement
77
+
78
+ # Internal method.
79
+ def _before_create
80
+ @domtype = :li
81
+ end
82
+
83
+ # Remove this item from the list and the DOM.
84
+ def destroy
85
+ super
86
+ parent.unlist_item(@sym)
87
+ end
88
+ end
89
+
90
+ # Creates an inline element.
91
+ # In the DOM creates a: <a>.
92
+ # When link is clicked will navigate to the new location within the
93
+ # application or page 404 if not found.
94
+ # Specify option :href to set the location.
95
+ class Anchor < BaseElement
96
+
97
+ # Internal method.
98
+ def _before_create
99
+ @domtype = :a
100
+ @href = @options[:href]
101
+ end
102
+
103
+ # Internal method.
104
+ def _after_create
105
+ `#{@element}.addEventListener("click",function(e){e.preventDefault();history.pushState(null,null,#{@href});#{clicked};document.activeElement.blur();})`
106
+ end
107
+
108
+ # Set a new html reference for this item.
109
+ #
110
+ # @param [String] value New reference
111
+ def update_href(value)
112
+ @href = value
113
+ set_attribute('href', @href)
114
+ end
115
+
116
+ # Callback for click event. Calls `router.navigated`.
117
+ # Override this method to change its behavior.
118
+ def clicked
119
+ router.navigated
120
+ end
121
+ end
122
+
123
+ # Creates an inline element.
124
+ # In the DOM creates a: <a>.
125
+ # When link is clicked will navigate to a location outside the
126
+ # application and in a new tab.
127
+ # Specify option :target to set the location.
128
+ class ExternalLink < BaseElement
129
+
130
+ # Internal method.
131
+ def _before_create
132
+ @domtype = :a
133
+ @options[:target] ||= '_blank'
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,60 @@
1
+ module Ferro
2
+ module Element
3
+ # Creates a miscellaneous element.
4
+ # In the DOM creates a: <img>.
5
+ # Will be changed to use <figure> and <figcaption>
6
+ # Specify option :src to set the source url.
7
+ class Image < BaseElement
8
+
9
+ # Internal method.
10
+ def _before_create
11
+ @domtype = :img
12
+ @options[:alt] ||= @options[:src]
13
+ end
14
+ end
15
+
16
+ # Creates a miscellaneous element.
17
+ # In the DOM creates a: <canvas>.
18
+ class Canvas < BaseElement
19
+
20
+ # Internal method.
21
+ def _before_create
22
+ @domtype = :canvas
23
+ end
24
+ end
25
+
26
+ # Creates a miscellaneous element.
27
+ # In the DOM creates a: <script>.
28
+ # Specify option :invoke = false to prevent the script
29
+ # from being executed.
30
+ class Script < BaseElement
31
+
32
+ # Internal method.
33
+ def _before_create
34
+ @domtype = :script
35
+ @run = option_replace :invoke, true
36
+ end
37
+
38
+ # Internal method.
39
+ def _after_create
40
+ load
41
+ invoke if @run
42
+ end
43
+
44
+ # Override method to specify the content of the Javascript.
45
+ def load;end
46
+
47
+ # Override method to specify the execution of the script.
48
+ def invoke;end
49
+ end
50
+
51
+ # Creates any kind of element depending on option :domtype.
52
+ class Var < BaseElement
53
+
54
+ # Internal method.
55
+ def _before_create
56
+ @domtype = option_replace :domtype, :div
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,187 @@
1
+ module Ferro
2
+ # This class encapsulates all functionality of
3
+ # Master Object Model elements.
4
+ # Every Ferro element should (indirectly) inherit from this class.
5
+ # Ferro elements should not be instanciated directly. Instead the parent
6
+ # element should call the add_child method.
7
+ class BaseElement
8
+
9
+ include Elementary
10
+
11
+ attr_reader :parent, :sym, :children, :element, :domtype
12
+
13
+ # Create the element and continue the creation
14
+ # process (casading).
15
+ # Ferro elements should not be instanciated directly. Instead the
16
+ # parent element should call the add_child method.
17
+ #
18
+ # @param [String] parent The parent Ruby element
19
+ # @param [String] sym The symbolized name for the element
20
+ # @param [Hash] options Any options for the creation process
21
+ def initialize(parent, sym, options = {})
22
+ @parent = parent
23
+ @sym = sym
24
+ @children = {}
25
+ @element = nil
26
+ @domtype = :div
27
+ @states = {}
28
+ @options = options
29
+
30
+ creation
31
+ end
32
+
33
+ # Searches the element hierarchy upwards until the factory is found
34
+ def factory
35
+ @parent.factory
36
+ end
37
+
38
+ # Searches the element hierarchy upwards until the root element is
39
+ # found
40
+ def root
41
+ @parent.root
42
+ end
43
+
44
+ # Searches the element hierarchy upwards an element is found that is
45
+ # a component
46
+ def component
47
+ @parent.component
48
+ end
49
+
50
+ # Searches the element hierarchy upwards until the router is found
51
+ def router
52
+ @parent.router
53
+ end
54
+
55
+ # Delete a key from the elements options hash. Will be renamed
56
+ # to option_delete.
57
+ #
58
+ # @param [key] key Key of the option hash to be removed
59
+ # @param [value] default Optional value to use if option value is nil
60
+ # @return [value] Return the current option value or value of
61
+ # default parameter
62
+ def option_replace(key, default = nil)
63
+ value = @options[key] || default
64
+ @options.delete(key) if @options.has_key?(key)
65
+ value
66
+ end
67
+
68
+ # Add states to the element. A state toggles a CSS class with the
69
+ # same (dasherized) name as the state.
70
+ # If the state is thruthy (not nil or true) the CSS class is added
71
+ # to the element. Otherwise the CSS class is removed.
72
+ #
73
+ # @param [Array] states An array of state names to add to the
74
+ # element. All disabled (false) state initially.
75
+ def add_states(states)
76
+ states.each do |state|
77
+ add_state(state)
78
+ end
79
+ end
80
+
81
+ # Add a state to the element. A state toggles a CSS class with the
82
+ # same (dasherized) name as the state.
83
+ # If the state is thruthy (not nil or true) the CSS class is added
84
+ # to the element. Otherwise the CSS class is removed.
85
+ #
86
+ # @param [String] state The state name to add to the element
87
+ # @param [value] value The initial enabled/disabled state value
88
+ def add_state(state, value = false)
89
+ @states[state] = [factory.dasherize(state), value]
90
+ classify_state @states[state]
91
+ end
92
+
93
+ # Update the value of the state.
94
+ #
95
+ # @param [String] state The state name
96
+ # @param [Boolean] active The new value of the state. Pass
97
+ # true to enable and set the CSS class
98
+ # false to disable and remove the CSS class
99
+ # nil to skip altering state and the CSS class
100
+ def update_state(state, active)
101
+ if !active.nil?
102
+ @states.each do |s, v|
103
+ v[1] = active if s == state
104
+ classify_state v
105
+ end
106
+ end
107
+ end
108
+
109
+ # Toggle the boolean value of the state
110
+ #
111
+ # @param [String] state The state name
112
+ def toggle_state(state)
113
+ @states.select { |s, _| s == state }.each do |s, v|
114
+ v[1] = !v[1]
115
+ classify_state v
116
+ end
117
+ end
118
+
119
+ # Add or remove the CSS class for the state
120
+ #
121
+ # @param [String] state The state name
122
+ def classify_state(state)
123
+ if state[1]
124
+ `#{element}.classList.add(#{state[0]})`
125
+ else
126
+ `#{element}.classList.remove(#{state[0]})`
127
+ end
128
+ end
129
+
130
+ # Determine if the state is active
131
+ #
132
+ # @param [String] state The state name
133
+ # @return [Boolean] The state value
134
+ def state_active?(state)
135
+ @states[state][1]
136
+ end
137
+
138
+ # Get the current html value of the element
139
+ #
140
+ # @return [String] The html value
141
+ def value
142
+ `#{@element}.innerHTML`
143
+ end
144
+
145
+ # Get the current text content of the element
146
+ #
147
+ # @return [String] The text value
148
+ def get_text
149
+ `#{@element}.textContent`
150
+ end
151
+
152
+ # Set the current text content of the element
153
+ #
154
+ # @param [String] value The new text value
155
+ def set_text(value)
156
+ # https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
157
+ `#{@element}.textContent = #{value}`
158
+ end
159
+
160
+ # Set the current html content of the element. Use with caution if
161
+ # the html content is not trusted, it may be invalid or contain scripts.
162
+ #
163
+ # @param [String] raw_html The new html value
164
+ def html(raw_html)
165
+ `#{@element}.innerHTML = #{raw_html}`
166
+ end
167
+
168
+ # Set an attribute value on the elements corresponding DOM element
169
+ #
170
+ # @param [String] name The attribute name
171
+ # @param [String] value The attribute value
172
+ def set_attribute(name, value)
173
+ if name == 'scrollTop'
174
+ `#{element}.scrollTop=#{value}`
175
+ else
176
+ `#{element}.setAttribute(#{name}, #{value})`
177
+ end
178
+ end
179
+
180
+ # Remove an attribute value on the elements corresponding DOM element
181
+ #
182
+ # @param [String] name The attribute name
183
+ def remove_attribute(name)
184
+ `#{element}.removeAttribute(#{name})`
185
+ end
186
+ end
187
+ end