lissio 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
1
+ require 'singleton'
2
+ require 'forwardable'
3
+
4
+ module Lissio
5
+
6
+ class Application < Component
7
+ def self.inherited(klass)
8
+ super
9
+
10
+ klass.include Singleton
11
+
12
+ $document.on :load do
13
+ klass.start
14
+ end
15
+ end
16
+
17
+ def self.expose(what, options = {})
18
+ if what.start_with?(?@)
19
+ name = what[1 .. -1]
20
+
21
+ define_singleton_method name do
22
+ instance.__send__ name
23
+ end
24
+
25
+ attr_reader name
26
+ else
27
+ define_singleton_method what do |*args, &block|
28
+ instance.__send__ what, *args, &block
29
+ end
30
+ end
31
+ end
32
+
33
+ expose :start
34
+ expose :refresh
35
+ expose :navigate
36
+ expose :@router
37
+
38
+ extend Forwardable
39
+ def_delegators :@router, :navigate, :route
40
+
41
+ def initialize
42
+ @router = Lissio::Router.new(fragment: false)
43
+ end
44
+
45
+ def start
46
+ render
47
+
48
+ @router.update
49
+ end
50
+
51
+ def refresh
52
+ @router.update
53
+ end
54
+
55
+ element :body
56
+
57
+ on :click, 'a[href^="/"]' do |e|
58
+ unless e.alt? || e.ctrl? || e.meta? || e.shift?
59
+ e.stop!
60
+
61
+ navigate e.target[:href] || e.target.ancestors('a').first[:href]
62
+ end
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,70 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'forwardable'
12
+
13
+ module Lissio
14
+
15
+ class Collection
16
+ def self.adapter(klass = nil, *args, &block)
17
+ if klass
18
+ @adapter.uninstall if @adapter
19
+
20
+ @adapter = klass.new(self, *args, &block)
21
+ @adapter.install
22
+ else
23
+ @adapter
24
+ end
25
+ end
26
+
27
+ def self.model(klass = nil)
28
+ klass ? @model = klass : @model
29
+ end
30
+
31
+ def self.parse(&block)
32
+ block ? @parse = block : @parse
33
+ end
34
+
35
+ extend Forwardable
36
+ def_delegators :class, :adapter, :model
37
+ def_delegators :@items, :empty?, :length, :[], :to_a
38
+
39
+ def initialize(data = nil, *fetched_with)
40
+ @fetched_with = fetched_with
41
+
42
+ if data
43
+ @items = data.map {|datum|
44
+ next datum if Model === datum
45
+
46
+ if block = self.class.parse
47
+ block.call(datum)
48
+ else
49
+ model.new(datum)
50
+ end
51
+ }
52
+ end
53
+ end
54
+
55
+ include Enumerable
56
+
57
+ def each(&block)
58
+ return enum_for :each unless block
59
+
60
+ @items.each(&block)
61
+
62
+ self
63
+ end
64
+
65
+ def inspect
66
+ "#<#{self.class.name}: #{@items.inspect}>"
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,177 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ module Lissio
12
+
13
+ class Component
14
+ def self.inherited(klass)
15
+ return if self == Component
16
+
17
+ element = @element
18
+ tag = @tag
19
+ events = @events
20
+
21
+ klass.instance_eval {
22
+ @element = element if element
23
+ @tag = tag if tag
24
+ @events = events.clone if events
25
+ }
26
+ end
27
+
28
+ def self.element(name = nil)
29
+ name ? @element = name : @element
30
+ end
31
+
32
+ def self.tag(options = nil)
33
+ options ? @tag = options : @tag
34
+ end
35
+
36
+ def self.events
37
+ @events ||= Hash.new { |h, k| h[k] = [] }
38
+ end
39
+
40
+ def self.on(name, selector = nil, method = nil, &block)
41
+ if block
42
+ events[name] << [selector, block]
43
+
44
+ [name, selector, block]
45
+ elsif method
46
+ events[name] << [selector, method]
47
+
48
+ [name, selector, method]
49
+ else
50
+ events[name] << [nil, method]
51
+
52
+ [name, nil, method]
53
+ end
54
+ end
55
+
56
+ def self.off(id)
57
+ name, selector, block = id
58
+
59
+ events[name].delete([selector, block])
60
+ end
61
+
62
+ def self.render(&block)
63
+ define_method :render do
64
+ instance_exec(&block)
65
+
66
+ super
67
+ end
68
+ end
69
+
70
+ def self.html(string = nil, &block)
71
+ if block
72
+ render {
73
+ if block.arity == 1
74
+ element.inner_dom { |d|
75
+ instance_exec(d, &block)
76
+ }
77
+ else
78
+ element.inner_dom(&block)
79
+ end
80
+ }
81
+ else
82
+ render {
83
+ element.inner_html = string
84
+ }
85
+ end
86
+ end
87
+
88
+ def self.css(content = nil, &block)
89
+ if content || block
90
+ @style.remove if @style
91
+
92
+ @style = CSS(content, &block)
93
+ @style.append_to($document.head)
94
+ else
95
+ CSS::StyleSheet.new(@style)
96
+ end
97
+ end
98
+
99
+ attr_accessor :parent
100
+
101
+ def initialize(parent = nil)
102
+ @parent = parent
103
+ end
104
+
105
+ def tag
106
+ { name: :div }.merge(self.class.tag || {})
107
+ end
108
+
109
+ def element
110
+ return @element if @element
111
+
112
+ scope = parent ? parent.element : $document
113
+ elem = if elem = self.class.element
114
+ scope.at(elem)
115
+ else
116
+ DOM::Element.create tag[:name]
117
+ end
118
+
119
+ unless elem
120
+ raise ArgumentError, 'element not found'
121
+ end
122
+
123
+ elem.add_class(*tag[:class]) if tag[:class]
124
+ elem[:id] = tag[:id] if tag[:id]
125
+
126
+ self.class.events.each {|name, blocks|
127
+ blocks.each {|selector, block|
128
+ if block.is_a? Symbol
129
+ elem.on(name, selector, &method(block))
130
+ else
131
+ elem.on(name, selector) {|*args|
132
+ instance_exec(*args, &block)
133
+ }
134
+ end
135
+ }
136
+ }
137
+
138
+ @element = elem
139
+ end
140
+
141
+ def on(name, selector = nil, method = nil, &block)
142
+ self.class.on(name, selector, method, &block)
143
+
144
+ if @element
145
+ if block
146
+ @element.on(name, selector) {|*args|
147
+ instance_exec(*args, &block)
148
+ }
149
+ elsif method
150
+ @element.on(name, selector, &method(method))
151
+ else
152
+ @element.on(name, &method(method))
153
+ end
154
+ end
155
+
156
+ self
157
+ end
158
+
159
+ # When overriding, remember to call super as last.
160
+ def render(*)
161
+ element.trigger :render, self
162
+ element
163
+ end
164
+
165
+ def remove
166
+ @element.remove if @element
167
+ end
168
+
169
+ alias destroy remove
170
+ end
171
+
172
+
173
+ Browser::DOM::Builder.for Component do |_, item|
174
+ item.render
175
+ end
176
+
177
+ end
@@ -0,0 +1,110 @@
1
+ module Lissio; class Component
2
+
3
+ class Alert < Component
4
+ def self.new!(message, options = {})
5
+ new(message, options.merge({ escape: false }))
6
+ end
7
+
8
+ attr_reader :message, :options
9
+
10
+ def initialize(message, options = {})
11
+ @message = message
12
+ @options = options
13
+ end
14
+
15
+ def render
16
+ if @options[:escape] == false
17
+ element.inner_html = @message
18
+ else
19
+ element << @message
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ tag class: :alert
26
+
27
+ css do
28
+ rule '.alert' do
29
+ border 1.px, :solid, :transparent
30
+
31
+ padding 15.px
32
+
33
+ rule 'a' do
34
+ font weight: :bold
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.customize(*args, &block)
40
+ if args.length == 1
41
+ options = args.first
42
+ else
43
+ name, options = args
44
+ end
45
+
46
+ name ||= "alert-custom-#{rand(10000)}"
47
+ options ||= {}
48
+
49
+ if self == Alert
50
+ inherited = []
51
+ else
52
+ inherited = class_names
53
+ end
54
+
55
+ Class.new(self) {
56
+ define_singleton_method :class_names do
57
+ inherited + [name]
58
+ end
59
+
60
+ tag class: [:alert, name, *inherited]
61
+
62
+ css do
63
+ rule ".alert#{".#{inherited.join('.')}" unless inherited.empty?}.#{name}" do
64
+ instance_exec(&block) if block
65
+
66
+ if value = options[:background] || options[:bg]
67
+ background color: value
68
+ end
69
+
70
+ if value = options[:foreground] || options[:fg]
71
+ color value
72
+ end
73
+
74
+ if value = options[:border]
75
+ border color: value
76
+ end
77
+
78
+ if value = options[:padding]
79
+ padding value
80
+ end
81
+ end
82
+ end
83
+ }
84
+ end
85
+
86
+ Info = customize :info,
87
+ background: '#d9edf7',
88
+ foreground: '#3a87ad',
89
+ border: '#bce8f1'
90
+
91
+ Success = customize :success,
92
+ message: "The operation was successful.",
93
+ background: '#dff0d8',
94
+ foreground: '#468847',
95
+ border: '#d6e9c6'
96
+
97
+ Warning = customize :warning,
98
+ message: "Something might have gone wrong.",
99
+ background: '#fcf8e3',
100
+ foreground: '#c09853',
101
+ border: '#fbeed5'
102
+
103
+ Danger = customize :danger,
104
+ message: "An unexpected error has occurred.",
105
+ background: '#f2dede',
106
+ foreground: '#b94a48',
107
+ border: '#eed3d7'
108
+ end
109
+
110
+ end; end
@@ -0,0 +1,56 @@
1
+ module Lissio; class Component
2
+
3
+ class Container < Component
4
+ class Definer
5
+ def initialize(&block)
6
+ @list = []
7
+
8
+ if block.arity == 0
9
+ instance_exec(&block)
10
+ else
11
+ block.call(self)
12
+ end
13
+ end
14
+
15
+ def render(what)
16
+ @list << what
17
+ end
18
+
19
+ def to_a
20
+ @list
21
+ end
22
+ end
23
+
24
+ def initialize(parent, &block)
25
+ super(parent)
26
+
27
+ if block
28
+ @content = Definer.new(&block).to_a
29
+ else
30
+ @content = []
31
+ end
32
+ end
33
+
34
+ def render(*content, &block)
35
+ content = @content.dup if content.empty?
36
+ content.compact! # FIXME: when it's fixed
37
+
38
+ element.clear
39
+
40
+ content.each {|c|
41
+ if String === c
42
+ element << c
43
+ else
44
+ element << c.render
45
+ end
46
+ }
47
+
48
+ if block
49
+ element << DOM(&block)
50
+ end
51
+
52
+ super
53
+ end
54
+ end
55
+
56
+ end; end