fron 0.2.0rc1 → 1.0.0rc1

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +20 -0
  3. data/.reek +2 -0
  4. data/.rubocop.yml +14 -11
  5. data/Gemfile +6 -3
  6. data/Gemfile.lock +73 -86
  7. data/Rakefile +11 -15
  8. data/Readme.md +1 -1
  9. data/fron.gemspec +2 -2
  10. data/lib/fron/version.rb +1 -1
  11. data/opal/fron.rb +2 -0
  12. data/opal/fron/core.rb +1 -0
  13. data/opal/fron/core/behaviors/components.rb +18 -10
  14. data/opal/fron/core/behaviors/events.rb +9 -10
  15. data/opal/fron/core/behaviors/routes.rb +6 -10
  16. data/opal/fron/core/behaviors/style.rb +30 -0
  17. data/opal/fron/core/component.rb +44 -23
  18. data/opal/fron/core/eventable.rb +3 -3
  19. data/opal/fron/core/logger.rb +1 -1
  20. data/opal/fron/core/sheet.rb +140 -0
  21. data/opal/fron/core_ext.rb +1 -0
  22. data/opal/fron/core_ext/array.rb +23 -0
  23. data/opal/fron/core_ext/hash.rb +6 -6
  24. data/opal/fron/core_ext/kernel.rb +10 -1
  25. data/opal/fron/core_ext/numeric.rb +7 -0
  26. data/opal/fron/core_ext/time.rb +6 -0
  27. data/opal/fron/dom/document.rb +6 -3
  28. data/opal/fron/dom/element.rb +79 -19
  29. data/opal/fron/dom/event.rb +5 -1
  30. data/opal/fron/dom/modules/dimensions.rb +0 -14
  31. data/opal/fron/dom/modules/element_accessor.rb +25 -0
  32. data/opal/fron/dom/modules/events.rb +1 -1
  33. data/opal/fron/dom/node.rb +7 -5
  34. data/opal/fron/dom/style.rb +0 -2
  35. data/opal/fron/dom/window.rb +14 -0
  36. data/opal/fron/event_mock.rb +24 -6
  37. data/opal/fron/js/scroll_into_view_if_needed.js +27 -0
  38. data/opal/fron/js/syntetic_event.js +20 -13
  39. data/opal/fron/request/request.rb +21 -19
  40. data/opal/fron/request/response.rb +1 -1
  41. data/opal/fron/storage.rb +2 -0
  42. data/opal/fron/storage/local_storage.rb +3 -45
  43. data/opal/fron/storage/session_storage.rb +12 -0
  44. data/opal/fron/storage/store.rb +54 -0
  45. data/opal/fron/utils/drag.rb +21 -18
  46. data/opal/fron/utils/keyboard.rb +14 -12
  47. data/opal/fron/utils/point.rb +12 -4
  48. data/opal/fron/utils/render_proc.rb +6 -2
  49. data/spec/core-ext/array_spec.rb +10 -2
  50. data/spec/core-ext/numeric_spec.rb +6 -0
  51. data/spec/core/behaviors/style_spec.rb +51 -0
  52. data/spec/core/component_inheritance_spec.rb +10 -15
  53. data/spec/core/component_spec.rb +10 -15
  54. data/spec/dom/element_spec.rb +12 -1
  55. data/spec/dom/modules/classlist_spec.rb +8 -9
  56. data/spec/dom/modules/dimensions_spec.rb +2 -1
  57. data/spec/dom/modules/events_spec.rb +42 -31
  58. data/spec/dom/style_spec.rb +1 -1
  59. data/spec/spec_helper.rb +0 -1
  60. data/spec/utils/drag_spec.rb +2 -2
  61. data/spec/utils/keyboard_spec.rb +4 -1
  62. data/website/application.rb +4 -0
  63. data/website/config.ru +30 -0
  64. data/website/examples/content_editable.rb +29 -0
  65. data/website/examples/converter.rb +49 -0
  66. data/website/examples/icon_button.rb +20 -0
  67. data/website/examples/image_paragraph.rb +33 -0
  68. data/website/examples/my_blue_box.rb +9 -0
  69. data/website/examples/my_box.rb +9 -0
  70. data/website/examples/my_button.rb +27 -0
  71. data/website/examples/my_green_box.rb +14 -0
  72. data/website/examples/source_reader.rb +32 -0
  73. data/website/examples/text_area.rb +42 -0
  74. data/website/pages/components.md.erb +16 -0
  75. data/website/pages/components/composition.md.erb +9 -0
  76. data/website/pages/components/events.md.erb +20 -0
  77. data/website/pages/components/inheritance.md.erb +19 -0
  78. data/website/pages/components/routes.md.erb +49 -0
  79. data/website/pages/components/styles.md.erb +38 -0
  80. data/website/pages/getting-started.md +8 -0
  81. data/website/pages/home.md +4 -0
  82. data/website/pages/intro.md +30 -0
  83. data/website/pages/utilities.md +10 -0
  84. data/website/pages/utilities/local-storage.md.erb +16 -0
  85. data/website/pages/utilities/request.md.erb +12 -0
  86. data/website/setup.rb +162 -0
  87. data/website/vendor/highlight.js +2 -0
  88. data/website/vendor/highlight.ruby.js +1 -0
  89. data/website/vendor/marked.min.js +6 -0
  90. metadata +43 -7
