glimmer-dsl-web 0.2.6 → 0.2.8
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/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
|