fron 0.2.0rc1 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
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