glimmer-dsl-web 0.7.1 → 0.7.3
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 +8 -0
- data/README.md +504 -330
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +4 -2
- data/lib/glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb +127 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb +81 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cdfd633318946532548053f4ab89f31e423280ae73ba9505b598b0e60695680
|
4
|
+
data.tar.gz: e95ac60a8077a1157abaf6da13cf0f7adab1f3f72fb727bb9cf270ae373cab2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c77c3cc7a2eabae100de6dd48e5797f8ce28774c4da17e6324bfe61fd8ece5d3e7b811538eabef1989f31de589e2ce64a1bbf122998c963158e844f33ca73987
|
7
|
+
data.tar.gz: a9f1a3ca8a39beae60602248b395621cadf2ae15362be300ff34f2d1d7d9c1464dcdab0614f31838388ae7a3e556abf0437f372642566827bd2ec72ba831934d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.7.3
|
4
|
+
|
5
|
+
- Hello, Component Attribute Data-Binding! Sample: `require 'glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb'`
|
6
|
+
|
7
|
+
## 0.7.2
|
8
|
+
|
9
|
+
- Hello, Component Attribute Listeners! Sample: `require 'glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb'`
|
10
|
+
|
3
11
|
## 0.7.1
|
4
12
|
|
5
13
|
- Fix issue with not being able to pass arguments containing apostrophe (') to glimmer_component helper (provided by GlimmerHelper)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.7.
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Web 0.7.3 (Beta)
|
2
2
|
## Ruby-in-the-Browser Web Frontend Framework
|
3
3
|
### The "Rails" of Frontend Frameworks!!! ([Fukuoka Award Winning](https://andymaleh.blogspot.com/2025/01/glimmer-dsl-for-web-wins-in-fukuoka.html))
|
4
4
|
#### Finally, Ruby Developer Productivity, Happiness, and Fun in the Frontend!!!
|
@@ -1348,10 +1348,12 @@ Learn more about the differences between various [Glimmer](https://github.com/An
|
|
1348
1348
|
- [Hello, Observer (Data-Binding)!](#hello-observer)
|
1349
1349
|
- [Hello, Data-Binding!](#hello-data-binding)
|
1350
1350
|
- [Hello, Content Data-Binding!](#hello-content-data-binding)
|
1351
|
-
- [Hello, Component!](#hello-
|
1351
|
+
- [Hello, Component!](#hello-component)
|
1352
1352
|
- [Hello, Component Slots!](#hello-component-slots)
|
1353
|
-
- [Hello, Component Listeners!](#hello-
|
1354
|
-
- [Hello, Component Listeners (Default Slot)!](#hello-
|
1353
|
+
- [Hello, Component Listeners!](#hello-component-listeners)
|
1354
|
+
- [Hello, Component Listeners (Default Slot)!](#hello-component-listeners-default-slot)
|
1355
|
+
- [Hello, Component Attribute Listeners!](#hello-component-attribute-listeners)
|
1356
|
+
- [Hello, Component Attribute Data-Binding!](#hello-component-attribute-data-binding)
|
1355
1357
|
- [Hello, glimmer_component Rails Helper!](#hello-glimmer_component-rails-helper)
|
1356
1358
|
- [Hello, Paragraph!](#hello-paragraph)
|
1357
1359
|
- [Hello, Style!](#hello-style)
|
@@ -1389,7 +1391,7 @@ Once done, read [Usage](#usage) instructions. Note that for serious app usage, i
|
|
1389
1391
|
|
1390
1392
|
### Rails 8
|
1391
1393
|
|
1392
|
-
Rails 8 instructions are not ready yet though they would rely on the `opal --watch` command in the Development environment to generate JavaScript files in the directory that propshaft is setup with, and Rails 8 would rely on the `opal` command to generate JavaScript files for the Production environment.
|
1394
|
+
Rails 8 instructions are not ready yet though they would rely on the `opal --watch` command in the Development environment to generate JavaScript files in the directory that propshaft is setup with, and Rails 8 would rely on the `opal` command to generate JavaScript files for the Production environment.
|
1393
1395
|
|
1394
1396
|
Instructions will be added in the future.
|
1395
1397
|
|
@@ -1416,7 +1418,7 @@ rails new glimmer_app_server
|
|
1416
1418
|
Add the following to `Gemfile`:
|
1417
1419
|
|
1418
1420
|
```
|
1419
|
-
gem 'glimmer-dsl-web', '~> 0.7.
|
1421
|
+
gem 'glimmer-dsl-web', '~> 0.7.3'
|
1420
1422
|
```
|
1421
1423
|
|
1422
1424
|
Run:
|
@@ -2994,367 +2996,357 @@ Glimmer HTML DSL Ruby code in the frontend:
|
|
2994
2996
|
```ruby
|
2995
2997
|
require 'glimmer-dsl-web'
|
2996
2998
|
|
2997
|
-
|
2998
|
-
|
2999
|
-
|
3000
|
-
|
3001
|
-
|
3002
|
-
|
3003
|
-
|
3004
|
-
|
3005
|
-
|
3006
|
-
|
3007
|
-
|
3008
|
-
|
3009
|
-
|
3010
|
-
|
3011
|
-
|
3012
|
-
|
3013
|
-
|
3014
|
-
|
3015
|
-
|
3016
|
-
|
3017
|
-
|
3018
|
-
|
3019
|
-
|
3020
|
-
|
3021
|
-
|
3022
|
-
|
3023
|
-
|
3024
|
-
|
3025
|
-
|
3026
|
-
|
3027
|
-
|
3028
|
-
|
3029
|
-
|
3030
|
-
|
3031
|
-
|
3032
|
-
|
3033
|
-
|
3034
|
-
|
3035
|
-
|
3036
|
-
|
3037
|
-
|
3038
|
-
|
3039
|
-
|
3040
|
-
|
3041
|
-
|
3042
|
-
|
3043
|
-
|
3044
|
-
|
3045
|
-
|
3046
|
-
|
3047
|
-
|
3048
|
-
|
3049
|
-
|
3050
|
-
|
3051
|
-
|
3052
|
-
|
3053
|
-
|
3054
|
-
|
3055
|
-
}
|
3056
|
-
|
3057
|
-
def state_code
|
3058
|
-
STATES.invert[state]
|
3059
|
-
end
|
3060
|
-
|
3061
|
-
def state_code=(value)
|
3062
|
-
self.state = STATES[value]
|
3063
|
-
end
|
2999
|
+
Address = Struct.new(:full_name, :street, :street2, :city, :state, :zip_code, keyword_init: true) do
|
3000
|
+
STATES = {
|
3001
|
+
"AK"=>"Alaska",
|
3002
|
+
"AL"=>"Alabama",
|
3003
|
+
"AR"=>"Arkansas",
|
3004
|
+
"AS"=>"American Samoa",
|
3005
|
+
"AZ"=>"Arizona",
|
3006
|
+
"CA"=>"California",
|
3007
|
+
"CO"=>"Colorado",
|
3008
|
+
"CT"=>"Connecticut",
|
3009
|
+
"DC"=>"District of Columbia",
|
3010
|
+
"DE"=>"Delaware",
|
3011
|
+
"FL"=>"Florida",
|
3012
|
+
"GA"=>"Georgia",
|
3013
|
+
"GU"=>"Guam",
|
3014
|
+
"HI"=>"Hawaii",
|
3015
|
+
"IA"=>"Iowa",
|
3016
|
+
"ID"=>"Idaho",
|
3017
|
+
"IL"=>"Illinois",
|
3018
|
+
"IN"=>"Indiana",
|
3019
|
+
"KS"=>"Kansas",
|
3020
|
+
"KY"=>"Kentucky",
|
3021
|
+
"LA"=>"Louisiana",
|
3022
|
+
"MA"=>"Massachusetts",
|
3023
|
+
"MD"=>"Maryland",
|
3024
|
+
"ME"=>"Maine",
|
3025
|
+
"MI"=>"Michigan",
|
3026
|
+
"MN"=>"Minnesota",
|
3027
|
+
"MO"=>"Missouri",
|
3028
|
+
"MS"=>"Mississippi",
|
3029
|
+
"MT"=>"Montana",
|
3030
|
+
"NC"=>"North Carolina",
|
3031
|
+
"ND"=>"North Dakota",
|
3032
|
+
"NE"=>"Nebraska",
|
3033
|
+
"NH"=>"New Hampshire",
|
3034
|
+
"NJ"=>"New Jersey",
|
3035
|
+
"NM"=>"New Mexico",
|
3036
|
+
"NV"=>"Nevada",
|
3037
|
+
"NY"=>"New York",
|
3038
|
+
"OH"=>"Ohio",
|
3039
|
+
"OK"=>"Oklahoma",
|
3040
|
+
"OR"=>"Oregon",
|
3041
|
+
"PA"=>"Pennsylvania",
|
3042
|
+
"PR"=>"Puerto Rico",
|
3043
|
+
"RI"=>"Rhode Island",
|
3044
|
+
"SC"=>"South Carolina",
|
3045
|
+
"SD"=>"South Dakota",
|
3046
|
+
"TN"=>"Tennessee",
|
3047
|
+
"TX"=>"Texas",
|
3048
|
+
"UT"=>"Utah",
|
3049
|
+
"VA"=>"Virginia",
|
3050
|
+
"VI"=>"Virgin Islands",
|
3051
|
+
"VT"=>"Vermont",
|
3052
|
+
"WA"=>"Washington",
|
3053
|
+
"WI"=>"Wisconsin",
|
3054
|
+
"WV"=>"West Virginia",
|
3055
|
+
"WY"=>"Wyoming"
|
3056
|
+
}
|
3064
3057
|
|
3065
|
-
|
3066
|
-
|
3067
|
-
|
3058
|
+
def state_code
|
3059
|
+
STATES.invert[state]
|
3060
|
+
end
|
3061
|
+
|
3062
|
+
def state_code=(value)
|
3063
|
+
self.state = STATES[value]
|
3064
|
+
end
|
3065
|
+
|
3066
|
+
def summary
|
3067
|
+
to_h.values.map(&:to_s).reject(&:empty?).join(', ')
|
3068
3068
|
end
|
3069
3069
|
end
|
3070
3070
|
|
3071
|
-
|
3072
|
-
|
3073
|
-
|
3074
|
-
|
3075
|
-
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3079
|
-
include Glimmer::Web::Component
|
3080
|
-
|
3081
|
-
option :address
|
3082
|
-
|
3083
|
-
markup {
|
3084
|
-
div {
|
3085
|
-
div(style: {display: :grid, grid_auto_columns: '80px 260px'}) { |address_div|
|
3086
|
-
label('Full Name: ', for: 'full-name-field')
|
3087
|
-
input(id: 'full-name-field') {
|
3088
|
-
value <=> [address, :full_name]
|
3089
|
-
}
|
3090
|
-
|
3091
|
-
label('Street: ', for: 'street-field')
|
3092
|
-
input(id: 'street-field') {
|
3093
|
-
value <=> [address, :street]
|
3094
|
-
}
|
3095
|
-
|
3096
|
-
label('Street 2: ', for: 'street2-field')
|
3097
|
-
textarea(id: 'street2-field') {
|
3098
|
-
value <=> [address, :street2]
|
3099
|
-
}
|
3100
|
-
|
3101
|
-
label('City: ', for: 'city-field')
|
3102
|
-
input(id: 'city-field') {
|
3103
|
-
value <=> [address, :city]
|
3104
|
-
}
|
3105
|
-
|
3106
|
-
label('State: ', for: 'state-field')
|
3107
|
-
select(id: 'state-field') {
|
3108
|
-
Address::STATES.each do |state_code, state|
|
3109
|
-
option(value: state_code) { state }
|
3110
|
-
end
|
3071
|
+
# AddressForm Glimmer Web Component (View component)
|
3072
|
+
#
|
3073
|
+
# Including Glimmer::Web::Component makes this class a View component and automatically
|
3074
|
+
# generates a new Glimmer HTML DSL keyword that matches the lowercase underscored version
|
3075
|
+
# of the name of the class. AddressForm generates address_form keyword, which can be used
|
3076
|
+
# elsewhere in Glimmer HTML DSL code as done inside HelloComponentListeners below.
|
3077
|
+
class AddressForm
|
3078
|
+
include Glimmer::Web::Component
|
3111
3079
|
|
3112
|
-
|
3113
|
-
|
3114
|
-
|
3115
|
-
|
3116
|
-
|
3117
|
-
|
3118
|
-
|
3119
|
-
|
3120
|
-
}
|
3121
|
-
|
3122
|
-
style {
|
3123
|
-
r("#{address_div.selector} *") {
|
3124
|
-
margin '5px'
|
3125
|
-
}
|
3126
|
-
r("#{address_div.selector} input, #{address_div.selector} select") {
|
3127
|
-
grid_column '2'
|
3128
|
-
}
|
3129
|
-
}
|
3080
|
+
option :address
|
3081
|
+
|
3082
|
+
markup {
|
3083
|
+
div {
|
3084
|
+
div(style: {display: :grid, grid_auto_columns: '80px 260px'}) { |address_div|
|
3085
|
+
label('Full Name: ', for: 'full-name-field')
|
3086
|
+
input(id: 'full-name-field') {
|
3087
|
+
value <=> [address, :full_name]
|
3130
3088
|
}
|
3131
3089
|
|
3132
|
-
|
3133
|
-
|
3134
|
-
|
3135
|
-
]
|
3090
|
+
label('Street: ', for: 'street-field')
|
3091
|
+
input(id: 'street-field') {
|
3092
|
+
value <=> [address, :street]
|
3136
3093
|
}
|
3137
|
-
|
3138
|
-
|
3139
|
-
|
3140
|
-
|
3141
|
-
|
3142
|
-
|
3143
|
-
|
3144
|
-
|
3145
|
-
|
3146
|
-
|
3147
|
-
|
3148
|
-
|
3149
|
-
|
3150
|
-
|
3151
|
-
|
3152
|
-
def expand(instant: false)
|
3153
|
-
self.instant_transition = instant
|
3154
|
-
self.collapsed = false
|
3155
|
-
end
|
3156
|
-
|
3157
|
-
def collapse(instant: false)
|
3158
|
-
self.instant_transition = instant
|
3159
|
-
self.collapsed = true
|
3160
|
-
end
|
3161
|
-
end
|
3162
|
-
|
3163
|
-
include Glimmer::Web::Component
|
3164
|
-
|
3165
|
-
events :expanded, :collapsed
|
3166
|
-
|
3167
|
-
option :title
|
3168
|
-
|
3169
|
-
attr_reader :presenter
|
3170
|
-
|
3171
|
-
before_render do
|
3172
|
-
@presenter = Presenter.new
|
3173
|
-
end
|
3174
|
-
|
3175
|
-
markup {
|
3176
|
-
section {
|
3177
|
-
# Unidirectionally data-bind the class inclusion of 'collapsed' to the @presenter.collapsed boolean attribute,
|
3178
|
-
# meaning if @presenter.collapsed changes to true, the CSS class 'collapsed' is included on the element,
|
3179
|
-
# and if it changes to false, the CSS class 'collapsed' is removed from the element.
|
3180
|
-
class_name(:collapsed) <= [@presenter, :collapsed]
|
3181
|
-
class_name(:instant_transition) <= [@presenter, :instant_transition]
|
3182
|
-
|
3183
|
-
header(title, class: 'accordion-section-title') {
|
3184
|
-
onclick do |event|
|
3185
|
-
@presenter.toggle_collapsed
|
3186
|
-
if @presenter.collapsed
|
3187
|
-
notify_listeners(:collapsed)
|
3188
|
-
else
|
3189
|
-
notify_listeners(:expanded)
|
3190
|
-
end
|
3094
|
+
|
3095
|
+
label('Street 2: ', for: 'street2-field')
|
3096
|
+
textarea(id: 'street2-field') {
|
3097
|
+
value <=> [address, :street2]
|
3098
|
+
}
|
3099
|
+
|
3100
|
+
label('City: ', for: 'city-field')
|
3101
|
+
input(id: 'city-field') {
|
3102
|
+
value <=> [address, :city]
|
3103
|
+
}
|
3104
|
+
|
3105
|
+
label('State: ', for: 'state-field')
|
3106
|
+
select(id: 'state-field') {
|
3107
|
+
Address::STATES.each do |state_code, state|
|
3108
|
+
option(value: state_code) { state }
|
3191
3109
|
end
|
3110
|
+
|
3111
|
+
value <=> [address, :state_code]
|
3192
3112
|
}
|
3193
3113
|
|
3194
|
-
|
3195
|
-
|
3196
|
-
|
3197
|
-
|
3198
|
-
|
3199
|
-
|
3200
|
-
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
|
3206
|
-
|
3207
|
-
|
3208
|
-
|
3209
|
-
r('.accordion-section-title::before') {
|
3210
|
-
content '"▼"'
|
3211
|
-
position :absolute
|
3212
|
-
font_size 0.5.em
|
3213
|
-
top 10
|
3214
|
-
left 0
|
3215
|
-
}
|
3216
|
-
|
3217
|
-
r('.accordion-section-content') {
|
3218
|
-
height 246
|
3219
|
-
overflow :hidden
|
3220
|
-
transition 'height 0.5s linear'
|
3221
|
-
}
|
3222
|
-
|
3223
|
-
r("#{component_element_selector}.instant_transition .accordion-section-content") {
|
3224
|
-
transition 'initial'
|
3225
|
-
}
|
3226
|
-
|
3227
|
-
r("#{component_element_selector}.collapsed .accordion-section-title::before") {
|
3228
|
-
content '"►"'
|
3114
|
+
label('Zip Code: ', for: 'zip-code-field')
|
3115
|
+
input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
|
3116
|
+
value <=> [address, :zip_code,
|
3117
|
+
on_write: :to_s,
|
3118
|
+
]
|
3119
|
+
}
|
3120
|
+
|
3121
|
+
style {
|
3122
|
+
r("#{address_div.selector} *") {
|
3123
|
+
margin '5px'
|
3124
|
+
}
|
3125
|
+
r("#{address_div.selector} input, #{address_div.selector} select") {
|
3126
|
+
grid_column '2'
|
3127
|
+
}
|
3128
|
+
}
|
3229
3129
|
}
|
3230
3130
|
|
3231
|
-
|
3232
|
-
|
3131
|
+
div(style: {margin: 5}) {
|
3132
|
+
inner_text <= [address, :summary,
|
3133
|
+
computed_by: address.members + ['state_code'],
|
3134
|
+
]
|
3233
3135
|
}
|
3234
3136
|
}
|
3235
|
-
|
3137
|
+
}
|
3236
3138
|
end
|
3237
3139
|
|
3238
|
-
|
3239
|
-
class
|
3240
|
-
|
3140
|
+
class AccordionSection
|
3141
|
+
class Presenter
|
3142
|
+
attr_accessor :collapsed, :instant_transition
|
3241
3143
|
|
3242
|
-
|
3144
|
+
def toggle_collapsed(instant: false)
|
3145
|
+
self.instant_transition = instant
|
3146
|
+
self.collapsed = !collapsed
|
3147
|
+
end
|
3243
3148
|
|
3244
|
-
|
3245
|
-
|
3246
|
-
|
3247
|
-
|
3248
|
-
|
3249
|
-
|
3250
|
-
|
3251
|
-
|
3252
|
-
|
3253
|
-
|
3254
|
-
|
3149
|
+
def expand(instant: false)
|
3150
|
+
self.instant_transition = instant
|
3151
|
+
self.collapsed = false
|
3152
|
+
end
|
3153
|
+
|
3154
|
+
def collapse(instant: false)
|
3155
|
+
self.instant_transition = instant
|
3156
|
+
self.collapsed = true
|
3157
|
+
end
|
3158
|
+
end
|
3159
|
+
|
3160
|
+
include Glimmer::Web::Component
|
3161
|
+
|
3162
|
+
events :expanded, :collapsed
|
3255
3163
|
|
3256
|
-
|
3257
|
-
accordion_section.presenter.collapse(instant: true) if accordion_section_number != 1
|
3164
|
+
option :title
|
3258
3165
|
|
3259
|
-
|
3260
|
-
on_expanded do
|
3261
|
-
other_accordion_sections = accordion_sections.reject {|other_accordion_section| other_accordion_section == accordion_section }
|
3262
|
-
other_accordion_sections.each { |other_accordion_section| other_accordion_section.presenter.collapse }
|
3263
|
-
notify_listeners(:accordion_section_expanded, accordion_section_number)
|
3264
|
-
end
|
3166
|
+
attr_reader :presenter
|
3265
3167
|
|
3266
|
-
|
3267
|
-
|
3268
|
-
|
3269
|
-
|
3168
|
+
before_render do
|
3169
|
+
@presenter = Presenter.new
|
3170
|
+
end
|
3171
|
+
|
3172
|
+
markup {
|
3173
|
+
section {
|
3174
|
+
# Unidirectionally data-bind the class inclusion of 'collapsed' to the @presenter.collapsed boolean attribute,
|
3175
|
+
# meaning if @presenter.collapsed changes to true, the CSS class 'collapsed' is included on the element,
|
3176
|
+
# and if it changes to false, the CSS class 'collapsed' is removed from the element.
|
3177
|
+
class_name(:collapsed) <= [@presenter, :collapsed]
|
3178
|
+
class_name(:instant_transition) <= [@presenter, :instant_transition]
|
3179
|
+
|
3180
|
+
header(title, class: 'accordion-section-title') {
|
3181
|
+
onclick do |event|
|
3182
|
+
@presenter.toggle_collapsed
|
3183
|
+
if @presenter.collapsed
|
3184
|
+
notify_listeners(:collapsed)
|
3185
|
+
else
|
3186
|
+
notify_listeners(:expanded)
|
3270
3187
|
end
|
3271
3188
|
end
|
3272
3189
|
}
|
3190
|
+
|
3191
|
+
div(slot: :section_content, class: 'accordion-section-content')
|
3273
3192
|
}
|
3274
|
-
|
3193
|
+
}
|
3194
|
+
|
3195
|
+
style {
|
3196
|
+
r('.accordion-section-title') {
|
3197
|
+
font_size 2.em
|
3198
|
+
font_weight :bold
|
3199
|
+
cursor :pointer
|
3200
|
+
padding_left 20
|
3201
|
+
position :relative
|
3202
|
+
margin_block_start 0.33.em
|
3203
|
+
margin_block_end 0.33.em
|
3204
|
+
}
|
3205
|
+
|
3206
|
+
r('.accordion-section-title::before') {
|
3207
|
+
content '"▼"'
|
3208
|
+
position :absolute
|
3209
|
+
font_size 0.5.em
|
3210
|
+
top 10
|
3211
|
+
left 0
|
3212
|
+
}
|
3213
|
+
|
3214
|
+
r('.accordion-section-content') {
|
3215
|
+
height 246
|
3216
|
+
overflow :hidden
|
3217
|
+
transition 'height 0.5s linear'
|
3218
|
+
}
|
3219
|
+
|
3220
|
+
r("#{component_element_selector}.instant_transition .accordion-section-content") {
|
3221
|
+
transition 'initial'
|
3222
|
+
}
|
3223
|
+
|
3224
|
+
r("#{component_element_selector}.collapsed .accordion-section-title::before") {
|
3225
|
+
content '"►"'
|
3226
|
+
}
|
3227
|
+
|
3228
|
+
r("#{component_element_selector}.collapsed .accordion-section-content") {
|
3229
|
+
height 0
|
3230
|
+
}
|
3231
|
+
}
|
3275
3232
|
end
|
3276
3233
|
|
3277
|
-
|
3278
|
-
|
3279
|
-
|
3280
|
-
|
3281
|
-
|
3282
|
-
|
3283
|
-
|
3284
|
-
|
3285
|
-
|
3286
|
-
|
3287
|
-
|
3234
|
+
class Accordion
|
3235
|
+
include Glimmer::Web::Component
|
3236
|
+
|
3237
|
+
events :accordion_section_expanded, :accordion_section_collapsed
|
3238
|
+
|
3239
|
+
markup {
|
3240
|
+
# given that no slots are specified, nesting content under the accordion component
|
3241
|
+
# in consumer code adds content directly inside the markup root div.
|
3242
|
+
div { |accordion|
|
3243
|
+
# on render, all accordion sections would have been added by consumers already, so we can
|
3244
|
+
# attach listeners to all of them by re-opening their content with `.content { ... }` block
|
3245
|
+
on_render do
|
3246
|
+
accordion_section_elements = accordion.children
|
3247
|
+
accordion_sections = accordion_section_elements.map(&:component)
|
3248
|
+
accordion_sections.each_with_index do |accordion_section, index|
|
3249
|
+
accordion_section_number = index + 1
|
3250
|
+
|
3251
|
+
# ensure only the first section is expanded
|
3252
|
+
accordion_section.presenter.collapse(instant: true) if accordion_section_number != 1
|
3253
|
+
|
3254
|
+
accordion_section.content {
|
3255
|
+
on_expanded do
|
3256
|
+
other_accordion_sections = accordion_sections.reject {|other_accordion_section| other_accordion_section == accordion_section }
|
3257
|
+
other_accordion_sections.each { |other_accordion_section| other_accordion_section.presenter.collapse }
|
3258
|
+
notify_listeners(:accordion_section_expanded, accordion_section_number)
|
3259
|
+
end
|
3260
|
+
|
3261
|
+
on_collapsed do
|
3262
|
+
notify_listeners(:accordion_section_collapsed, accordion_section_number)
|
3263
|
+
end
|
3264
|
+
}
|
3265
|
+
end
|
3288
3266
|
end
|
3289
|
-
|
3290
|
-
|
3291
|
-
|
3267
|
+
}
|
3268
|
+
}
|
3269
|
+
end
|
3270
|
+
|
3271
|
+
# HelloComponentListeners Glimmer Web Component (View component)
|
3272
|
+
#
|
3273
|
+
# This View component represents the main page being rendered,
|
3274
|
+
# as done by its `render` class method below
|
3275
|
+
class HelloComponentListeners
|
3276
|
+
class Presenter
|
3277
|
+
attr_accessor :status_message
|
3292
3278
|
|
3293
|
-
|
3294
|
-
@
|
3295
|
-
@shipping_address = Address.new(
|
3296
|
-
full_name: 'Johnny Doe',
|
3297
|
-
street: '3922 Park Ave',
|
3298
|
-
street2: 'PO BOX 8382',
|
3299
|
-
city: 'San Diego',
|
3300
|
-
state: 'California',
|
3301
|
-
zip_code: '91913',
|
3302
|
-
)
|
3303
|
-
@billing_address = Address.new(
|
3304
|
-
full_name: 'John C Doe',
|
3305
|
-
street: '123 Main St',
|
3306
|
-
street2: 'Apartment 3C',
|
3307
|
-
city: 'San Diego',
|
3308
|
-
state: 'California',
|
3309
|
-
zip_code: '91911',
|
3310
|
-
)
|
3311
|
-
@emergency_address = Address.new(
|
3312
|
-
full_name: 'Mary Doe',
|
3313
|
-
street: '2038 Ipswitch St',
|
3314
|
-
street2: 'Suite 300',
|
3315
|
-
city: 'San Diego',
|
3316
|
-
state: 'California',
|
3317
|
-
zip_code: '91912',
|
3318
|
-
)
|
3279
|
+
def initialize
|
3280
|
+
@status_message = "Accordion section 1 is expanded!"
|
3319
3281
|
end
|
3320
|
-
|
3321
|
-
|
3322
|
-
|
3323
|
-
|
3324
|
-
|
3325
|
-
|
3326
|
-
|
3327
|
-
|
3328
|
-
|
3329
|
-
|
3330
|
-
|
3331
|
-
|
3332
|
-
|
3333
|
-
|
3334
|
-
|
3335
|
-
|
3336
|
-
|
3337
|
-
|
3338
|
-
|
3339
|
-
|
3340
|
-
|
3341
|
-
|
3342
|
-
|
3343
|
-
|
3282
|
+
end
|
3283
|
+
|
3284
|
+
include Glimmer::Web::Component
|
3285
|
+
|
3286
|
+
before_render do
|
3287
|
+
@presenter = Presenter.new
|
3288
|
+
@shipping_address = Address.new(
|
3289
|
+
full_name: 'Johnny Doe',
|
3290
|
+
street: '3922 Park Ave',
|
3291
|
+
street2: 'PO BOX 8382',
|
3292
|
+
city: 'San Diego',
|
3293
|
+
state: 'California',
|
3294
|
+
zip_code: '91913',
|
3295
|
+
)
|
3296
|
+
@billing_address = Address.new(
|
3297
|
+
full_name: 'John C Doe',
|
3298
|
+
street: '123 Main St',
|
3299
|
+
street2: 'Apartment 3C',
|
3300
|
+
city: 'San Diego',
|
3301
|
+
state: 'California',
|
3302
|
+
zip_code: '91911',
|
3303
|
+
)
|
3304
|
+
@emergency_address = Address.new(
|
3305
|
+
full_name: 'Mary Doe',
|
3306
|
+
street: '2038 Ipswitch St',
|
3307
|
+
street2: 'Suite 300',
|
3308
|
+
city: 'San Diego',
|
3309
|
+
state: 'California',
|
3310
|
+
zip_code: '91912',
|
3311
|
+
)
|
3312
|
+
end
|
3313
|
+
|
3314
|
+
markup {
|
3315
|
+
div {
|
3316
|
+
h1(style: {font_style: :italic}) {
|
3317
|
+
inner_html <= [@presenter, :status_message]
|
3318
|
+
}
|
3319
|
+
|
3320
|
+
accordion { # any content nested under component directly is added under its markup root div element
|
3321
|
+
accordion_section(title: 'Shipping Address') {
|
3322
|
+
section_content { # contribute elements to section_content slot declared in AccordionSection component
|
3323
|
+
address_form(address: @shipping_address)
|
3344
3324
|
}
|
3345
|
-
|
3346
|
-
|
3347
|
-
|
3348
|
-
|
3325
|
+
}
|
3326
|
+
|
3327
|
+
accordion_section(title: 'Billing Address') {
|
3328
|
+
section_content {
|
3329
|
+
address_form(address: @billing_address)
|
3349
3330
|
}
|
3350
|
-
|
3351
|
-
|
3352
|
-
|
3331
|
+
}
|
3332
|
+
|
3333
|
+
accordion_section(title: 'Emergency Address') {
|
3334
|
+
section_content {
|
3335
|
+
address_form(address: @emergency_address)
|
3353
3336
|
}
|
3354
3337
|
}
|
3338
|
+
|
3339
|
+
# on_accordion_section_expanded listener matches event :accordion_section_expanded declared in Accordion component
|
3340
|
+
on_accordion_section_expanded { |accordion_section_number|
|
3341
|
+
@presenter.status_message = "Accordion section #{accordion_section_number} is expanded!"
|
3342
|
+
}
|
3343
|
+
|
3344
|
+
on_accordion_section_collapsed { |accordion_section_number|
|
3345
|
+
@presenter.status_message = "Accordion section #{accordion_section_number} is collapsed!"
|
3346
|
+
}
|
3355
3347
|
}
|
3356
3348
|
}
|
3357
|
-
|
3349
|
+
}
|
3358
3350
|
end
|
3359
3351
|
|
3360
3352
|
Document.ready? do
|
@@ -3758,6 +3750,188 @@ Screenshot:
|
|
3758
3750
|
|
3759
3751
|

|
3760
3752
|
|
3753
|
+
#### Hello, Component Attribute Listeners!
|
3754
|
+
|
3755
|
+
[lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb](/lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb)
|
3756
|
+
|
3757
|
+
Glimmer HTML DSL Ruby code in the frontend:
|
3758
|
+
|
3759
|
+
```ruby
|
3760
|
+
require 'glimmer-dsl-web'
|
3761
|
+
|
3762
|
+
class AddressTypeSelector
|
3763
|
+
include Glimmer::Web::Component
|
3764
|
+
|
3765
|
+
attribute :address_types, default: []
|
3766
|
+
attribute :selected_address_type
|
3767
|
+
|
3768
|
+
before_render do
|
3769
|
+
self.selected_address_type ||= address_types.first
|
3770
|
+
end
|
3771
|
+
|
3772
|
+
markup {
|
3773
|
+
select { |select_element|
|
3774
|
+
address_types.each do |address_type|
|
3775
|
+
option(value: address_type) { address_type }
|
3776
|
+
end
|
3777
|
+
|
3778
|
+
# Bidirectionally data-bind select value to selected_address_type attribute on self (component)
|
3779
|
+
value <=> [self, :selected_address_type]
|
3780
|
+
}
|
3781
|
+
}
|
3782
|
+
|
3783
|
+
style {
|
3784
|
+
r(component_element_selector) {
|
3785
|
+
font_size 2.em
|
3786
|
+
margin_left 5.px
|
3787
|
+
}
|
3788
|
+
}
|
3789
|
+
end
|
3790
|
+
|
3791
|
+
class AddressTypeSelectorPage
|
3792
|
+
include Glimmer::Web::Component
|
3793
|
+
|
3794
|
+
markup {
|
3795
|
+
div {
|
3796
|
+
h1('Address type for delivery:', style: {display: :inline})
|
3797
|
+
|
3798
|
+
address_type_selector(address_types: ['Home', 'Work', 'Other']) {
|
3799
|
+
# We can listen to the updates of any attribute/option in a Glimmer Web Component
|
3800
|
+
# on_{attribute_name}_update do execute code when component attribute/option with attribute_name is updated
|
3801
|
+
# This is an alternative to using Component Listeners, which require that the component explicitly calls notify_listeners,
|
3802
|
+
# whereas Component Attribute Listeners get tracked automatically, but depend on a specific attribute
|
3803
|
+
# The trade-off is Component Listeners provide more flexibility when needed as they are not bound to specific attributes,
|
3804
|
+
# but often Component Attribute Listeners are good enough as a solution for certain problems.
|
3805
|
+
on_selected_address_type_update do |address_type|
|
3806
|
+
$$.alert("You selected the address type: #{address_type}")
|
3807
|
+
end
|
3808
|
+
}
|
3809
|
+
}
|
3810
|
+
}
|
3811
|
+
end
|
3812
|
+
|
3813
|
+
Document.ready? do
|
3814
|
+
AddressTypeSelectorPage.render
|
3815
|
+
end
|
3816
|
+
```
|
3817
|
+
|
3818
|
+
Screenshot:
|
3819
|
+
|
3820
|
+

|
3821
|
+
|
3822
|
+

|
3823
|
+
|
3824
|
+

|
3825
|
+
|
3826
|
+
#### Hello, Component Attribute Data-Binding!
|
3827
|
+
|
3828
|
+
[lib/glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb](/lib/glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb)
|
3829
|
+
|
3830
|
+
```ruby
|
3831
|
+
require 'glimmer-dsl-web'
|
3832
|
+
|
3833
|
+
class AddressTypePresenter
|
3834
|
+
attr_accessor :address_types, :selected_address_type
|
3835
|
+
|
3836
|
+
def initialize(address_types:, selected_address_type: nil)
|
3837
|
+
@address_types = address_types
|
3838
|
+
@selected_address_type = selected_address_type
|
3839
|
+
@selected_address_type ||= address_types.first
|
3840
|
+
end
|
3841
|
+
end
|
3842
|
+
|
3843
|
+
class AddressTypeSelect
|
3844
|
+
include Glimmer::Web::Component
|
3845
|
+
|
3846
|
+
attribute :address_types, default: []
|
3847
|
+
attribute :selected_address_type
|
3848
|
+
|
3849
|
+
markup {
|
3850
|
+
select { |select_element|
|
3851
|
+
address_types.each do |address_type|
|
3852
|
+
option(value: address_type) { address_type }
|
3853
|
+
end
|
3854
|
+
|
3855
|
+
value <=> [self, :selected_address_type]
|
3856
|
+
}
|
3857
|
+
}
|
3858
|
+
|
3859
|
+
style {
|
3860
|
+
r(component_element_selector) {
|
3861
|
+
font_size 2.em
|
3862
|
+
margin_left 5.px
|
3863
|
+
}
|
3864
|
+
}
|
3865
|
+
end
|
3866
|
+
|
3867
|
+
class AddressTypeRadioGroup
|
3868
|
+
include Glimmer::Web::Component
|
3869
|
+
|
3870
|
+
attribute :address_types, default: []
|
3871
|
+
attribute :selected_address_type
|
3872
|
+
|
3873
|
+
markup {
|
3874
|
+
div { |select_element|
|
3875
|
+
address_types.each do |address_type|
|
3876
|
+
input_id = "radio_address_type_#{address_type}"
|
3877
|
+
input(id: input_id, type: 'radio', name: 'radio_address_type', value: address_type) {
|
3878
|
+
checked <=> [self, :selected_address_type,
|
3879
|
+
on_read: ->(address_type_string_value) { address_type_string_value == address_type },
|
3880
|
+
on_write: ->(radio_input_boolean_value) { radio_input_boolean_value ? address_type : selected_address_type },
|
3881
|
+
]
|
3882
|
+
}
|
3883
|
+
label(for: input_id) { address_type }
|
3884
|
+
end
|
3885
|
+
}
|
3886
|
+
}
|
3887
|
+
|
3888
|
+
style {
|
3889
|
+
r(component_element_selector) {
|
3890
|
+
font_size 2.em
|
3891
|
+
margin_top 10.px
|
3892
|
+
}
|
3893
|
+
r("#{component_element_selector} label") {
|
3894
|
+
margin_left 2.px
|
3895
|
+
margin_right 10.px
|
3896
|
+
}
|
3897
|
+
}
|
3898
|
+
end
|
3899
|
+
|
3900
|
+
class AddressTypeComponentAttributeDataBindingPage
|
3901
|
+
include Glimmer::Web::Component
|
3902
|
+
|
3903
|
+
before_render do
|
3904
|
+
@presenter = AddressTypePresenter.new(address_types: ['Home', 'Work', 'Other'])
|
3905
|
+
end
|
3906
|
+
|
3907
|
+
markup {
|
3908
|
+
div {
|
3909
|
+
h1('Address type for delivery:')
|
3910
|
+
|
3911
|
+
address_type_select(address_types: @presenter.address_types, selected_address_type: @presenter.selected_address_type) {
|
3912
|
+
# Not only can we data-bind attributes on basic HTML elements,
|
3913
|
+
# but we can also data-bind custom attributes on Glimmer Web Components
|
3914
|
+
selected_address_type <=> [@presenter, :selected_address_type]
|
3915
|
+
}
|
3916
|
+
|
3917
|
+
address_type_radio_group(address_types: @presenter.address_types, selected_address_type: @presenter.selected_address_type) {
|
3918
|
+
# Not only can we data-bind attributes on basic HTML elements,
|
3919
|
+
# but we can also data-bind custom attributes on Glimmer Web Components
|
3920
|
+
selected_address_type <=> [@presenter, :selected_address_type]
|
3921
|
+
}
|
3922
|
+
}
|
3923
|
+
}
|
3924
|
+
end
|
3925
|
+
|
3926
|
+
Document.ready? do
|
3927
|
+
AddressTypeComponentAttributeDataBindingPage.render
|
3928
|
+
end
|
3929
|
+
```
|
3930
|
+
|
3931
|
+
Screenshot:
|
3932
|
+
|
3933
|
+

|
3934
|
+
|
3761
3935
|
#### Hello, glimmer_component Rails Helper!
|
3762
3936
|
|
3763
3937
|
You may insert a Glimmer component anywhere into a Rails View using
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.3
|
data/glimmer-dsl-web.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer-dsl-web 0.7.
|
5
|
+
# stub: glimmer-dsl-web 0.7.3 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer-dsl-web".freeze
|
9
|
-
s.version = "0.7.
|
9
|
+
s.version = "0.7.3".freeze
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
@@ -33,6 +33,8 @@ Gem::Specification.new do |s|
|
|
33
33
|
"lib/glimmer-dsl-web/ext/kernel.rb",
|
34
34
|
"lib/glimmer-dsl-web/samples/hello/hello_button.rb",
|
35
35
|
"lib/glimmer-dsl-web/samples/hello/hello_component.rb",
|
36
|
+
"lib/glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb",
|
37
|
+
"lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb",
|
36
38
|
"lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb",
|
37
39
|
"lib/glimmer-dsl-web/samples/hello/hello_component_listeners_default_slot.rb",
|
38
40
|
"lib/glimmer-dsl-web/samples/hello/hello_component_slots.rb",
|
@@ -0,0 +1,127 @@
|
|
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
|
+
unless Object.const_defined?(:AddressTypePresenter)
|
25
|
+
class AddressTypePresenter
|
26
|
+
attr_accessor :address_types, :selected_address_type
|
27
|
+
|
28
|
+
def initialize(address_types:, selected_address_type: nil)
|
29
|
+
@address_types = address_types
|
30
|
+
@selected_address_type = selected_address_type
|
31
|
+
@selected_address_type ||= address_types.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
unless Object.const_defined?(:AddressTypeSelect)
|
37
|
+
class AddressTypeSelect
|
38
|
+
include Glimmer::Web::Component
|
39
|
+
|
40
|
+
attribute :address_types, default: []
|
41
|
+
attribute :selected_address_type
|
42
|
+
|
43
|
+
markup {
|
44
|
+
select { |select_element|
|
45
|
+
address_types.each do |address_type|
|
46
|
+
option(value: address_type) { address_type }
|
47
|
+
end
|
48
|
+
|
49
|
+
value <=> [self, :selected_address_type]
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
style {
|
54
|
+
r(component_element_selector) {
|
55
|
+
font_size 2.em
|
56
|
+
margin_left 5.px
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
unless Object.const_defined?(:AddressTypeRadioGroup)
|
63
|
+
class AddressTypeRadioGroup
|
64
|
+
include Glimmer::Web::Component
|
65
|
+
|
66
|
+
attribute :address_types, default: []
|
67
|
+
attribute :selected_address_type
|
68
|
+
|
69
|
+
markup {
|
70
|
+
div { |select_element|
|
71
|
+
address_types.each do |address_type|
|
72
|
+
input_id = "radio_address_type_#{address_type}"
|
73
|
+
input(id: input_id, type: 'radio', name: 'radio_address_type', value: address_type) {
|
74
|
+
checked <=> [self, :selected_address_type,
|
75
|
+
on_read: ->(address_type_string_value) { address_type_string_value == address_type },
|
76
|
+
on_write: ->(radio_input_boolean_value) { radio_input_boolean_value ? address_type : selected_address_type },
|
77
|
+
]
|
78
|
+
}
|
79
|
+
label(for: input_id) { address_type }
|
80
|
+
end
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
style {
|
85
|
+
r(component_element_selector) {
|
86
|
+
font_size 2.em
|
87
|
+
margin_top 10.px
|
88
|
+
}
|
89
|
+
r("#{component_element_selector} label") {
|
90
|
+
margin_left 2.px
|
91
|
+
margin_right 10.px
|
92
|
+
}
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
unless Object.const_defined?(:AddressTypeComponentAttributeDataBindingPage)
|
98
|
+
class AddressTypeComponentAttributeDataBindingPage
|
99
|
+
include Glimmer::Web::Component
|
100
|
+
|
101
|
+
before_render do
|
102
|
+
@presenter = AddressTypePresenter.new(address_types: ['Home', 'Work', 'Other'])
|
103
|
+
end
|
104
|
+
|
105
|
+
markup {
|
106
|
+
div {
|
107
|
+
h1('Address type for delivery:')
|
108
|
+
|
109
|
+
address_type_select(address_types: @presenter.address_types, selected_address_type: @presenter.selected_address_type) {
|
110
|
+
# Not only can we data-bind attributes on basic HTML elements,
|
111
|
+
# but we can also data-bind custom attributes on Glimmer Web Components
|
112
|
+
selected_address_type <=> [@presenter, :selected_address_type]
|
113
|
+
}
|
114
|
+
|
115
|
+
address_type_radio_group(address_types: @presenter.address_types, selected_address_type: @presenter.selected_address_type) {
|
116
|
+
# Not only can we data-bind attributes on basic HTML elements,
|
117
|
+
# but we can also data-bind custom attributes on Glimmer Web Components
|
118
|
+
selected_address_type <=> [@presenter, :selected_address_type]
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Document.ready? do
|
126
|
+
AddressTypeComponentAttributeDataBindingPage.render
|
127
|
+
end
|
@@ -0,0 +1,81 @@
|
|
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
|
+
unless Object.const_defined?(:AddressTypeSelector)
|
25
|
+
class AddressTypeSelector
|
26
|
+
include Glimmer::Web::Component
|
27
|
+
|
28
|
+
attribute :address_types, default: []
|
29
|
+
attribute :selected_address_type
|
30
|
+
|
31
|
+
before_render do
|
32
|
+
self.selected_address_type ||= address_types.first
|
33
|
+
end
|
34
|
+
|
35
|
+
markup {
|
36
|
+
select(placeholder: 'Select an address type') { |select_element|
|
37
|
+
address_types.each do |address_type|
|
38
|
+
option(value: address_type) { address_type }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Bidirectionally data-bind select value to selected_address_type attribute on self (component)
|
42
|
+
value <=> [self, :selected_address_type]
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
style {
|
47
|
+
r(component_element_selector) {
|
48
|
+
font_size 2.em
|
49
|
+
margin_left 5.px
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
unless Object.const_defined?(:AddressTypeSelectorPage)
|
56
|
+
class AddressTypeSelectorPage
|
57
|
+
include Glimmer::Web::Component
|
58
|
+
|
59
|
+
markup {
|
60
|
+
div {
|
61
|
+
h1('Address type for delivery:', style: {display: :inline})
|
62
|
+
|
63
|
+
address_type_selector(address_types: ['Home', 'Work', 'Other']) {
|
64
|
+
# We can listen to the updates of any attribute/option in a Glimmer Web Component
|
65
|
+
# on_{attribute_name}_update do execute code when component attribute/option with attribute_name is updated
|
66
|
+
# This is an alternative to using Component Listeners, which require that the component explicitly calls notify_listeners,
|
67
|
+
# whereas Component Attribute Listeners get tracked automatically, but depend on a specific attribute
|
68
|
+
# The trade-off is Component Listeners provide more flexibility when needed as they are not bound to specific attributes,
|
69
|
+
# but often Component Attribute Listeners are good enough as a solution for certain problems.
|
70
|
+
on_selected_address_type_update do |address_type|
|
71
|
+
$$.alert("You selected the address type: #{address_type}")
|
72
|
+
end
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Document.ready? do
|
80
|
+
AddressTypeSelectorPage.render
|
81
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
@@ -266,6 +266,8 @@ files:
|
|
266
266
|
- lib/glimmer-dsl-web/ext/kernel.rb
|
267
267
|
- lib/glimmer-dsl-web/samples/hello/hello_button.rb
|
268
268
|
- lib/glimmer-dsl-web/samples/hello/hello_component.rb
|
269
|
+
- lib/glimmer-dsl-web/samples/hello/hello_component_attribute_data_binding.rb
|
270
|
+
- lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb
|
269
271
|
- lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb
|
270
272
|
- lib/glimmer-dsl-web/samples/hello/hello_component_listeners_default_slot.rb
|
271
273
|
- lib/glimmer-dsl-web/samples/hello/hello_component_slots.rb
|