glimmer-dsl-web 0.7.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fa66088970372c55644a8ffe63929208eb2ff33f70210fbb37c9df47c640553
4
- data.tar.gz: 3322387162239687f62b4dd3ea7c1d1d34d52bda6e1242960dd9e6e7d8cf0f0a
3
+ metadata.gz: 6cdfd633318946532548053f4ab89f31e423280ae73ba9505b598b0e60695680
4
+ data.tar.gz: e95ac60a8077a1157abaf6da13cf0f7adab1f3f72fb727bb9cf270ae373cab2e
5
5
  SHA512:
6
- metadata.gz: 6cd62b7128c8da53cb68a359ed633724439671877e485c17bda919fca70f7e5d80c10fda3720072b9f7e6fdcd5c1f6fb0ec1652539fa9e864234779ef5bea267
7
- data.tar.gz: 0e2c789e2eb779f9fb5e0a70bc22da3ece42d0e9c1efcb77fd965a39c93e380142ca082b131fada732e25ce4b22ef131f5292af91d9cfe94cd7e19c2ce7229b9
6
+ metadata.gz: c77c3cc7a2eabae100de6dd48e5797f8ce28774c4da17e6324bfe61fd8ece5d3e7b811538eabef1989f31de589e2ce64a1bbf122998c963158e844f33ca73987
7
+ data.tar.gz: a9f1a3ca8a39beae60602248b395621cadf2ae15362be300ff34f2d1d7d9c1464dcdab0614f31838388ae7a3e556abf0437f372642566827bd2ec72ba831934d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
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
+
3
7
  ## 0.7.2
4
8
 
