lissio 0.1.0.beta1

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.
@@ -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