@@ -1,7 +1,6 @@
1
1
  module Fron
2
- # Bevahviors
3
2
  module Behaviors
4
- # Components
3
+ # Behavior for handling routes in applications.
5
4
  module Routes
6
5
  # Register a route
7
6
  #
@@ -36,7 +35,6 @@ module Fron
36
35
  return if @initialized
37
36
  @initialized = true
38
37
  @routes = []
39
- listen
40
38
  end
41
39
 
42
40
  # Listen on events (maily for tests)
@@ -46,13 +44,11 @@ module Fron
46
44
 
47
45
  # Registers routes from the registry
48
46
  #
49
- # @param registry [Array] The routes
50
- def self.route(registry)
51
- registry.each do |item|
52
- path, action = item
53
- fail "There is no method #{action} on #{self}" unless respond_to? action
54
- Routes.register path, action, self
55
- end
47
+ # @param item [Array] The route
48
+ def self.route(item)
49
+ path, action = item[:args]
50
+ raise "There is no method #{action} on #{self}" unless respond_to? action
51
+ Routes.register path, action, self
56
52
  end
57
53
  end
58
54
  end
@@ -0,0 +1,30 @@
1
+ module Fron
2
+ module Behaviors
3
+ # Behavior for hanlding styles on components.
4
+ module Style
5
+ # Runs for included classes
6
+ #
7
+ # @param base [Class] The class
8
+ def self.included(base)
9
+ base.meta_def :ensure_styles! do
10
+ styles.each do |(style, id)|
11
+ Sheet.add_rule tagname, style, id
12
+ end
13
+ end
14
+
15
+ base.meta_def :style do |item|
16
+ styles << [item, SecureRandom.uuid]
17
+ ensure_styles!
18
+ end
19
+
20
+ base.meta_def :keyframes do |name, data|
21
+ Sheet.add_animation name, data
22
+ end
23
+
24
+ base.meta_def :stylesheet do |url|
25
+ Sheet.stylesheet url
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,16 +1,23 @@
1
1
  require 'fron/core/behaviors/components'
2
2
  require 'fron/core/behaviors/events'
3
3
  require 'fron/core/behaviors/routes'
4
+ require 'fron/core/behaviors/style'
5
+ require 'securerandom'
4
6
 
5
7
  module Fron
6
- # Component
8
+ # Base class for components.
7
9
  class Component < DOM::Element
8
10
  class << self
9
11
  # @return [String] The tagname of the component
10
12
  attr_reader :tagname
11
13
 
12
- # @return [Hash] The hash of behaviors
13
- attr_reader :behaviors
14
+ # @return [Array] The registry of behaviors
15
+ attr_reader :registry
16
+
17
+ # @return [Array] The styles for this component
18
+ attr_reader :styles
19
+
20
+ attr_reader :defaults
14
21
 
