glimmer-dsl-web 0.2.6 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +213 -12
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +30 -18
- data/lib/glimmer/dsl/web/a_expression.rb +17 -0
- data/lib/glimmer/dsl/web/dsl.rb +2 -0
- data/lib/glimmer/dsl/web/element_expression.rb +1 -1
- data/lib/glimmer/dsl/web/style_expression.rb +7 -2
- data/lib/glimmer/web/element_proxy.rb +2 -1
- data/lib/glimmer/web/formatting_element_proxy.rb +2 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_content_data_binding.rb +2 -2
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/models/todo.rb +44 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/presenters/todo_presenter.rb +102 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/edit_todo_input.rb +55 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_form.rb +30 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_input.rb +41 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_filters.rb +123 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_input.rb +30 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list.rb +88 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list_item.rb +158 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_mvc_footer.rb +45 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc.rb +87 -0
- metadata +17 -5
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'glimmer/data_binding/observer'
|
2
|
+
|
3
|
+
require_relative '../models/todo'
|
4
|
+
|
5
|
+
class TodoPresenter
|
6
|
+
FILTER_ROUTE_REGEXP = /\#\/([^\/]*)$/
|
7
|
+
|
8
|
+
attr_accessor :todos, :can_clear_completed, :active_todo_count
|
9
|
+
attr_reader :new_todo, :filter
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@todos = Todo.all.clone
|
13
|
+
@new_todo = Todo.new(task: '')
|
14
|
+
@filter = :all
|
15
|
+
refresh_todo_stats
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_todo(todo = nil)
|
19
|
+
todo ||= new_todo.clone
|
20
|
+
Todo.all.prepend(todo)
|
21
|
+
observers_for_todo_stats[todo.object_id] = todo_stat_observer.observe(todo, :completed) unless observers_for_todo_stats.has_key?(todo.object_id)
|
22
|
+
refresh_todos_with_filter
|
23
|
+
refresh_todo_stats
|
24
|
+
new_todo.task = ''
|
25
|
+
end
|
26
|
+
|
27
|
+
def refresh_todos_with_filter
|
28
|
+
self.todos = Todo.send(filter).clone
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter=(filter)
|
32
|
+
return if filter == @filter
|
33
|
+
@filter = filter
|
34
|
+
refresh_todos_with_filter
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy(todo)
|
38
|
+
delete(todo)
|
39
|
+
refresh_todos_with_filter
|
40
|
+
refresh_todo_stats
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear_completed
|
44
|
+
Todo.completed.each { |todo| delete(todo) }
|
45
|
+
refresh_todos_with_filter
|
46
|
+
refresh_todo_stats
|
47
|
+
end
|
48
|
+
|
49
|
+
def toggle_all_completed
|
50
|
+
target_completed_value = Todo.active.any?
|
51
|
+
todos_to_update = target_completed_value ? Todo.active : Todo.completed
|
52
|
+
todos_to_update.each { |todo| todo.completed = target_completed_value }
|
53
|
+
end
|
54
|
+
|
55
|
+
def setup_filter_routes
|
56
|
+
@filter_router_function = -> (event) { apply_route_filter }
|
57
|
+
$$.addEventListener('popstate', &@filter_router_function)
|
58
|
+
apply_route_filter
|
59
|
+
end
|
60
|
+
|
61
|
+
def apply_route_filter
|
62
|
+
route_filter_match = $$.document.location.href.to_s.match(FILTER_ROUTE_REGEXP)
|
63
|
+
return if route_filter_match.nil?
|
64
|
+
route_filter = route_filter_match[1]
|
65
|
+
route_filter = 'all' if route_filter == ''
|
66
|
+
self.filter = route_filter
|
67
|
+
end
|
68
|
+
|
69
|
+
def unsetup_filter_routes
|
70
|
+
$$.removeEventListener('popstate', &@filter_router_function)
|
71
|
+
@filter_router_function = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def delete(todo)
|
77
|
+
Todo.all.delete(todo)
|
78
|
+
observer_registration = observers_for_todo_stats.delete(todo.object_id)
|
79
|
+
observer_registration&.deregister
|
80
|
+
end
|
81
|
+
|
82
|
+
def observers_for_todo_stats
|
83
|
+
@observers_for_todo_stats = {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def todo_stat_observer
|
87
|
+
@todo_stat_observer ||= Glimmer::DataBinding::Observer.proc { refresh_todo_stats }
|
88
|
+
end
|
89
|
+
|
90
|
+
def refresh_todo_stats
|
91
|
+
refresh_can_clear_completed
|
92
|
+
refresh_active_todo_count
|
93
|
+
end
|
94
|
+
|
95
|
+
def refresh_can_clear_completed
|
96
|
+
self.can_clear_completed = Todo.completed.any?
|
97
|
+
end
|
98
|
+
|
99
|
+
def refresh_active_todo_count
|
100
|
+
self.active_todo_count = Todo.active.count
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'todo_input'
|
2
|
+
|
3
|
+
class EditTodoInput < TodoInput
|
4
|
+
option :presenter
|
5
|
+
option :todo
|
6
|
+
|
7
|
+
markup {
|
8
|
+
input(class: todo_input_class) { |edit_input|
|
9
|
+
style <= [ todo, :editing,
|
10
|
+
on_read: ->(editing) { editing ? '' : 'display: none;' },
|
11
|
+
after_read: ->(_) { edit_input.focus if todo.editing? }
|
12
|
+
]
|
13
|
+
|
14
|
+
value <=> [todo, :task]
|
15
|
+
|
16
|
+
onkeyup do |event|
|
17
|
+
if event.key == 'Enter' || event.keyCode == "\r"
|
18
|
+
todo.save_editing
|
19
|
+
presenter.destroy(todo) if todo.task.strip.empty?
|
20
|
+
elsif event.key == 'Escape' || event.keyCode == 27
|
21
|
+
todo.cancel_editing
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
onblur do |event|
|
26
|
+
todo.save_editing
|
27
|
+
end
|
28
|
+
|
29
|
+
style {
|
30
|
+
todo_input_styles
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
def todo_input_class
|
36
|
+
'edit-todo'
|
37
|
+
end
|
38
|
+
|
39
|
+
def todo_input_styles
|
40
|
+
super
|
41
|
+
|
42
|
+
rule("*:has(> .#{todo_input_class})") {
|
43
|
+
position 'relative'
|
44
|
+
}
|
45
|
+
|
46
|
+
rule(".#{todo_input_class}") {
|
47
|
+
position 'absolute'
|
48
|
+
display 'block'
|
49
|
+
width 'calc(100% - 43px)'
|
50
|
+
padding '12px 16px'
|
51
|
+
margin '0 0 0 43px'
|
52
|
+
top '0'
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'new_todo_input'
|
2
|
+
|
3
|
+
class NewTodoForm
|
4
|
+
include Glimmer::Web::Component
|
5
|
+
|
6
|
+
option :presenter
|
7
|
+
|
8
|
+
markup {
|
9
|
+
header(class: 'header') {
|
10
|
+
h1('todos')
|
11
|
+
|
12
|
+
new_todo_input(presenter: presenter)
|
13
|
+
|
14
|
+
style {
|
15
|
+
rule('.header h1') {
|
16
|
+
color '#b83f45'
|
17
|
+
font_size '80px'
|
18
|
+
font_weight '200'
|
19
|
+
position 'absolute'
|
20
|
+
text_align 'center'
|
21
|
+
_webkit_text_rendering 'optimizeLegibility'
|
22
|
+
_moz_text_rendering 'optimizeLegibility'
|
23
|
+
text_rendering 'optimizeLegibility'
|
24
|
+
top '-140px'
|
25
|
+
width '100%'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'todo_input'
|
2
|
+
|
3
|
+
class NewTodoInput < TodoInput
|
4
|
+
option :presenter
|
5
|
+
|
6
|
+
markup {
|
7
|
+
input(class: todo_input_class, placeholder: "What needs to be done?", autofocus: "") {
|
8
|
+
value <=> [presenter.new_todo, :task]
|
9
|
+
|
10
|
+
onkeyup do |event|
|
11
|
+
presenter.create_todo if event.key == 'Enter' || event.keyCode == "\r"
|
12
|
+
end
|
13
|
+
|
14
|
+
style {
|
15
|
+
todo_input_styles
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
def todo_input_class
|
21
|
+
'new-todo'
|
22
|
+
end
|
23
|
+
|
24
|
+
def todo_input_styles
|
25
|
+
super
|
26
|
+
|
27
|
+
rule(".#{todo_input_class}") {
|
28
|
+
padding '16px 16px 16px 60px'
|
29
|
+
height '65px'
|
30
|
+
border 'none'
|
31
|
+
background 'rgba(0, 0, 0, 0.003)'
|
32
|
+
box_shadow 'inset 0 -2px 1px rgba(0,0,0,0.03)'
|
33
|
+
}
|
34
|
+
|
35
|
+
rule(".#{todo_input_class}::placeholder") {
|
36
|
+
font_style 'italic'
|
37
|
+
font_weight '400'
|
38
|
+
color 'rgba(0, 0, 0, 0.4)'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
class TodoFilters
|
2
|
+
include Glimmer::Web::Component
|
3
|
+
|
4
|
+
option :presenter
|
5
|
+
|
6
|
+
markup {
|
7
|
+
footer(class: 'todo-filters') {
|
8
|
+
style <= [ Todo, :all,
|
9
|
+
on_read: ->(todos) { todos.empty? ? 'display: none;' : '' }
|
10
|
+
]
|
11
|
+
|
12
|
+
span(class: 'todo-count') {
|
13
|
+
span('.strong') {
|
14
|
+
inner_text <= [presenter, :active_todo_count]
|
15
|
+
}
|
16
|
+
span {
|
17
|
+
" items left"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
ul(class: 'filters') {
|
22
|
+
Todo::FILTERS.each do |filter|
|
23
|
+
li {
|
24
|
+
a(filter.to_s.capitalize, href: "#/#{filter unless filter == :all}") {
|
25
|
+
class_name <= [ presenter, :filter,
|
26
|
+
on_read: -> (presenter_filter) { presenter_filter == filter ? 'selected' : '' }
|
27
|
+
]
|
28
|
+
|
29
|
+
onclick do |event|
|
30
|
+
presenter.filter = filter
|
31
|
+
end
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
button('Clear completed', class: 'clear-completed') {
|
38
|
+
style <= [ presenter, :can_clear_completed,
|
39
|
+
on_read: -> (can_clear_completed) { can_clear_completed ? '' : 'display: none;' },
|
40
|
+
]
|
41
|
+
|
42
|
+
onclick do |event|
|
43
|
+
presenter.clear_completed
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
style {
|
48
|
+
rule('.todo-filters') {
|
49
|
+
border_top '1px solid #e6e6e6'
|
50
|
+
font_size '15px'
|
51
|
+
height '20px'
|
52
|
+
padding '10px 15px'
|
53
|
+
text_align 'center'
|
54
|
+
}
|
55
|
+
|
56
|
+
rule('.todo-filters:before') {
|
57
|
+
bottom '0'
|
58
|
+
box_shadow '0 1px 1px rgba(0,0,0,.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0,0,0,.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0,0,0,.2)'
|
59
|
+
content '""'
|
60
|
+
height '50px'
|
61
|
+
left '0'
|
62
|
+
overflow 'hidden'
|
63
|
+
position 'absolute'
|
64
|
+
right '0'
|
65
|
+
}
|
66
|
+
|
67
|
+
rule('.todo-count') {
|
68
|
+
float 'left'
|
69
|
+
text_align 'left'
|
70
|
+
}
|
71
|
+
|
72
|
+
rule('.todo-count .strong') {
|
73
|
+
font_weight '300'
|
74
|
+
}
|
75
|
+
|
76
|
+
rule('.filters') {
|
77
|
+
left '0'
|
78
|
+
list_style 'none'
|
79
|
+
margin '0'
|
80
|
+
padding '0'
|
81
|
+
position 'absolute'
|
82
|
+
right '0'
|
83
|
+
}
|
84
|
+
|
85
|
+
rule('.filters li') {
|
86
|
+
display 'inline'
|
87
|
+
}
|
88
|
+
|
89
|
+
rule('.filters li a') {
|
90
|
+
border '1px solid transparent'
|
91
|
+
border_radius '3px'
|
92
|
+
color 'inherit'
|
93
|
+
margin '3px'
|
94
|
+
padding '3px 7px'
|
95
|
+
text_decoration 'none'
|
96
|
+
cursor 'pointer'
|
97
|
+
}
|
98
|
+
|
99
|
+
rule('.filters li a.selected') {
|
100
|
+
border_color '#ce4646'
|
101
|
+
}
|
102
|
+
|
103
|
+
rule('.clear-completed, html .clear-completed:active') {
|
104
|
+
cursor 'pointer'
|
105
|
+
float 'right'
|
106
|
+
line_height '19px'
|
107
|
+
position 'relative'
|
108
|
+
text_decoration 'none'
|
109
|
+
}
|
110
|
+
|
111
|
+
media('(max-width: 430px)') {
|
112
|
+
rule('.todo-filters') {
|
113
|
+
height '50px'
|
114
|
+
}
|
115
|
+
|
116
|
+
rule('.filters') {
|
117
|
+
bottom '10px'
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Superclass for NewTodoInput and EditTodoInput with common styles
|
2
|
+
class TodoInput
|
3
|
+
include Glimmer::Web::Component
|
4
|
+
|
5
|
+
def todo_input_class
|
6
|
+
'todo-input'
|
7
|
+
end
|
8
|
+
|
9
|
+
def todo_input_styles
|
10
|
+
rule(".#{todo_input_class}") {
|
11
|
+
position 'relative'
|
12
|
+
margin '0'
|
13
|
+
width '100%'
|
14
|
+
font_size '24px'
|
15
|
+
font_family 'inherit'
|
16
|
+
font_weight 'inherit'
|
17
|
+
line_height '1.4em'
|
18
|
+
color 'inherit'
|
19
|
+
padding '6px'
|
20
|
+
border '1px solid #999'
|
21
|
+
box_shadow 'inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2)'
|
22
|
+
box_sizing 'border-box'
|
23
|
+
_webkit_font_smoothing 'antialiased'
|
24
|
+
}
|
25
|
+
|
26
|
+
rule(".#{todo_input_class}::selection") {
|
27
|
+
background 'red'
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative 'todo_list_item'
|
2
|
+
|
3
|
+
class TodoList
|
4
|
+
include Glimmer::Web::Component
|
5
|
+
|
6
|
+
option :presenter
|
7
|
+
|
8
|
+
markup {
|
9
|
+
main(class: 'main') {
|
10
|
+
style <= [ Todo, :all,
|
11
|
+
on_read: ->(todos) { todos.empty? ? 'display: none;' : '' }
|
12
|
+
]
|
13
|
+
|
14
|
+
div(class: 'toggle-all-container') {
|
15
|
+
input(class: 'toggle-all', type: 'checkbox')
|
16
|
+
|
17
|
+
label('Mark all as complete', class: 'toggle-all-label', for: 'toggle-all') {
|
18
|
+
onclick do |event|
|
19
|
+
presenter.toggle_all_completed
|
20
|
+
end
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
ul(class: 'todo-list') {
|
25
|
+
content(presenter, :todos) {
|
26
|
+
presenter.todos.each do |todo|
|
27
|
+
todo_list_item(presenter:, todo:)
|
28
|
+
end
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
style {
|
33
|
+
todo_list_styles
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
def todo_list_styles
|
39
|
+
rule('.main') {
|
40
|
+
border_top '1px solid #e6e6e6'
|
41
|
+
position 'relative'
|
42
|
+
z_index '2'
|
43
|
+
}
|
44
|
+
|
45
|
+
rule('.toggle-all') {
|
46
|
+
border 'none'
|
47
|
+
bottom '100%'
|
48
|
+
height '1px'
|
49
|
+
opacity '0'
|
50
|
+
position 'absolute'
|
51
|
+
right '100%'
|
52
|
+
width '1px'
|
53
|
+
}
|
54
|
+
|
55
|
+
rule('.toggle-all+label') {
|
56
|
+
align_items 'center'
|
57
|
+
display 'flex'
|
58
|
+
font_size '0'
|
59
|
+
height '65px'
|
60
|
+
justify_content 'center'
|
61
|
+
left '0'
|
62
|
+
position 'absolute'
|
63
|
+
top '-65px'
|
64
|
+
width '45px'
|
65
|
+
}
|
66
|
+
|
67
|
+
rule('.toggle-all+label:before') {
|
68
|
+
color '#949494'
|
69
|
+
content '"❯"'
|
70
|
+
display 'inline-block'
|
71
|
+
font_size '22px'
|
72
|
+
padding '10px 27px'
|
73
|
+
_webkit_transform 'rotate(90deg)'
|
74
|
+
transform 'rotate(90deg)'
|
75
|
+
}
|
76
|
+
|
77
|
+
rule('.toggle-all:focus+label, .toggle:focus+label, :focus') {
|
78
|
+
box_shadow '0 0 2px 2px #cf7d7d'
|
79
|
+
outline '0'
|
80
|
+
}
|
81
|
+
|
82
|
+
rule('.todo-list') {
|
83
|
+
list_style 'none'
|
84
|
+
margin '0'
|
85
|
+
padding '0'
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require_relative 'edit_todo_input'
|
2
|
+
|
3
|
+
class TodoListItem
|
4
|
+
include Glimmer::Web::Component
|
5
|
+
|
6
|
+
option :presenter
|
7
|
+
option :todo
|
8
|
+
|
9
|
+
markup {
|
10
|
+
li {
|
11
|
+
class_name <= [ todo, :completed,
|
12
|
+
on_read: -> (completed) { li_class_name(todo) }
|
13
|
+
]
|
14
|
+
class_name <= [ todo, :editing,
|
15
|
+
on_read: -> (editing) { li_class_name(todo) }
|
16
|
+
]
|
17
|
+
|
18
|
+
div(class: 'view') {
|
19
|
+
input(class: 'toggle', type: 'checkbox') {
|
20
|
+
checked <=> [ todo, :completed,
|
21
|
+
after_write: -> (_) { presenter.refresh_todos_with_filter if presenter.filter != :all }
|
22
|
+
]
|
23
|
+
}
|
24
|
+
|
25
|
+
label {
|
26
|
+
inner_html <= [todo, :task]
|
27
|
+
|
28
|
+
ondblclick do |event|
|
29
|
+
todo.start_editing
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
33
|
+
button(class: 'destroy') {
|
34
|
+
onclick do |event|
|
35
|
+
presenter.destroy(todo)
|
36
|
+
end
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
edit_todo_input(presenter:, todo:)
|
41
|
+
|
42
|
+
if todo == presenter.todos.first
|
43
|
+
style {
|
44
|
+
todo_list_item_styles
|
45
|
+
}
|
46
|
+
end
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
def li_class_name(todo)
|
51
|
+
classes = []
|
52
|
+
classes << 'completed' if todo.completed?
|
53
|
+
classes << 'editing' if todo.editing?
|
54
|
+
classes.join(' ')
|
55
|
+
end
|
56
|
+
|
57
|
+
def todo_list_item_styles
|
58
|
+
rule('.todo-list li.completed label') {
|
59
|
+
color '#949494'
|
60
|
+
text_decoration 'line-through'
|
61
|
+
}
|
62
|
+
|
63
|
+
rule('.todo-list li') {
|
64
|
+
border_bottom '1px solid #ededed'
|
65
|
+
font_size '24px'
|
66
|
+
position 'relative'
|
67
|
+
}
|
68
|
+
|
69
|
+
rule('.todo-list li .toggle') {
|
70
|
+
_webkit_appearance 'none'
|
71
|
+
appearance 'none'
|
72
|
+
border 'none'
|
73
|
+
bottom '0'
|
74
|
+
height 'auto'
|
75
|
+
margin 'auto 0'
|
76
|
+
opacity '0'
|
77
|
+
position 'absolute'
|
78
|
+
text_align 'center'
|
79
|
+
top '0'
|
80
|
+
width '40px'
|
81
|
+
}
|
82
|
+
|
83
|
+
rule('.todo-list li label') {
|
84
|
+
color '#484848'
|
85
|
+
display 'block'
|
86
|
+
font_weight '400'
|
87
|
+
line_height '1.2'
|
88
|
+
min_height '40px'
|
89
|
+
padding '15px 15px 15px 60px'
|
90
|
+
transition 'color .4s'
|
91
|
+
word_break 'break-all'
|
92
|
+
}
|
93
|
+
|
94
|
+
rule('.todo-list li .toggle+label') {
|
95
|
+
background_image 'url(data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E)'
|
96
|
+
background_position '0'
|
97
|
+
background_repeat 'no-repeat'
|
98
|
+
}
|
99
|
+
|
100
|
+
rule('.todo-list li.completed label') {
|
101
|
+
color '#949494'
|
102
|
+
text_decoration 'line-through'
|
103
|
+
}
|
104
|
+
|
105
|
+
rule('.todo-list li .toggle:checked+label') {
|
106
|
+
background_image 'url(data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E)'
|
107
|
+
}
|
108
|
+
|
109
|
+
rule('.todo-list li.editing') {
|
110
|
+
border_bottom 'none'
|
111
|
+
padding '0'
|
112
|
+
}
|
113
|
+
|
114
|
+
rule('.todo-list li.editing input[type=checkbox], .todo-list li.editing label') {
|
115
|
+
opacity '0'
|
116
|
+
}
|
117
|
+
|
118
|
+
rule('.todo-list li .destroy') {
|
119
|
+
bottom '0'
|
120
|
+
color '#949494'
|
121
|
+
display 'none'
|
122
|
+
font_size '30px'
|
123
|
+
height '40px'
|
124
|
+
margin 'auto 0'
|
125
|
+
position 'absolute'
|
126
|
+
right '10px'
|
127
|
+
top '0'
|
128
|
+
transition 'color .2s ease-out'
|
129
|
+
width '40px'
|
130
|
+
}
|
131
|
+
|
132
|
+
rule('.todo-list li:focus .destroy, .todo-list li:hover .destroy') {
|
133
|
+
display 'block'
|
134
|
+
}
|
135
|
+
|
136
|
+
rule('.todo-list li .destroy:focus, .todo-list li .destroy:hover') {
|
137
|
+
color '#c18585'
|
138
|
+
}
|
139
|
+
|
140
|
+
rule('.todo-list li .destroy:after') {
|
141
|
+
content '"×"'
|
142
|
+
display 'block'
|
143
|
+
height '100%'
|
144
|
+
line_height '1.1'
|
145
|
+
}
|
146
|
+
|
147
|
+
media ('screen and (-webkit-min-device-pixel-ratio: 0)') {
|
148
|
+
rule('.todo-list li .toggle, .toggle-all') {
|
149
|
+
background 'none'
|
150
|
+
}
|
151
|
+
|
152
|
+
rule('.todo-list li .toggle') {
|
153
|
+
height '40px'
|
154
|
+
}
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class TodoMvcFooter
|
2
|
+
include Glimmer::Web::Component
|
3
|
+
|
4
|
+
markup {
|
5
|
+
footer(class: 'info') {
|
6
|
+
p {
|
7
|
+
"Double-click to edit a todo"
|
8
|
+
}
|
9
|
+
p {
|
10
|
+
"Created by #{a('Andy Maleh', href: 'https://github.com/AndyObtiva')}"
|
11
|
+
}
|
12
|
+
p {
|
13
|
+
"Part of #{a('TodoMVC', href: 'http://todomvc.com')}"
|
14
|
+
}
|
15
|
+
|
16
|
+
style {
|
17
|
+
todo_mvc_styles
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
def todo_mvc_styles
|
23
|
+
rule('footer.info') {
|
24
|
+
margin '65px auto 0'
|
25
|
+
color '#4d4d4d'
|
26
|
+
font_size '11px'
|
27
|
+
text_shadow '0 1px 0 rgba(255, 255, 255, 0.5)'
|
28
|
+
text_align 'center'
|
29
|
+
}
|
30
|
+
|
31
|
+
rule('footer.info p') {
|
32
|
+
line_height '1'
|
33
|
+
}
|
34
|
+
|
35
|
+
rule('footer.info a') {
|
36
|
+
color 'inherit'
|
37
|
+
text_decoration 'none'
|
38
|
+
font_weight '400'
|
39
|
+
}
|
40
|
+
|
41
|
+
rule('footer.info a:hover') {
|
42
|
+
text_decoration 'underline'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|