glimmer-dsl-web 0.3.1 → 0.4.0
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 +19 -2
- data/README.md +71 -8
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +7 -5
- data/lib/glimmer/data_binding/element_binding.rb +4 -4
- data/lib/glimmer/dsl/web/component_expression.rb +1 -1
- data/lib/glimmer/dsl/web/dsl.rb +5 -5
- data/lib/glimmer/dsl/web/formatting_element_expression.rb +11 -0
- data/lib/glimmer/web/component/component_style_container.rb +13 -0
- data/lib/glimmer/web/component.rb +148 -54
- data/lib/glimmer/web/element_proxy.rb +25 -9
- data/lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb +2 -2
- data/lib/glimmer-dsl-web/samples/hello/hello_style.rb +178 -0
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/edit_todo_input.rb +9 -17
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_form.rb +15 -15
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/new_todo_input.rb +7 -15
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_filters.rb +73 -73
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_input.rb +27 -24
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list.rb +3 -9
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_list_item.rb +8 -4
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc/views/todo_mvc_footer.rb +2 -6
- data/lib/glimmer-dsl-web/samples/regular/todo_mvc.rb +2 -6
- metadata +8 -6
@@ -123,13 +123,18 @@ module Glimmer
|
|
123
123
|
REGEX_FORMAT_DATE = /^\d{4}-\d{2}-\d{2}$/
|
124
124
|
REGEX_FORMAT_TIME = /^\d{2}:\d{2}$/
|
125
125
|
|
126
|
-
attr_reader :keyword, :parent, :parent_component, :args, :options, :children, :enabled, :foreground, :background, :removed
|
126
|
+
attr_reader :keyword, :parent, :parent_component, :component, :args, :options, :children, :enabled, :foreground, :background, :removed, :rendered
|
127
127
|
alias rendered? rendered
|
128
|
+
alias removed? removed
|
128
129
|
|
129
130
|
def initialize(keyword, parent, args, block)
|
130
131
|
@keyword = keyword
|
131
132
|
@parent = parent.is_a?(Glimmer::Web::Component) ? parent.markup_root : parent
|
132
133
|
@parent_component = parent if parent.is_a?(Glimmer::Web::Component)
|
134
|
+
if Component.interpretation_stack.last&.markup_root.nil?
|
135
|
+
@component = Component.interpretation_stack.last
|
136
|
+
@component&.instance_variable_set("@markup_root", self)
|
137
|
+
end
|
133
138
|
@options = args.last.is_a?(Hash) ? args.last.symbolize_keys : {}
|
134
139
|
if parent.nil?
|
135
140
|
options[:parent] ||= Component.interpretation_stack.last&.options&.[](:parent)
|
@@ -164,7 +169,7 @@ module Glimmer
|
|
164
169
|
|
165
170
|
# Executes at the closing of a parent widget curly braces after all children/properties have been added/set
|
166
171
|
def post_add_content
|
167
|
-
render if bulk_render? && @parent.nil?
|
172
|
+
render if bulk_render? && @parent.nil? && !rendered?
|
168
173
|
end
|
169
174
|
|
170
175
|
def css_classes
|
@@ -172,6 +177,7 @@ module Glimmer
|
|
172
177
|
end
|
173
178
|
|
174
179
|
def remove
|
180
|
+
return if @removed
|
175
181
|
on_remove_listeners = listeners_for('on_remove').dup
|
176
182
|
if rendered?
|
177
183
|
@children.dup.each do |child|
|
@@ -181,6 +187,10 @@ module Glimmer
|
|
181
187
|
dom_element.remove
|
182
188
|
end
|
183
189
|
parent&.post_remove_child(self)
|
190
|
+
if component
|
191
|
+
Glimmer::Web::Component.remove_component(component)
|
192
|
+
component.remove_style_block
|
193
|
+
end
|
184
194
|
@removed = true
|
185
195
|
on_remove_listeners.each do |listener|
|
186
196
|
listener.original_event_listener.call(EventProxy.new(listener: listener))
|
@@ -362,7 +372,12 @@ module Glimmer
|
|
362
372
|
end
|
363
373
|
|
364
374
|
def html_options
|
365
|
-
|
375
|
+
framework_css_classes = [name, element_id]
|
376
|
+
if component
|
377
|
+
framework_css_classes.prepend(component.class.component_element_class)
|
378
|
+
framework_css_classes.prepend(component.class.component_shortcut_element_class) if component.class.component_shortcut_element_class != component.class.component_element_class
|
379
|
+
end
|
380
|
+
body_class = (framework_css_classes + css_classes.to_a).join(' ')
|
366
381
|
html_options = options.dup
|
367
382
|
GLIMMER_ATTRIBUTES.each do |attribute|
|
368
383
|
next unless html_options.include?(attribute)
|
@@ -413,7 +428,7 @@ module Glimmer
|
|
413
428
|
if rendered?
|
414
429
|
dom_element.add_class(css_class)
|
415
430
|
else
|
416
|
-
enqueue_post_render_method_call('
|
431
|
+
enqueue_post_render_method_call('add_css_class', css_class)
|
417
432
|
end
|
418
433
|
end
|
419
434
|
|
@@ -425,7 +440,7 @@ module Glimmer
|
|
425
440
|
if rendered?
|
426
441
|
dom_element.remove_class(css_class)
|
427
442
|
else
|
428
|
-
enqueue_post_render_method_call('
|
443
|
+
enqueue_post_render_method_call('remove_css_class', css_class)
|
429
444
|
end
|
430
445
|
end
|
431
446
|
|
@@ -539,8 +554,8 @@ module Glimmer
|
|
539
554
|
end
|
540
555
|
|
541
556
|
def data_bind(property, model_binding)
|
542
|
-
|
543
|
-
element_binding_parameters = [self, property,
|
557
|
+
element_binding_read_translator = value_converters_for_input_type(type)&.[](:model_to_view)
|
558
|
+
element_binding_parameters = [self, property, element_binding_read_translator]
|
544
559
|
element_binding = DataBinding::ElementBinding.new(*element_binding_parameters)
|
545
560
|
#TODO make this options observer dependent and all similar observers in element specific data binding handlers
|
546
561
|
element_binding.observe(model_binding)
|
@@ -552,7 +567,8 @@ module Glimmer
|
|
552
567
|
if listener_keyword
|
553
568
|
data_binding_read_listener = lambda do |event|
|
554
569
|
view_property_value = send(property)
|
555
|
-
|
570
|
+
element_binding_write_translator = value_converters_for_input_type(type)&.[](:view_to_model)
|
571
|
+
converted_view_property_value = element_binding_write_translator&.call(view_property_value, model_binding.evaluate_property) || view_property_value
|
556
572
|
model_binding.call(converted_view_property_value)
|
557
573
|
end
|
558
574
|
handle_observation_request(listener_keyword, data_binding_read_listener)
|
@@ -722,7 +738,7 @@ module Glimmer
|
|
722
738
|
end
|
723
739
|
|
724
740
|
def value_converters_for_input_type(input_type)
|
725
|
-
input_value_converters[input_type]
|
741
|
+
input_value_converters[input_type]
|
726
742
|
end
|
727
743
|
|
728
744
|
def input_value_converters
|
@@ -29,7 +29,7 @@ class NumberHolder
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
class
|
32
|
+
class HelloObserverDataBinding
|
33
33
|
include Glimmer::Web::Component
|
34
34
|
|
35
35
|
before_render do
|
@@ -53,5 +53,5 @@ class HelloObserver
|
|
53
53
|
end
|
54
54
|
|
55
55
|
Document.ready? do
|
56
|
-
|
56
|
+
HelloObserverDataBinding.render
|
57
57
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# Copyright (c) 2023-2024 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'glimmer-dsl-web'
|
23
|
+
|
24
|
+
class ButtonModel
|
25
|
+
WIDTH_MIN = 160
|
26
|
+
WIDTH_MAX = 960
|
27
|
+
HEIGHT_MIN = 100
|
28
|
+
HEIGHT_MAX = 600
|
29
|
+
FONT_SIZE_MIN = 40
|
30
|
+
FONT_SIZE_MAX = 200
|
31
|
+
|
32
|
+
attr_accessor :text, :pushed, :width, :height, :font_size
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@text = 'Push'
|
36
|
+
@width = WIDTH_MIN
|
37
|
+
@height = HEIGHT_MIN
|
38
|
+
@font_size = FONT_SIZE_MIN
|
39
|
+
end
|
40
|
+
|
41
|
+
def push
|
42
|
+
self.pushed = !pushed
|
43
|
+
end
|
44
|
+
|
45
|
+
def text
|
46
|
+
pushed ? 'Pull' : 'Push'
|
47
|
+
end
|
48
|
+
|
49
|
+
def width=(value)
|
50
|
+
@width = value
|
51
|
+
self.font_size = @width/4 if @font_size > @width/4
|
52
|
+
end
|
53
|
+
|
54
|
+
def height=(value)
|
55
|
+
@height = value
|
56
|
+
self.font_size = @height/2.5 if @font_size > @height/2.5
|
57
|
+
end
|
58
|
+
|
59
|
+
def font_size=(value)
|
60
|
+
@font_size = value
|
61
|
+
self.width = @font_size*4 if @height < @font_size*4
|
62
|
+
self.height = @font_size*2.5 if @height < @font_size*2.5
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class StyledButton
|
67
|
+
include Glimmer::Web::Component
|
68
|
+
|
69
|
+
option :button_model
|
70
|
+
|
71
|
+
markup {
|
72
|
+
button {
|
73
|
+
inner_text <= [button_model, :text, computed_by: :pushed]
|
74
|
+
|
75
|
+
class_name <= [button_model, :pushed,
|
76
|
+
on_read: ->(pushed) { pushed ? 'pushed' : 'pulled' }
|
77
|
+
]
|
78
|
+
|
79
|
+
style <= [ button_model, :width,
|
80
|
+
on_read: method(:button_style_value) # convert value on read before storing in style
|
81
|
+
]
|
82
|
+
|
83
|
+
style <= [ button_model, :height,
|
84
|
+
on_read: method(:button_style_value) # convert value on read before storing in style
|
85
|
+
]
|
86
|
+
|
87
|
+
style <= [ button_model, :font_size,
|
88
|
+
on_read: method(:button_style_value) # convert value on read before storing in style
|
89
|
+
]
|
90
|
+
|
91
|
+
onclick do
|
92
|
+
button_model.push
|
93
|
+
end
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
style {'
|
98
|
+
button {
|
99
|
+
font-family: Courrier New, Courrier;
|
100
|
+
border-radius: 5px;
|
101
|
+
border-width: 17px;
|
102
|
+
border-color: #ACC7D5;
|
103
|
+
background-color: #ADD8E6;
|
104
|
+
margin: 5px;
|
105
|
+
}
|
106
|
+
|
107
|
+
button.pulled {
|
108
|
+
border-style: outset;
|
109
|
+
}
|
110
|
+
|
111
|
+
button.pushed {
|
112
|
+
border-style: inset;
|
113
|
+
}
|
114
|
+
'}
|
115
|
+
|
116
|
+
def button_style_value
|
117
|
+
"
|
118
|
+
width: #{button_model.width}px;
|
119
|
+
height: #{button_model.height}px;
|
120
|
+
font-size: #{button_model.font_size}px;
|
121
|
+
"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class StyledButtonRangeInput
|
126
|
+
include Glimmer::Web::Component
|
127
|
+
|
128
|
+
option :button_model
|
129
|
+
option :property
|
130
|
+
option :property_min
|
131
|
+
option :property_max
|
132
|
+
|
133
|
+
markup {
|
134
|
+
input(type: 'range', min: property_min, max: property_max) {
|
135
|
+
value <=> [button_model, property]
|
136
|
+
}
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
class HelloStyle
|
141
|
+
include Glimmer::Web::Component
|
142
|
+
|
143
|
+
before_render do
|
144
|
+
@button_model = ButtonModel.new
|
145
|
+
end
|
146
|
+
|
147
|
+
markup {
|
148
|
+
div(class: 'hello-style') {
|
149
|
+
div(class: 'form-row') {
|
150
|
+
label('Styled Button Width:', for: 'styled-button-width-input')
|
151
|
+
styled_button_range_input(button_model: @button_model, property: :width, property_min: ButtonModel::WIDTH_MIN, property_max: ButtonModel::WIDTH_MAX, id: 'styled-button-width-input')
|
152
|
+
}
|
153
|
+
div(class: 'form-row') {
|
154
|
+
label('Styled Button Height:', for: 'styled-button-height-input')
|
155
|
+
styled_button_range_input(button_model: @button_model, property: :height, property_min: ButtonModel::HEIGHT_MIN, property_max: ButtonModel::HEIGHT_MAX, id: 'styled-button-height-input')
|
156
|
+
}
|
157
|
+
div(class: 'form-row') {
|
158
|
+
label('Styled Button Font Size:', for: 'styled-button-font-size-input')
|
159
|
+
styled_button_range_input(button_model: @button_model, property: :font_size, property_min: ButtonModel::FONT_SIZE_MIN, property_max: ButtonModel::FONT_SIZE_MAX, id: 'styled-button-font-size-input')
|
160
|
+
}
|
161
|
+
styled_button(button_model: @button_model)
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
style {'
|
166
|
+
.hello-style {
|
167
|
+
padding: 20px;
|
168
|
+
}
|
169
|
+
|
170
|
+
.hello-style .form-row {
|
171
|
+
margin: 10px 0;
|
172
|
+
}
|
173
|
+
'}
|
174
|
+
end
|
175
|
+
|
176
|
+
Document.ready? do
|
177
|
+
HelloStyle.render
|
178
|
+
end
|
@@ -4,11 +4,11 @@ class EditTodoInput < TodoInput
|
|
4
4
|
option :presenter
|
5
5
|
option :todo
|
6
6
|
|
7
|
-
markup {
|
8
|
-
input
|
7
|
+
markup { # evaluated against instance as a smart default convention
|
8
|
+
input { |edit_input|
|
9
9
|
style <= [ todo, :editing,
|
10
10
|
on_read: ->(editing) { editing ? '' : 'display: none;' },
|
11
|
-
after_read: ->
|
11
|
+
after_read: -> { edit_input.focus if todo.editing? }
|
12
12
|
]
|
13
13
|
|
14
14
|
value <=> [todo, :task]
|
@@ -25,25 +25,17 @@ class EditTodoInput < TodoInput
|
|
25
25
|
onblur do |event|
|
26
26
|
todo.save_editing
|
27
27
|
end
|
28
|
-
|
29
|
-
style {
|
30
|
-
todo_input_styles
|
31
|
-
}
|
32
28
|
}
|
33
29
|
}
|
34
|
-
|
35
|
-
def todo_input_class
|
36
|
-
'edit-todo'
|
37
|
-
end
|
38
|
-
|
39
|
-
def todo_input_styles
|
40
|
-
super
|
41
30
|
|
42
|
-
|
31
|
+
style { # evaluated against class as a smart default convention (common to all instances)
|
32
|
+
todo_input_styles
|
33
|
+
|
34
|
+
rule("*:has(> .#{component_element_class})") {
|
43
35
|
position 'relative'
|
44
36
|
}
|
45
37
|
|
46
|
-
rule(".#{
|
38
|
+
rule(".#{component_element_class}") {
|
47
39
|
position 'absolute'
|
48
40
|
display 'block'
|
49
41
|
width 'calc(100% - 43px)'
|
@@ -51,5 +43,5 @@ class EditTodoInput < TodoInput
|
|
51
43
|
margin '0 0 0 43px'
|
52
44
|
top '0'
|
53
45
|
}
|
54
|
-
|
46
|
+
}
|
55
47
|
end
|
@@ -10,21 +10,21 @@ class NewTodoForm
|
|
10
10
|
h1('todos')
|
11
11
|
|
12
12
|
new_todo_input(presenter: presenter)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
style {
|
17
|
+
rule('.header h1') {
|
18
|
+
color '#b83f45'
|
19
|
+
font_size '80px'
|
20
|
+
font_weight '200'
|
21
|
+
position 'absolute'
|
22
|
+
text_align 'center'
|
23
|
+
_webkit_text_rendering 'optimizeLegibility'
|
24
|
+
_moz_text_rendering 'optimizeLegibility'
|
25
|
+
text_rendering 'optimizeLegibility'
|
26
|
+
top '-140px'
|
27
|
+
width '100%'
|
28
28
|
}
|
29
29
|
}
|
30
30
|
end
|
@@ -3,28 +3,20 @@ require_relative 'todo_input'
|
|
3
3
|
class NewTodoInput < TodoInput
|
4
4
|
option :presenter
|
5
5
|
|
6
|
-
markup {
|
7
|
-
input(
|
6
|
+
markup { # evaluated against instance as a smart default convention
|
7
|
+
input(placeholder: "What needs to be done?", autofocus: "") {
|
8
8
|
value <=> [presenter.new_todo, :task]
|
9
9
|
|
10
10
|
onkeyup do |event|
|
11
11
|
presenter.create_todo if event.key == 'Enter' || event.keyCode == "\r"
|
12
12
|
end
|
13
|
-
|
14
|
-
style {
|
15
|
-
todo_input_styles
|
16
|
-
}
|
17
13
|
}
|
18
14
|
}
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def todo_input_styles
|
25
|
-
super
|
16
|
+
style { # evaluated against class as a smart default convention (common to all instances)
|
17
|
+
todo_input_styles
|
26
18
|
|
27
|
-
rule(".#{
|
19
|
+
rule(".#{component_element_class}") { # built-in component_class.component_element_class (e.g. NewTodoInput has CSS class as new-todo-input)
|
28
20
|
padding '16px 16px 16px 60px'
|
29
21
|
height '65px'
|
30
22
|
border 'none'
|
@@ -32,10 +24,10 @@ class NewTodoInput < TodoInput
|
|
32
24
|
box_shadow 'inset 0 -2px 1px rgba(0,0,0,0.03)'
|
33
25
|
}
|
34
26
|
|
35
|
-
rule(".#{
|
27
|
+
rule(".#{component_element_class}::placeholder") { # built-in component_class.component_element_class (e.g. NewTodoInput has CSS class as new-todo-input)
|
36
28
|
font_style 'italic'
|
37
29
|
font_weight '400'
|
38
30
|
color 'rgba(0, 0, 0, 0.4)'
|
39
31
|
}
|
40
|
-
|
32
|
+
}
|
41
33
|
end
|
@@ -45,80 +45,80 @@ class TodoFilters
|
|
45
45
|
presenter.clear_completed
|
46
46
|
end
|
47
47
|
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
style {
|
52
|
+
rule('.todo-filters') {
|
53
|
+
border_top '1px solid #e6e6e6'
|
54
|
+
font_size '15px'
|
55
|
+
height '20px'
|
56
|
+
padding '10px 15px'
|
57
|
+
text_align 'center'
|
58
|
+
}
|
59
|
+
|
60
|
+
rule('.todo-filters:before') {
|
61
|
+
bottom '0'
|
62
|
+
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)'
|
63
|
+
content '""'
|
64
|
+
height '50px'
|
65
|
+
left '0'
|
66
|
+
overflow 'hidden'
|
67
|
+
position 'absolute'
|
68
|
+
right '0'
|
69
|
+
}
|
70
|
+
|
71
|
+
rule('.todo-count') {
|
72
|
+
float 'left'
|
73
|
+
text_align 'left'
|
74
|
+
}
|
75
|
+
|
76
|
+
rule('.todo-count .strong') {
|
77
|
+
font_weight '300'
|
78
|
+
}
|
79
|
+
|
80
|
+
rule('.filters') {
|
81
|
+
left '0'
|
82
|
+
list_style 'none'
|
83
|
+
margin '0'
|
84
|
+
padding '0'
|
85
|
+
position 'absolute'
|
86
|
+
right '0'
|
87
|
+
}
|
88
|
+
|
89
|
+
rule('.filters li') {
|
90
|
+
display 'inline'
|
91
|
+
}
|
92
|
+
|
93
|
+
rule('.filters li a') {
|
94
|
+
border '1px solid transparent'
|
95
|
+
border_radius '3px'
|
96
|
+
color 'inherit'
|
97
|
+
margin '3px'
|
98
|
+
padding '3px 7px'
|
99
|
+
text_decoration 'none'
|
100
|
+
cursor 'pointer'
|
101
|
+
}
|
102
|
+
|
103
|
+
rule('.filters li a.selected') {
|
104
|
+
border_color '#ce4646'
|
105
|
+
}
|
106
|
+
|
107
|
+
rule('.clear-completed, html .clear-completed:active') {
|
108
|
+
cursor 'pointer'
|
109
|
+
float 'right'
|
110
|
+
line_height '19px'
|
111
|
+
position 'relative'
|
112
|
+
text_decoration 'none'
|
113
|
+
}
|
114
|
+
|
115
|
+
media('(max-width: 430px)') {
|
116
|
+
rule('.todo-filters') {
|
117
|
+
height '50px'
|
118
|
+
}
|
48
119
|
|
49
|
-
|
50
|
-
|
51
|
-
border_top '1px solid #e6e6e6'
|
52
|
-
font_size '15px'
|
53
|
-
height '20px'
|
54
|
-
padding '10px 15px'
|
55
|
-
text_align 'center'
|
56
|
-
}
|
57
|
-
|
58
|
-
rule('.todo-filters:before') {
|
59
|
-
bottom '0'
|
60
|
-
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)'
|
61
|
-
content '""'
|
62
|
-
height '50px'
|
63
|
-
left '0'
|
64
|
-
overflow 'hidden'
|
65
|
-
position 'absolute'
|
66
|
-
right '0'
|
67
|
-
}
|
68
|
-
|
69
|
-
rule('.todo-count') {
|
70
|
-
float 'left'
|
71
|
-
text_align 'left'
|
72
|
-
}
|
73
|
-
|
74
|
-
rule('.todo-count .strong') {
|
75
|
-
font_weight '300'
|
76
|
-
}
|
77
|
-
|
78
|
-
rule('.filters') {
|
79
|
-
left '0'
|
80
|
-
list_style 'none'
|
81
|
-
margin '0'
|
82
|
-
padding '0'
|
83
|
-
position 'absolute'
|
84
|
-
right '0'
|
85
|
-
}
|
86
|
-
|
87
|
-
rule('.filters li') {
|
88
|
-
display 'inline'
|
89
|
-
}
|
90
|
-
|
91
|
-
rule('.filters li a') {
|
92
|
-
border '1px solid transparent'
|
93
|
-
border_radius '3px'
|
94
|
-
color 'inherit'
|
95
|
-
margin '3px'
|
96
|
-
padding '3px 7px'
|
97
|
-
text_decoration 'none'
|
98
|
-
cursor 'pointer'
|
99
|
-
}
|
100
|
-
|
101
|
-
rule('.filters li a.selected') {
|
102
|
-
border_color '#ce4646'
|
103
|
-
}
|
104
|
-
|
105
|
-
rule('.clear-completed, html .clear-completed:active') {
|
106
|
-
cursor 'pointer'
|
107
|
-
float 'right'
|
108
|
-
line_height '19px'
|
109
|
-
position 'relative'
|
110
|
-
text_decoration 'none'
|
111
|
-
}
|
112
|
-
|
113
|
-
media('(max-width: 430px)') {
|
114
|
-
rule('.todo-filters') {
|
115
|
-
height '50px'
|
116
|
-
}
|
117
|
-
|
118
|
-
rule('.filters') {
|
119
|
-
bottom '10px'
|
120
|
-
}
|
121
|
-
}
|
120
|
+
rule('.filters') {
|
121
|
+
bottom '10px'
|
122
122
|
}
|
123
123
|
}
|
124
124
|
}
|
@@ -2,29 +2,32 @@
|
|
2
2
|
class TodoInput
|
3
3
|
include Glimmer::Web::Component
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
5
|
+
class << self
|
6
|
+
def todo_input_styles
|
7
|
+
rule(".#{component_element_class}") {
|
8
|
+
position 'relative'
|
9
|
+
margin '0'
|
10
|
+
width '100%'
|
11
|
+
font_size '24px'
|
12
|
+
font_family 'inherit'
|
13
|
+
font_weight 'inherit'
|
14
|
+
line_height '1.4em'
|
15
|
+
color 'inherit'
|
16
|
+
padding '6px'
|
17
|
+
border '1px solid #999'
|
18
|
+
box_shadow 'inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2)'
|
19
|
+
box_sizing 'border-box'
|
20
|
+
_webkit_font_smoothing 'antialiased'
|
21
|
+
}
|
22
|
+
|
23
|
+
rule(".#{component_element_class}::selection") {
|
24
|
+
background 'red'
|
25
|
+
}
|
26
|
+
|
27
|
+
rule(".#{component_element_class}:focus") {
|
28
|
+
box_shadow '0 0 2px 2px #cf7d7d'
|
29
|
+
outline '0'
|
30
|
+
}
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
@@ -38,14 +38,10 @@ class TodoList
|
|
38
38
|
todo_list_item(presenter:, todo:)
|
39
39
|
end
|
40
40
|
}
|
41
|
-
|
42
|
-
style {
|
43
|
-
todo_list_styles
|
44
|
-
}
|
45
41
|
}
|
46
42
|
}
|
47
43
|
|
48
|
-
|
44
|
+
style {
|
49
45
|
rule('.main') {
|
50
46
|
border_top '1px solid #e6e6e6'
|
51
47
|
position 'relative'
|
@@ -84,7 +80,7 @@ class TodoList
|
|
84
80
|
transform 'rotate(90deg)'
|
85
81
|
}
|
86
82
|
|
87
|
-
rule('.toggle-all:focus+label, .toggle:focus+label
|
83
|
+
rule('.toggle-all:focus+label, .toggle:focus+label') {
|
88
84
|
box_shadow '0 0 2px 2px #cf7d7d'
|
89
85
|
outline '0'
|
90
86
|
}
|
@@ -102,7 +98,5 @@ class TodoList
|
|
102
98
|
rule('.todo-list.completed li.active') {
|
103
99
|
display 'none'
|
104
100
|
}
|
105
|
-
|
106
|
-
TodoListItem.todo_list_item_styles
|
107
|
-
end
|
101
|
+
}
|
108
102
|
end
|