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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +20 -0
- data/.reek +2 -0
- data/.rubocop.yml +14 -11
- data/Gemfile +6 -3
- data/Gemfile.lock +73 -86
- data/Rakefile +11 -15
- data/Readme.md +1 -1
- data/fron.gemspec +2 -2
- data/lib/fron/version.rb +1 -1
- data/opal/fron.rb +2 -0
- data/opal/fron/core.rb +1 -0
- data/opal/fron/core/behaviors/components.rb +18 -10
- data/opal/fron/core/behaviors/events.rb +9 -10
- data/opal/fron/core/behaviors/routes.rb +6 -10
- data/opal/fron/core/behaviors/style.rb +30 -0
- data/opal/fron/core/component.rb +44 -23
- data/opal/fron/core/eventable.rb +3 -3
- data/opal/fron/core/logger.rb +1 -1
- data/opal/fron/core/sheet.rb +140 -0
- data/opal/fron/core_ext.rb +1 -0
- data/opal/fron/core_ext/array.rb +23 -0
- data/opal/fron/core_ext/hash.rb +6 -6
- data/opal/fron/core_ext/kernel.rb +10 -1
- data/opal/fron/core_ext/numeric.rb +7 -0
- data/opal/fron/core_ext/time.rb +6 -0
- data/opal/fron/dom/document.rb +6 -3
- data/opal/fron/dom/element.rb +79 -19
- data/opal/fron/dom/event.rb +5 -1
- data/opal/fron/dom/modules/dimensions.rb +0 -14
- data/opal/fron/dom/modules/element_accessor.rb +25 -0
- data/opal/fron/dom/modules/events.rb +1 -1
- data/opal/fron/dom/node.rb +7 -5
- data/opal/fron/dom/style.rb +0 -2
- data/opal/fron/dom/window.rb +14 -0
- data/opal/fron/event_mock.rb +24 -6
- data/opal/fron/js/scroll_into_view_if_needed.js +27 -0
- data/opal/fron/js/syntetic_event.js +20 -13
- data/opal/fron/request/request.rb +21 -19
- data/opal/fron/request/response.rb +1 -1
- data/opal/fron/storage.rb +2 -0
- data/opal/fron/storage/local_storage.rb +3 -45
- data/opal/fron/storage/session_storage.rb +12 -0
- data/opal/fron/storage/store.rb +54 -0
- data/opal/fron/utils/drag.rb +21 -18
- data/opal/fron/utils/keyboard.rb +14 -12
- data/opal/fron/utils/point.rb +12 -4
- data/opal/fron/utils/render_proc.rb +6 -2
- data/spec/core-ext/array_spec.rb +10 -2
- data/spec/core-ext/numeric_spec.rb +6 -0
- data/spec/core/behaviors/style_spec.rb +51 -0
- data/spec/core/component_inheritance_spec.rb +10 -15
- data/spec/core/component_spec.rb +10 -15
- data/spec/dom/element_spec.rb +12 -1
- data/spec/dom/modules/classlist_spec.rb +8 -9
- data/spec/dom/modules/dimensions_spec.rb +2 -1
- data/spec/dom/modules/events_spec.rb +42 -31
- data/spec/dom/style_spec.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- data/spec/utils/drag_spec.rb +2 -2
- data/spec/utils/keyboard_spec.rb +4 -1
- data/website/application.rb +4 -0
- data/website/config.ru +30 -0
- data/website/examples/content_editable.rb +29 -0
- data/website/examples/converter.rb +49 -0
- data/website/examples/icon_button.rb +20 -0
- data/website/examples/image_paragraph.rb +33 -0
- data/website/examples/my_blue_box.rb +9 -0
- data/website/examples/my_box.rb +9 -0
- data/website/examples/my_button.rb +27 -0
- data/website/examples/my_green_box.rb +14 -0
- data/website/examples/source_reader.rb +32 -0
- data/website/examples/text_area.rb +42 -0
- data/website/pages/components.md.erb +16 -0
- data/website/pages/components/composition.md.erb +9 -0
- data/website/pages/components/events.md.erb +20 -0
- data/website/pages/components/inheritance.md.erb +19 -0
- data/website/pages/components/routes.md.erb +49 -0
- data/website/pages/components/styles.md.erb +38 -0
- data/website/pages/getting-started.md +8 -0
- data/website/pages/home.md +4 -0
- data/website/pages/intro.md +30 -0
- data/website/pages/utilities.md +10 -0
- data/website/pages/utilities/local-storage.md.erb +16 -0
- data/website/pages/utilities/request.md.erb +12 -0
- data/website/setup.rb +162 -0
- data/website/vendor/highlight.js +2 -0
- data/website/vendor/highlight.ruby.js +1 -0
- data/website/vendor/marked.min.js +6 -0
- metadata +43 -7
@@ -1,7 +1,6 @@
|
|
1
1
|
module Fron
|
2
|
-
# Bevahviors
|
3
2
|
module Behaviors
|
4
|
-
#
|
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
|
50
|
-
def self.route(
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
data/opal/fron/core/component.rb
CHANGED
@@ -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
|
-
#
|
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 [
|
13
|
-
attr_reader :
|
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
|
-
@
|
32
|
-
@
|
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
|
-
|
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 '@
|
49
|
-
|
50
|
-
|
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(
|
85
|
+
def initialize(tagname = nil)
|
73
86
|
klass = self.class
|
74
87
|
|
75
|
-
|
88
|
+
tag = tagname || klass.tagname
|
76
89
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
data/opal/fron/core/eventable.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# rubocop:disable ModuleFunction
|
2
2
|
|
3
3
|
module Fron
|
4
|
-
#
|
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 = {},
|
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
|
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
|
data/opal/fron/core/logger.rb
CHANGED
@@ -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
|
data/opal/fron/core_ext.rb
CHANGED
data/opal/fron/core_ext/array.rb
CHANGED
@@ -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
|
data/opal/fron/core_ext/hash.rb
CHANGED
@@ -9,7 +9,7 @@ class Hash
|
|
9
9
|
Hash[to_a - other.to_a]
|
10
10
|
end
|
11
11
|
|
12
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|