15
22
  # Creates a new class with the specific tag
16
23
  #
@@ -28,31 +35,30 @@ module Fron
28
35
  # @param behavior [Module] The behavior
29
36
  # @param methods [Array] The methods to register
30
37
  def register(behavior, methods)
31
- @behaviors ||= {}
32
- @behaviors[behavior] = methods
38
+ @registry ||= []
39
+ @styles ||= []
33
40
 
34
41
  methods.each do |name|
35
- instance_variable_set "@#{name}", []
36
42
  meta_def name do |*args, &block|
37
- args << block if block_given?
38
- instance_variable_get("@#{name}") << args
43
+ @registry << { method: behavior.method(name), args: args, block: block, id: SecureRandom.uuid }
39
44
  end
40
45
  end
41
46
  end
42
47
 
48
+ def defaults(data = nil)
49
+ return @defaults unless data
50
+ @defaults ||= {}
51
+ @defaults.merge! data
52
+ end
53
+
43
54
  # Handles inheritance
44
55
  #
45
56
  # @param subclass [Class] The subclass
46
57
  def inherited(subclass)
47
58
  # Copy behaviours
48
- subclass.instance_variable_set '@behaviors', @behaviors.dup
49
-
50
- # Copy registries
51
- @behaviors.values.reduce(&:+).each do |type|
52
- next unless (var = instance_variable_get("@#{type}"))
53
- inst_var = subclass.instance_variable_get("@#{type}") || []
54
- subclass.instance_variable_set("@#{type}", inst_var.concat(var))
55
- end
59
+ subclass.instance_variable_set '@registry', @registry.dup
60
+ subclass.instance_variable_set '@styles', @styles.dup
61
+ subclass.instance_variable_set '@defaults', (@defaults || {}).dup
56
62
  end
57
63
 
58
64
  # Sets the tag name of the component
@@ -61,24 +67,39 @@ module Fron
61
67
  def tag(tag)
62
68
  @tagname = tag
63
69
  end
70
+
71
+ def tagname
72
+ @tagname || name.split('::').join('-').downcase
73
+ end
64
74
  end
65
75
 
66
76
  include Behaviors::Components
67
77
  include Behaviors::Events
78
+ include Behaviors::Style
79
+
80
+ TAGNAME_REGEXP = /^\w([\w\d-]+)*$/
68
81
 
69
82
  # Initalizs the component
70
83
  #
71
84
  # @param tag [String] The tagname
72
- def initialize(tag = nil)
85
+ def initialize(tagname = nil)
73
86
  klass = self.class
74
87
 
75
- super tag || klass.tagname || klass.name.split('::').last
88
+ tag = tagname || klass.tagname
76
89
 
77
- klass.behaviors.each do |mod, methods|
78
- methods.each do |name|
79
- next unless mod.respond_to?(name)
80
- registry = self.class.instance_variable_get("@#{name}")
81
- instance_exec registry, &mod.method(name)
90
+ raise "Invalid tag '#{tag}' for #{self}!" unless tag =~ TAGNAME_REGEXP
91
+
92
+ super tag
93
+
94
+ klass.registry.each do |item|
95
+ instance_exec item, &item[:method].unbind.bind(self)
96
+ end
97
+
98
+ klass.defaults.to_h.each do |key, value|
99
+ if respond_to?("#{key}=")
100
+ send "#{key}=", value
101
+ else
102
+ self[key] = value
82
103
  end
83
104
  end
84
105
  end
@@ -1,7 +1,7 @@
1
1
  # rubocop:disable ModuleFunction
2
2
 
3
3
  module Fron
4
- # Eventable
4
+ # Class for adding events to any Ruby object.
5
5
  module Eventable
6
6
  extend self
7
7
 
@@ -23,10 +23,10 @@ module Fron
23
23
  # @param event [String] The type of the event
24
24
  # @param data = {} [type] The data
25
25
  # @param triggerGlobal [Boolean] Whether or not to trigger a global event
26
- def trigger(event, data = {}, triggerGlobal = true)
26
+ def trigger(event, data = {}, trigger_global = true)
27
27
  return unless @events