5
9
  - Hello, Component Attribute Listeners! Sample: `require 'glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb'`
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.2 (Beta)
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!!!
@@ -1353,6 +1353,7 @@ Learn more about the differences between various [Glimmer](https://github.com/An
1353
1353
  - [Hello, Component Listeners!](#hello-component-listeners)
1354
1354
  - [Hello, Component Listeners (Default Slot)!](#hello-component-listeners-default-slot)
1355
1355
  - [Hello, Component Attribute Listeners!](#hello-component-attribute-listeners)
1356
+ - [Hello, Component Attribute Data-Binding!](#hello-component-attribute-data-binding)
1356
1357
  - [Hello, glimmer_component Rails Helper!](#hello-glimmer_component-rails-helper)
1357
1358
  - [Hello, Paragraph!](#hello-paragraph)
1358
1359
  - [Hello, Style!](#hello-style)
@@ -1417,7 +1418,7 @@ rails new glimmer_app_server
1417
1418
  Add the following to `Gemfile`:
1418
1419
 
1419
1420
  ```
1420
- gem 'glimmer-dsl-web', '~> 0.7.2'
1421
+ gem 'glimmer-dsl-web', '~> 0.7.3'
1421
1422
  ```
1422
1423
 
1423
1424
  Run:
@@ -2995,367 +2996,357 @@ Glimmer HTML DSL Ruby code in the frontend:
2995
2996
  ```ruby
2996
2997
  require 'glimmer-dsl-web'
2997
2998
 
2998
- unless Object.const_defined?(:Address)
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
- }
3057
-
3058
- def state_code
3059
- STATES.invert[state]
3060
- end
3061
-
3062
- def state_code=(value)
3063
- self.state = STATES[value]
3064
- 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
+ }
3065
3057
 
3066
- def summary
3067
- to_h.values.map(&:to_s).reject(&:empty?).join(', ')
3068
- end
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(', ')
3069
3068
  end
3070
3069
  end
3071
3070
 
3072
- unless Object.const_defined?(:AddressForm)
3073
- # AddressForm Glimmer Web Component (View component)
3074
- #
3075
- # Including Glimmer::Web::Component makes this class a View component and automatically
3076
- # generates a new Glimmer HTML DSL keyword that matches the lowercase underscored version
3077
- # of the name of the class. AddressForm generates address_form keyword, which can be used
3078
- # elsewhere in Glimmer HTML DSL code as done inside HelloComponentListeners below.
3079
- class AddressForm
3080
- include Glimmer::Web::Component
3081
-
3082
- option :address
3083
-
3084
- markup {
3085
- div {
3086
- div(style: {display: :grid, grid_auto_columns: '80px 260px'}) { |address_div|
3087
- label('Full Name: ', for: 'full-name-field')
3088
- input(id: 'full-name-field') {
3089
- value <=> [address, :full_name]
3090
- }
3091
-
3092
- label('Street: ', for: 'street-field')
3093
- input(id: 'street-field') {
3094
- value <=> [address, :street]
3095
- }
3096
-
3097
- label('Street 2: ', for: 'street2-field')
3098
- textarea(id: 'street2-field') {
3099
- value <=> [address, :street2]
3100
- }
3101
-
3102
- label('City: ', for: 'city-field')
3103
- input(id: 'city-field') {
3104
- value <=> [address, :city]
3105
- }
3106
-
3107
- label('State: ', for: 'state-field')
3108
- select(id: 'state-field') {
3109
- Address::STATES.each do |state_code, state|
3110
- option(value: state_code) { state }
3111
- 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
3112
3079
 
3113
- value <=> [address, :state_code]
3114
- }
3115
-
3116
- label('Zip Code: ', for: 'zip-code-field')
3117
- input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
3118
- value <=> [address, :zip_code,
3119
- on_write: :to_s,
3120
- ]
3121
- }
3122
-
3123
- style {
3124
- r("#{address_div.selector} *") {
3125
- margin '5px'
3126
- }
3127
- r("#{address_div.selector} input, #{address_div.selector} select") {
3128
- grid_column '2'
3129
- }
3130
- }
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]
3131
3088
  }
3132
3089
 
3133
- div(style: {margin: 5}) {
3134
- inner_text <= [address, :summary,
3135
- computed_by: address.members + ['state_code'],
3136
- ]
3090
+ label('Street: ', for: 'street-field')
3091
+ input(id: 'street-field') {
3092
+ value <=> [address, :street]
3137
3093
  }
3138
- }
3139
- }
3140
- end
3141
- end
3142
-
3143
- unless Object.const_defined?(:AccordionSection)
3144
- class AccordionSection
3145
- class Presenter
3146
- attr_accessor :collapsed, :instant_transition
3147
-
3148
- def toggle_collapsed(instant: false)
3149
- self.instant_transition = instant
3150
- self.collapsed = !collapsed
3151
- end
3152
-
3153
- def expand(instant: false)
3154
- self.instant_transition = instant
3155
- self.collapsed = false
3156
- end
3157
-
3158
- def collapse(instant: false)
3159
- self.instant_transition = instant
3160
- self.collapsed = true
3161
- end
3162
- end
3163
-
3164
- include Glimmer::Web::Component
3165
-
3166
- events :expanded, :collapsed
3167
-
3168
- option :title
3169
-
3170
- attr_reader :presenter
3171
-
3172
- before_render do
3173
- @presenter = Presenter.new
3174
- end
3175
-
3176
- markup {
3177
- section {
3178
- # Unidirectionally data-bind the class inclusion of 'collapsed' to the @presenter.collapsed boolean attribute,
3179
- # meaning if @presenter.collapsed changes to true, the CSS class 'collapsed' is included on the element,
3180
- # and if it changes to false, the CSS class 'collapsed' is removed from the element.
3181
- class_name(:collapsed) <= [@presenter, :collapsed]
3182
- class_name(:instant_transition) <= [@presenter, :instant_transition]
3183
-
3184
- header(title, class: 'accordion-section-title') {
3185
- onclick do |event|
3186
- @presenter.toggle_collapsed
3187
- if @presenter.collapsed
3188
- notify_listeners(:collapsed)
3189
- else
3190
- notify_listeners(:expanded)
3191
- 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 }
3192
3109
  end
3110
+
3111
+ value <=> [address, :state_code]
3193
3112
  }
3194
3113
 
3195
- div(slot: :section_content, class: 'accordion-section-content')
3196
- }
3197
- }
3198
-
3199
- style {
3200
- r('.accordion-section-title') {
3201
- font_size 2.em
3202
- font_weight :bold
3203
- cursor :pointer
3204
- padding_left 20
3205
- position :relative
3206
- margin_block_start 0.33.em
3207
- margin_block_end 0.33.em
3208
- }
3209
-
3210
- r('.accordion-section-title::before') {
3211
- content '"▼"'
3212
- position :absolute
3213
- font_size 0.5.em
3214
- top 10
3215
- left 0
3216
- }
3217
-
3218
- r('.accordion-section-content') {
3219
- height 246
3220
- overflow :hidden
3221
- transition 'height 0.5s linear'
3222
- }
3223
-
3224
- r("#{component_element_selector}.instant_transition .accordion-section-content") {
3225
- transition 'initial'
3226
- }
3227
-
3228
- r("#{component_element_selector}.collapsed .accordion-section-title::before") {
3229
- 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
+ }
3230
3129
  }
3231
3130
 
3232
- r("#{component_element_selector}.collapsed .accordion-section-content") {
3233
- height 0
3131
+ div(style: {margin: 5}) {
3132
+ inner_text <= [address, :summary,
3133
+ computed_by: address.members + ['state_code'],
3134
+ ]
3234
3135
  }
3235
3136
  }
3236
- end
3137
+ }
3237
3138
  end
3238
3139
 
3239
- unless Object.const_defined?(:Accordion)
3240
- class Accordion
3241
- include Glimmer::Web::Component
3140
+ class AccordionSection
3141
+ class Presenter
3142
+ attr_accessor :collapsed, :instant_transition
3242
3143
 
3243
- events :accordion_section_expanded, :accordion_section_collapsed
3144
+ def toggle_collapsed(instant: false)
3145
+ self.instant_transition = instant
3146
+ self.collapsed = !collapsed
3147
+ end
3244
3148
 
3245
- markup {
3246
- # given that no slots are specified, nesting content under the accordion component
3247
- # in consumer code adds content directly inside the markup root div.
3248
- div { |accordion|
3249
- # on render, all accordion sections would have been added by consumers already, so we can
3250
- # attach listeners to all of them by re-opening their content with `.content { ... }` block
3251
- on_render do
3252
- accordion_section_elements = accordion.children
3253
- accordion_sections = accordion_section_elements.map(&:component)
3254
- accordion_sections.each_with_index do |accordion_section, index|
3255
- accordion_section_number = index + 1
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
3256
3161
 
3257
- # ensure only the first section is expanded
3258
- accordion_section.presenter.collapse(instant: true) if accordion_section_number != 1
3162
+ events :expanded, :collapsed
3259
3163
 
3260
- accordion_section.content {
3261
- on_expanded do
3262
- other_accordion_sections = accordion_sections.reject {|other_accordion_section| other_accordion_section == accordion_section }
3263
- other_accordion_sections.each { |other_accordion_section| other_accordion_section.presenter.collapse }
3264
- notify_listeners(:accordion_section_expanded, accordion_section_number)
3265
- end
3164
+ option :title
3266
3165
 
3267
- on_collapsed do
3268
- notify_listeners(:accordion_section_collapsed, accordion_section_number)
3269
- end
3270
- }
3166
+ attr_reader :presenter
3167
+
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)
3271
3187
  end
3272
3188
  end
3273
3189
  }
3190
+
3191
+ div(slot: :section_content, class: 'accordion-section-content')
3274
3192
  }
3275
- end
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
+ }
3276
3232
  end
3277
3233
 
3278
- unless Object.const_defined?(:HelloComponentListeners)
3279
- # HelloComponentListeners Glimmer Web Component (View component)
3280
- #
3281
- # This View component represents the main page being rendered,
3282
- # as done by its `render` class method below
3283
- class HelloComponentListeners
3284
- class Presenter
3285
- attr_accessor :status_message
3286
-
3287
- def initialize
3288
- @status_message = "Accordion section 1 is expanded!"
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
3289
3266
  end
3290
- end
3291
-
3292
- include Glimmer::Web::Component
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
3293
3278
 
3294
- before_render do
3295
- @presenter = Presenter.new
3296
- @shipping_address = Address.new(
3297
- full_name: 'Johnny Doe',
3298
- street: '3922 Park Ave',
3299
- street2: 'PO BOX 8382',
3300
- city: 'San Diego',
3301
- state: 'California',
3302
- zip_code: '91913',
3303
- )
3304
- @billing_address = Address.new(
3305
- full_name: 'John C Doe',
3306
- street: '123 Main St',
3307
- street2: 'Apartment 3C',
3308
- city: 'San Diego',
3309
- state: 'California',
3310
- zip_code: '91911',
3311
- )
3312
- @emergency_address = Address.new(
3313
- full_name: 'Mary Doe',
3314
- street: '2038 Ipswitch St',
3315
- street2: 'Suite 300',
3316
- city: 'San Diego',
3317
- state: 'California',
3318
- zip_code: '91912',
3319
- )
3279
+ def initialize
3280
+ @status_message = "Accordion section 1 is expanded!"
3320
3281
  end
3321
-
3322
- markup {
3323
- div {
3324
- h1(style: {font_style: :italic}) {
3325
- inner_html <= [@presenter, :status_message]
3326
- }
3327
-
3328
- accordion { # any content nested under component directly is added under its markup root div element
3329
- accordion_section(title: 'Shipping Address') {
3330
- section_content { # contribute elements to section_content slot declared in AccordionSection component
3331
- address_form(address: @shipping_address)
3332
- }
3333
- }
3334
-
3335
- accordion_section(title: 'Billing Address') {
3336
- section_content {
3337
- address_form(address: @billing_address)
3338
- }
3339
- }
3340
-
3341
- accordion_section(title: 'Emergency Address') {
3342
- section_content {
3343
- address_form(address: @emergency_address)
3344
- }
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)
3345
3324
  }
3346
-
3347
- # on_accordion_section_expanded listener matches event :accordion_section_expanded declared in Accordion component
3348
- on_accordion_section_expanded { |accordion_section_number|
3349
- @presenter.status_message = "Accordion section #{accordion_section_number} is expanded!"
3325
+ }
3326
+
3327
+ accordion_section(title: 'Billing Address') {
3328
+ section_content {
3329
+ address_form(address: @billing_address)
3350
3330
  }
3351
-
3352
- on_accordion_section_collapsed { |accordion_section_number|
3353
- @presenter.status_message = "Accordion section #{accordion_section_number} is collapsed!"
3331
+ }
3332
+
3333
+ accordion_section(title: 'Emergency Address') {
3334
+ section_content {
3335
+ address_form(address: @emergency_address)
3354
3336
  }
3355
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
+ }
3356
3347
  }
3357
3348
  }
3358
- end
3349
+ }
3359
3350
  end
3360
3351
 
3361
3352
  Document.ready? do
@@ -3768,59 +3759,55 @@ Glimmer HTML DSL Ruby code in the frontend:
3768
3759
  ```ruby
3769
3760
  require 'glimmer-dsl-web'
3770
3761
 
3771
- unless Object.const_defined?(:AddressTypeSelector)
3772
- class AddressTypeSelector
3773
- include Glimmer::Web::Component
3774
-
3775
- attribute :address_types, default: []
3776
- attribute :selected_address_type
3777
-
3778
- before_render do
3779
- self.selected_address_type ||= address_types.first
3780
- end
3781
-
3782
- markup {
3783
- select(placeholder: 'Select an address type') { |select_element|
3784
- address_types.each do |address_type|
3785
- option(value: address_type) { address_type }
3786
- end
3787
-
3788
- # Bidirectionally data-bind select value to selected_address_type attribute on self (component)
3789
- value <=> [self, :selected_address_type]
3790
- }
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]
3791
3780
  }
3792
-
3793
- style {
3794
- r(component_element_selector) {
3795
- font_size 2.em
3796
- margin_left 5.px
3797
- }
3781
+ }
3782
+
3783
+ style {
3784
+ r(component_element_selector) {
3785
+ font_size 2.em
3786
+ margin_left 5.px
3798
3787
  }
3799
- end
3788
+ }
3800
3789
  end
3801
3790
 
3802
- unless Object.const_defined?(:AddressTypeSelectorPage)
3803
- class AddressTypeSelectorPage
3804
- include Glimmer::Web::Component
3805
-
3806
- markup {
3807
- div {
3808
- h1('Address type for delivery:', style: {display: :inline})
3809
-
3810
- address_type_selector(address_types: ['Home', 'Work', 'Other']) {
3811
- # We can listen to the updates of any attribute/option in a Glimmer Web Component
3812
- # on_{attribute_name}_update do execute code when component attribute/option with attribute_name is updated
3813
- # This is an alternative to using Component Listeners, which require that the component explicitly calls notify_listeners,
3814
- # whereas Component Attribute Listeners get tracked automatically, but depend on a specific attribute
3815
- # The trade-off is Component Listeners provide more flexibility when needed as they are not bound to specific attributes,
3816
- # but often Component Attribute Listeners are good enough as a solution for certain problems.
3817
- on_selected_address_type_update do |address_type|
3818
- $$.alert("You selected the address type: #{address_type}")
3819
- end
3820
- }
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
3821
3808
  }
3822
3809
  }
3823
- end
3810
+ }
3824
3811
  end
3825
3812
 
3826
3813
  Document.ready? do
@@ -3836,6 +3823,115 @@ Screenshot:
3836
3823
 
3837
3824
  ![Hello, Component Attribute Listeners Selected Address Type Dialog!](/images/glimmer-dsl-web-samples-hello-hello-component-attribute-listeners-selected-address-type-dialog.png)
3838
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
+ ![Hello, Component Attribute Data-Binding!](/images/glimmer-dsl-web-samples-hello-hello-component-attribute-data-binding.gif)
3934
+
3839
3935
  #### Hello, glimmer_component Rails Helper!
3840
3936
 
3841
3937
  You may insert a Glimmer component anywhere into a Rails View using
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.2
1
+ 0.7.3
@@ -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.2 ruby lib
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.2".freeze
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,7 @@ 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",
36
37
  "lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb",
37
38
  "lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb",
38
39
  "lib/glimmer-dsl-web/samples/hello/hello_component_listeners_default_slot.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
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.2
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
@@ -266,6 +266,7 @@ 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
269
270
  - lib/glimmer-dsl-web/samples/hello/hello_component_attribute_listeners.rb
270
271
  - lib/glimmer-dsl-web/samples/hello/hello_component_listeners.rb
271
272
  - lib/glimmer-dsl-web/samples/hello/hello_component_listeners_default_slot.rb