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