28
28
  return unless @events[event]
29
- Eventable.trigger event, data, false if triggerGlobal && self != Fron::Eventable
29
+ Eventable.trigger event, data, false if trigger_global && self != Fron::Eventable
30
30
  @events[event].each do |block|
31
31
  block.call data
32
32
  end
@@ -1,5 +1,5 @@
1
1
  module Fron
2
- # Logger
2
+ # Class for logging messages with a timestamp.
3
3
  class Logger
4
4
  # Sets / gets the log level
5
5
  #
@@ -0,0 +1,140 @@
1
+ module Fron
2
+ # Module for handling component styles
3
+ module Sheet
4
+ class << self
5
+ attr_accessor :additional_styles
6
+
7
+ # Helpers context class
8
+ class Helpers
9
+ end
10
+
11
+ # Creates style tag to store the styles
12
+ #
13
+ # @return [DOM::Element] The style tag
14
+ def style
15
+ return @style if @style
16
+ @style = DOM::Element.new 'style'
17
+ @style >> DOM::Document.head
18
+ @style
19
+ end
20
+
21
+ # Adds a rule for the given tag
22
+ # with the given data
23
+ #
24
+ # @param tag [String] The selector for the tag
25
+ # @param data [Hash] The styles
26
+ def add_rule(tag, data, id)
27
+ @rules ||= {}
28
+ @rules[tag] ||= {}
29
+ @rules[tag][id] ||= data.each_with_object({}) do |(key, value), style|
30
+ if value.is_a? Hash
31
+ handle_rule_raw_hash tag, key, value
32
+ else
33
+ style[key] = value
34
+ end
35
+ end
36
+ end
37
+
38
+ def handle_rule_raw_hash(tag, key, value)
39
+ value['_rule_id'] ||= SecureRandom.uuid
40
+ id = value['_rule_id']
41
+ if key =~ /&/
42
+ add_rule key.gsub(/&/, tag), value, id
43
+ else
44
+ key.split(',').each do |part|
45
+ add_rule "#{tag.strip} #{part.strip}", value, id
46
+ end
47
+ end
48
+ end
49
+
50
+ # Renders the styles
51
+ def render
52
+ [render_stylesheets,
53
+ additional_styles.to_s,
54
+ render_rules].join("\n")
55
+ end
56
+
57
+ def render_rules
58
+ @rules.map { |tag, data|
59
+ body = tag.start_with?('@') ? render_at_block(data) : render_rule(data)
60
+ "#{tag} { #{body} }"
61
+ }.join("\n")
62
+ end
63
+
64
+ def render_stylesheets
65
+ @stylesheets
66
+ .to_h
67
+ .keys
68
+ .map { |url| "@import(#{url});" }
69
+ .join("\n")
70
+ end
71
+
72
+ # Returns the helper for the proc rendering.
73
+ #
74
+ # @return [Helper] The helper
75
+ def helper
76
+ @helper ||= Helpers.new
77
+ end
78
+
79
+ # Elavulates the given block in the helper scope.
80
+ #
81
+ # @param block [Proc] The block
82
+ def helpers(&block)
83
+ Helpers.class_eval(&block)
84
+ end
85
+
86
+ # Renders an at block
87
+ #
88
+ # @param data [Hash] The data
89
+ def render_at_block(data)
90
+ data.map { |key, block| "#{key} { #{render_block(block)} }" }.join('')
91
+ end
92
+
93
+ # Renders a rule with multiple "versions"
94
+ #
95
+ # @param data [Hash] The data
96
+ def render_rule(data)
97
+ render_block data.values.reduce(&:merge).to_h
98
+ end
99
+
100
+ # Renders an block of single key, values
101
+ #
102
+ # @param data [Hash] The data
103
+ def render_block(block)
104
+ block.map do |prop, value|
105
+ render_property prop, value
106
+ end.join('')
107
+ end
108
+
109
+ def render_property(prop, value)
110
+ return if prop == '_rule_id'
111
+ val = value.is_a?(Proc) ? helper.instance_eval(&value) : value
112
+ prop = prop.gsub(/(.)([A-Z])/, '\1-\2').downcase
113
+ "#{prop}: #{val};"
114
+ end
115
+
116
+ def render_style_tag
117
+ style.text = render
118
+ end
119
+
120
+ # Adds an animation with the given data
121
+ #
122
+ # @param name [String] The name
123
+ # @param data [Hash] The data
124
+ def add_animation(name, data)
125
+ @rules ||= {}
126
+ return if @rules["@keyframes #{name}"]
127
+ @rules["@keyframes #{name}"] ||= data
128
+ end
129
+
130
+ # Defines a stylesheet link tag
131
+ #
132
+ # @param url [String] The URL for the stylesheet
133
+ def stylesheet(url)
134
+ @stylesheets ||= {}
135
+ return if @stylesheets[url]
136
+ @stylesheets[url] = true
137
+ end
138
+ end
139
+ end
140
+ end
@@ -7,3 +7,4 @@ require 'fron/core_ext/object'
7
7
  require 'fron/core_ext/nil'
