opal-ferro 0.10.1 → 0.10.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cf34d1239b6886601737690e639cadd7e0fd9971a73cb233eed18faf78ba764
4
- data.tar.gz: 9d6f89cbc0c4fc22c095d0b3d057e1255ccbc28f2b6ae8da7cdca3222d6fae39
3
+ metadata.gz: f2d8c1d43bb035ec89c3ec3b1508845063b0b941f4384d353de5cfd496349f93
4
+ data.tar.gz: 3090d75958d6331a6957cd7e92406bf4e718ece68ee9b2dc0e7e92d1d0e64052
5
5
  SHA512:
6
- metadata.gz: 4e92a9b4a555a1a8c9ae937b222a43880b3d650aefb5400fe08766cb8cdf7f4e3f95351ba8b267c3f7f6e1d08dc5967c465056bf911572cd2fd2c6a1c94b3e4d
7
- data.tar.gz: 7de1f758372345527f612eeb744727c4c97282f706154979edc439f230f00a4fa043403c2785570dee947f625212b9041c9e691c8019c03ee8a34704074b386a
6
+ metadata.gz: ed7efe3c96aaede6364c1c9c8519ebd071e8d8b6c5643134a55064ea5156600961fd2acd007da5e2faddce2bd6962712c351e790cfe99ca5997c0b2016450e41
7
+ data.tar.gz: ecd0acb39193f98faabb31da93383d9de08b1fccfb03cde7c34784661d3eaa2ff292aa4f069c7456f3ab2a786c330b832fe99797145c584e55d52c8e1d64962c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 0.10.2 - June 1, 2018
2
+
3
+ [0.10.2]: https://github.com/easydatawarehousing/opal-ferro/compare/v0.10.1...v0.10.2
4
+
5
+ - Added the style compositor to map Ruby classnames to CSS classnames
6
+ - Fixed: don't add clicks on an anchor to the browser history when the href is empty
7
+
1
8
  # 0.10.1 - March 31, 2018
2
9
 
3
10
  - Added documentation (using Yard)
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Opal-Ferro
2
2
 
