glimmer-dsl-web 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|