8
8
  require 'fron/core_ext/date'
9
9
  require 'fron/core_ext/array'
10
+ require 'fron/core_ext/time'
@@ -9,4 +9,27 @@ class Array
9
9
  yield self[i], i
10
10
  end
11
11
  end
12
+
13
+ # Sort by array in place.
14
+ #
15
+ # @param block [Proc] The block
16
+ #
17
+ # @return [Array] The array
18
+ def sort_by!
19
+ sort! do |a, b|
20
+ yield(a) <=> yield(b)
21
+ end
22
+ end
23
+
24
+ def _uniq
25
+ return uniq unless block_given?
26
+ data = uniq
27
+ results = []
28
+ data.reject do |item|
29
+ value = yield item
30
+ next true if results.include? value
31
+ results << value
32
+ false
33
+ end
34
+ end
12
35
  end
@@ -9,7 +9,7 @@ class Hash
9
9
  Hash[to_a - other.to_a]
10
10
  end
11
11
 
12
- alias_method :-, :difference
12
+ alias - difference
13
13
 
14
14
  # Converts the hash into an url encoded query string
15
15
  #
@@ -41,11 +41,11 @@ class Hash
41
41
  self_key = self[key]
42
42
  other_key = other[key]
43
43
  next if self_key == other_key
44
- if self_key.respond_to?(:deep_diff) && other_key.respond_to?(:deep_diff)
45
- diff[key] = self_key.deep_diff(other_key)
46
- else
47
- diff[key] = [self_key, other_key]
48
- end
44
+ diff[key] = if self_key.respond_to?(:deep_diff) && other_key.respond_to?(:deep_diff)
45
+ self_key.deep_diff(other_key)
46
+ else
47
+ [self_key, other_key]
48
+ end
49
49
  diff
50
50
  end
51
51
  end
@@ -22,7 +22,7 @@ module Kernel
22
22
  #
23
23
  # @return [String] The user input
24
24
  def prompt(text, value)
25
- `prompt(#{text}, #{value})`
25
+ `prompt(#{text}, #{value}) || Opal.NIL`
26
26
  end
27
27
 
28
28
  # Shows an alert window with the given text
@@ -41,6 +41,10 @@ module Kernel
41
41
  `confirm(#{text})`
42
42
  end
43
43
 
44
+ def open_window(url)
45
+ `window.open(#{url})`
46
+ end
47
+
44
48
  # Clears the timeout with the given ID
45
49
  #
46
50
  # @param id [Numeric] The ID
@@ -54,4 +58,9 @@ module Kernel
54
58
  def logger
55
59
  @logger ||= Fron::Logger.new
56
60
  end
61
+
62
+ # Produces a JavaScript stack trace in the console
63
+ def trace!
64
+ `console.trace()`
65
+ end
57
66
  end
@@ -16,4 +16,11 @@ class Numeric
16
16
  def px
17
17
  "#{round}px"
18
18
  end
19
+
20
+ # Returns the em representation
21
+ #
22
+ # @return [String] The em
23
+ def em
24
+ "#{self}em"
25
+ end
19
26
  end