3
3
  [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://rubydoc.org/gems/opal-ferro)
4
- [![Gem Version](https://img.shields.io/badge/gem%20version-0.10.1-blue.svg)](https://github.com/easydatawarehousing/opal-ferro/releases)
4
+ [![Gem Version](https://img.shields.io/badge/gem%20version-0.10.2-blue.svg)](https://github.com/easydatawarehousing/opal-ferro/releases)
5
5
  [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
6
6
 
7
7
  Ferro is a small Ruby library on top of [Opal](http://opalrb.com/)
@@ -15,6 +15,7 @@ just beautiful and simple Ruby code. Front-End-Ruby-ROcks!
15
15
  * [Creation lifecycle](#lifecycle)
16
16
  * [Navigating the Master Object Model](#navigating)
17
17
  * [Styling Ferro elements](#styling)
18
+ * [Using a compositor to style elements](#compositor)
18
19
  * [More information](#more)
19
20
 
20
21
  <a name="ferro"></a>
@@ -174,6 +175,48 @@ one for its Ruby superclass `FerroFormButton`.
174
175
  These classnames are _dasherized_. In CSS you can reference these
175
176
  classnames as `demo-button` and `ferro-form-button`.
176
177
 
178
+ <a name="compositor"></a>
179
+
180
+ ## Using a compositor to style elements
181
+
182
+ There is alternative way to add styling to generated DOM objects.
183
+ In your `Document` you can set a `Compositor` object.
184
+ A compositor maps Ruby class names to a list of CSS class names.
185
+ When a Ferro object is created its class name is looked up in the
186
+ compositor mapping. If found the list of CSS classes will be added
187
+ to the corresponding DOM element.
188
+ The mapping is simply a Ruby Hash. The keys are Ruby class names,
189
+ the values are CSS class names.
190
+ Optionally you can use the theme parameter to create different
191
+ mappings per theme. Use the `Document.switch_compositor_theme`
192
+ method to switch themes at run time.
193
+ Add the compositor like this:
194
+
195
+ class Document < Ferro::Document
196
+ def before_create
197
+ @compositor = AppCompositor.new :dark
198
+ end
199
+ end
200
+
201
+ The theme parameter is optional. A simple mapping might look like this:
202
+
203
+ class MyCompositor < Ferro::Compositor
204
+
205
+ def map(theme)
206
+ m = {
207
+ 'Document' => %w{black bg-white sans-serif},
208
+ 'Banner' => %w{w-100},
209
+ }
210
+
211
+ if theme == :dark
212
+ m['Document'] = %w{white bg-black sans-serif}
213
+ end
214
+
215
+ m
216
+ end
217
+ end
218
+
219
+
177
220
  <a name="more"></a>
178
221
 
179
222
  ## More information
@@ -8,6 +8,6 @@ module Opal
8
8
  # compatibility with the Opal version with that same number.
9
9
  # So Ferro 0.10.x should be compatible with and dependant
10
10
  # on Opal 0.10.x.
11
- VERSION = "0.10.1"
11
+ VERSION = "0.10.2"
12
12
  end
13
13
  end
@@ -26,6 +26,8 @@ module Ferro
26
26
  # elements. Leave option blank to create <ul> elements.
27
27
  class List < BaseElement
28
28
 
29
+ attr_reader :items
30
+
29
31
  # Internal method.
30
32
  def _before_create
31
33
  @domtype = @options.has_key?(:type) ? :ol : :ul
@@ -98,11 +100,22 @@ module Ferro
98
100
  def _before_create
99
101
  @domtype = :a
100
102
  @href = @options[:href]
103
+ @push = @href.to_s != ''
101
104
  end
102
105
 
103
106
  # Internal method.
104
107
  def _after_create
105
- `#{@element}.addEventListener("click",function(e){e.preventDefault();history.pushState(null,null,#{@href});#{clicked};document.activeElement.blur();})`
108
+ `#{@element}.addEventListener("click", function(e) {
109
+ e.preventDefault();
110
+
111
+ if (#{@push}) {
112
+ history.pushState(null, null, #{@href});
113
+ }
114
+
115
+ #{clicked};
116
+
117
+ document.activeElement.blur();
118
+ })`
106
119
  end
107
120
 
108
121
  # Set a new html reference for this item.
@@ -86,7 +86,11 @@ module Ferro
86
86
  # @param [String] state The state name to add to the element
87
87
  # @param [value] value The initial enabled/disabled state value
88
88
  def add_state(state, value = false)
89
- @states[state] = [factory.dasherize(state), value]
89
+ @states[state] = [
90
+ factory.composite_state(self.class.name, state),
91
+ value
92
+ ]
93
+
90
94
  classify_state @states[state]
91
95
  end
92
96
 
@@ -121,9 +125,13 @@ module Ferro
121
125
  # @param [String] state The state name
122
126
  def classify_state(state)
123
127
  if state[1]
124
- `#{element}.classList.add(#{state[0]})`
128
+ state[0].each do |name|
129
+ `#{element}.classList.add(#{name})`
130
+ end
125
131
  else
126
- `#{element}.classList.remove(#{state[0]})`
132
+ state[0].each do |name|
133
+ `#{element}.classList.remove(#{name})`
134
+ end
127
135
  end
128
136
  end
129
137
 
@@ -0,0 +1,77 @@
1
+ module Ferro
2
+ # Composite styling for DOM objects.
3
+ class Compositor
4
+
5
+ attr_reader :current_theme
6
+
7
+ # Create the style compositor.
8
+ #
9
+ # @param [Symbol] theme Optional styling theme
10
+ def initialize(theme = nil)
11
+ @current_theme = theme
12
+ end
13
+
14
+ # Override this method to define a hash containing the mapping
15
+ # of Ruby classnames to an array of css classnames.
16
+ # To use the same styling on multiple objects: return the name
17
+ # of the object containing the styling as a String
18
+ # (see css_classes_for method).
19
+ #
20
+ # This hash may also contain the mapping for states.
21
+ # A state name is: <Ruby class name>::<state>.
22
+ # For instance 'MenuLink::active'.
23
+ def map
24
+ raise NotImplementedError
25
+ end
26
+
27
+ # Map a Ruby classname to an array of css classnames.
28
+ # If the mapping result is a string: recursively lookup
29
+ # the mapping for that string.
30
+ #
31
+ # @param [String] classname Ruby classname
32
+ # @return [Array] A list of CSS class names
33
+ def css_classes_for(classname)
34
+ css_classes_for_map classname, mapping
35
+ end
36
+
37
+ # Internal method to get mapping from selected map.
38
+ def css_classes_for_map(classname, map)
39
+ css = map[classname]
40
+ css.class == String ? css_classes_for_map(css, map) : (css || [])
41
+ end
42
+
43
+ # Internal method to switch to a new theme.
44
+ def switch_theme(root_element, theme)
45
+ old_map = @mapping
46
+ new_map = map(theme)
47
+
48
+ root_element.each_child do |e|
49
+ old_classes = css_classes_for_map e.class.name, old_map
50
+ new_classes = css_classes_for_map e.class.name, new_map
51
+ update_element_css_classes(e, old_classes, new_classes)
52
+
53
+ old_classes = css_classes_for_map e.class.superclass.name, old_map
54
+ new_classes = css_classes_for_map e.class.superclass.name, new_map
55
+ update_element_css_classes(e, old_classes, new_classes)
56
+ end
57
+
58
+ @mapping = new_map
59
+ end
60
+
61
+ # Internal method to add/remove CSS classes for an object.
62
+ def update_element_css_classes(obj, old_classes, new_classes)
63
+ (old_classes - new_classes).each do |name|
64
+ `#{obj.element}.classList.remove(#{name})`
65
+ end
66
+
67
+ (new_classes - old_classes).each do |name|
68
+ `#{obj.element}.classList.add(#{name})`
69
+ end
70
+ end
71
+
72
+ # Internal method to cache the mapping.
73
+ def mapping
74
+ @mapping ||= map(@current_theme)
75
+ end
76
+ end
77
+ end
@@ -12,10 +12,14 @@ module Ferro
12
12
 
13
13
  include Elementary
14
14
 
15
+ attr_reader :sym, :children, :compositor
16
+
15
17
  # Create the document and start the creation
16
18
  # process (casading).
17
19
  def initialize
18
- @children = {}
20
+ @compositor = nil
21
+ @sym = :document
22
+ @children = {}
19
23
  creation
20
24
  end
21
25
 
@@ -31,7 +35,7 @@ module Ferro
31
35
 
32
36
  # Returns the one and only instance of the factory.
33
37
  def factory
34
- @factory ||= Factory.new
38
+ @factory ||= Factory.new(self, @compositor)
35
39
  end
36
40
 
37
41
  # The document class is the root element.
@@ -44,6 +48,20 @@ module Ferro
44
48
  self
45
49
  end
46
50
 
51
+ # Returns the domtype of the document instance.
52
+ def domtype
53
+ :body
54
+ end
55
+
56
+ # Switch to a new theme for the compositor,
57
+ # this will remove/add CSS classes for all
58
+ # elements as needed.
59
+ #
60
+ # param [Symbol] theme The new theme name
61
+ def switch_compositor_theme(theme)
62
+ @compositor.switch_theme(self, theme) if @compositor
63
+ end
64
+
47
65
  # Returns the one and only instance of the router.
48
66
  def router
49
67
  @router ||= Router.new(method(:page404))
@@ -97,6 +97,20 @@ module Ferro
97
97
  @children.delete(sym)
98
98
  end
99
99
 
100
+ # Recursively iterate all child elements
101
+ #
102
+ # param [Block] block A block to execute for every child element
103
+ # and the element itself
104
+ def each_child(&block)
105
+ if block_given?
106
+ block.call self
107
+
108
+ @children.each do |_, child|
109
+ child.each_child(&block)
110
+ end
111
+ end
112
+ end
113
+
100
114
  # Remove a DOM element.
101
115
  def destroy
102
116
  `#{parent.element}.removeChild(#{element})`
@@ -6,14 +6,19 @@ module Ferro
6
6
 
7
7
  # Creates the factory. Do not create a factory directly, instead
8
8
  # call the 'factory' method that is available in all Ferro classes
9
- def initialize
9
+ #
10
+ # @param [Object] target The Ruby class instance
11
+ # @param [Compositor] compositor A style-compositor object or nil
12
+ def initialize(target, compositor)
13
+ @compositor = compositor
10
14
  @body = `document.body`
11
15
  `while (document.body.firstChild) {document.body.removeChild(document.body.firstChild);}`
16
+ composite_classes(target, @body, false)
12
17
  end
13
18
 
14
19
  # Create a DOM element.
15
20
  #
16
- # @param [String] target The Ruby class instance
21
+ # @param [Object] target The Ruby class instance
17
22
  # @param [String] type Type op DOM element to create
18
23
  # @param [String] parent The Ruby parent element
19
24
  # @param [Hash] options Options to pass to the element.
@@ -38,6 +43,9 @@ module Ferro
38
43
  `#{element}.classList.add(#{dasherize(target.class.superclass.name)})`
39
44
  end
40
45
 
46
+ # Add classes defined by compositor
47
+ composite_classes(target, element, target.class.superclass != BaseElement)
48
+
41
49
  # Set ruby object_id as default element id
42
50
  if !options.has_key?(:id)
43
51
  `#{element}.id = #{target.object_id}`
@@ -79,5 +87,38 @@ module Ferro
79
87
  return class_name if class_name !~ /-/
80
88
  class_name.gsub(/(?:-|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.strip
81
89
  end
90
+
91
+ # Convert a state-name to a list of CSS class names.
92
+ #
93
+ # @param [String] class_name Ruby class name
94
+ # @param [String] state State name
95
+ # @return [String] A list of CSS class names
96
+ def composite_state(class_name, state)
97
+ if @compositor
98
+ list = @compositor.css_classes_for("#{class_name}::#{state}")
99
+ return list if !list.empty?
100
+ end
101
+
102
+ [ dasherize(state) ]
103
+ end
104
+
105
+ # Internal method
106
+ # Composite CSS classes from Ruby class name
107
+ def composite_classes(target, element, add_superclass)
108
+ if @compositor
109
+ composite_for(target.class.name, element)
110
+
111
+ if add_superclass
112
+ composite_for(target.class.superclass.name, element)
113
+ end
114
+ end
115
+ end
116
+
117
+ # Internal method
118
+ def composite_for(classname, element)
119
+ @compositor.css_classes_for(classname).each do |name|
120
+ `#{element}.classList.add(#{name})`
121
+ end
122
+ end
82
123
  end
83
124
  end
data/opal/opal-ferro.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'opal'
2
2
  require 'opal-ferro/ferro_elementary'
3
3
  require 'opal-ferro/ferro_base_element'
4
+ require 'opal-ferro/ferro_compositor'
4
5
  require 'opal-ferro/ferro_document'
5
6
  require 'opal-ferro/ferro_factory'
6
7
  require 'opal-ferro/ferro_router'
data/opal-ferro.gemspec CHANGED
@@ -12,6 +12,12 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Simplifying web-development: no more html, just beautiful and simple Ruby code.}
13
13
  s.description = %q{Ferro is a small Ruby library on top of Opal that enables an object-oriented programming style for creating code that runs in the webbrowser. No more distractions like HTML and searching for DOM elements, just beautiful and simple Ruby code. Front-End-Ruby-ROcks!}
14
14
  s.homepage = 'https://github.com/easydatawarehousing/opal-ferro'
15
+ s.metadata = {
16
+ "homepage_uri" => "https://github.com/easydatawarehousing/opal-ferro/",
17
+ "changelog_uri" => "https://github.com/easydatawarehousing/opal-ferro/blob/master/CHANGELOG.md",
18
+ "source_code_uri" => "https://github.com/easydatawarehousing/opal-ferro/",
19
+ "bug_tracker_uri" => "https://github.com/easydatawarehousing/opal-ferro/issues",
20
+ }
15
21
  s.license = 'MIT'
16
22
 
17
23
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opal-ferro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivo Herweijer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-28 00:00:00.000000000 Z
11
+ date: 2018-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opal
@@ -110,6 +110,7 @@ files:
110
110
  - opal/opal-ferro/elements/ferro_inline.js.rb
111
111
  - opal/opal-ferro/elements/ferro_misc.js.rb
112
112
  - opal/opal-ferro/ferro_base_element.js.rb
113
+ - opal/opal-ferro/ferro_compositor.js.rb
113
114
  - opal/opal-ferro/ferro_document.js.rb
114
115
  - opal/opal-ferro/ferro_elementary.js.rb
115
116
  - opal/opal-ferro/ferro_factory.js.rb
@@ -119,7 +120,11 @@ files:
119
120
  homepage: https://github.com/easydatawarehousing/opal-ferro
120
121
  licenses:
121
122
  - MIT
122
- metadata: {}
123
+ metadata:
124
+ homepage_uri: https://github.com/easydatawarehousing/opal-ferro/
125
+ changelog_uri: https://github.com/easydatawarehousing/opal-ferro/blob/master/CHANGELOG.md
126
+ source_code_uri: https://github.com/easydatawarehousing/opal-ferro/
127
+ bug_tracker_uri: https://github.com/easydatawarehousing/opal-ferro/issues
123
128
  post_install_message:
124
129
  rdoc_options: []
125
130
  require_paths: