glimmer-dsl-web 0.10.2 → 0.10.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: aad28a356df583d4a4784a532111d9e4d4f3743f8855998deb69ff03521a0107
4
- data.tar.gz: 79c3a03baea98bd0774d95e4dbd049e30ddde606402b17770b037cffcbdb63ce
3
+ metadata.gz: 9612f4c1844bb9ccf62e32d1682a71bb933268fc9e9a74837a89a558001b96b0
4
+ data.tar.gz: c6bffc8d775f74c96f13cad87702648f2da115fd7da7de998946a7dc33572410
5
5
  SHA512:
6
- metadata.gz: 427e0f03396027917230ad86fbfbfc258253b31044cadb32718caab75de8230edf22c5dc628a561db08c9b1bb82729e9e8347eef097ccb67d5e823fbcf90686f
7
- data.tar.gz: e6f98162f1ab9a278547bf21d73cd861606b7595dc8ddd86cddf6901cc3b4f0d2e24af477bcbaf3bbaf870898abaa0901696e526ff040682bcede3cd595d3e07
6
+ metadata.gz: 5747a2f6092a03c2c0ae35c666f615aca23eac99b6b98eed533bf2876e747ca97bb33bd6a2f89285f34dcd0520873c2538e85306149cfbe5184fb921e4128928
7
+ data.tar.gz: 381d39d680af48941b8282aa180e4f6cd6641f7c79e408739c4e2e4838507f7e084011408f59dc09c5183c39912ec8b1cf6abbdadcff0f0587ec6d9151104d82
data/CHANGELOG.md CHANGED
@@ -1,8 +1,15 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.10.3
4
+
5
+ - Support `parent.prepend {}` on `ElementProxy` and `Glimmer::Web::Component`, which is a method that prepends elements to the content of a parent element
6
+ - Support `element.focused` & `element.focused=` methods, including `focused` data-binding support. Note that setting `element.focused=` as `true` gives an element focus, but setting it to `false` does not change focus (you would have to set another element as focused to remove focus from a previous element).
7
+ - Hello, Mutation! sample: `require 'glimmer-dsl-web/samples/hello/hello_mutation.rb'`
8
+ - Hello, Mutation Content Data-Binding! sample: `require 'glimmer-dsl-web/samples/hello/hello_mutation_content_data_binding.rb'`
9
+
3
10
  ## 0.10.2
4
11
 
5
- - Support `ElementProxy#append {}`/`Glimmer::Web::Component#append {}` as an alias for `content {}` to enable appending elements to the content of a parent element
12
+ - Support `parent.append {}` as an alias for `content {}` on `ElementProxy` and `Glimmer::Web::Component`, which is a method that appends elements to the content of a parent element
6
13
 
7
14
  ## 0.10.1
8
15
 
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.10.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.10.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!!!
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.2
1
+ 0.10.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.10.2 ruby lib
5
+ # stub: glimmer-dsl-web 0.10.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-web".freeze
9
- s.version = "0.10.2".freeze
9
+ s.version = "0.10.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]
@@ -50,6 +50,8 @@ Gem::Specification.new do |s|
50
50
  "lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb",
51
51
  "lib/glimmer-dsl-web/samples/hello/hello_input_date_time_data_binding.rb",
52
52
  "lib/glimmer-dsl-web/samples/hello/hello_modal.rb",
53
+ "lib/glimmer-dsl-web/samples/hello/hello_mutation.rb",
54
+ "lib/glimmer-dsl-web/samples/hello/hello_mutation_content_data_binding.rb",
53
55
  "lib/glimmer-dsl-web/samples/hello/hello_observer.rb",
54
56
  "lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb",
55
57
  "lib/glimmer-dsl-web/samples/hello/hello_page_component_link/conference_talk_page.rb",
@@ -12,6 +12,9 @@ module Glimmer
12
12
  end
13
13
 
14
14
  def add_content(parent, keyword, *args, &block)
15
+ options = args.last.is_a?(Hash) ? args.last : {}
16
+ parent_original_add_child_mutation = parent&.add_child_mutation
17
+ parent&.add_child_mutation = options[:add_child_mutation] if options[:add_child_mutation]
15
18
  if parent.bulk_render? || parent.rendered? || parent.skip_content_on_render_blocks?
16
19
  return_value = super(parent, keyword, *args, &block)
17
20
  parent.add_text_content(return_value, on_empty: true) if return_value.is_a?(String)
@@ -20,6 +23,8 @@ module Glimmer
20
23
  else
21
24
  parent.add_content_on_render(&block)
22
25
  end
26
+ ensure
27
+ parent&.add_child_mutation = parent_original_add_child_mutation
23
28
  end
24
29
  end
25
30
  end
@@ -512,11 +512,23 @@ module Glimmer
512
512
  end
513
513
  alias append content
514
514
 
515
+ def prepend(bulk_render: false, &block)
516
+ @markup_root.prepend(bulk_render:, &block)
517
+ end
518
+
515
519
  def remove_all_listeners
516
520
  data_bindings.each do |option_binding, model_binding|
517
521
  option_binding.unregister_all_observables
518
522
  end
519
523
  end
524
+
525
+ def add_child_mutation
526
+ markup_root.add_child_mutation
527
+ end
528
+
529
+ def add_child_mutation=(value)
530
+ markup_root.add_child_mutation = value
531
+ end
520
532
 
521
533
  def method_missing(method_name, *args, &block)
522
534
  if can_handle_observation_request?(method_name)
@@ -445,7 +445,7 @@ module Glimmer
445
445
  brand_new ||= @dom.nil? || !options[:parent].to_s.empty? || (old_element = dom_element).empty?
446
446
  build_dom(layout: !custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
447
447
  if brand_new
448
- attach(the_parent_dom_element)
448
+ attach(the_parent_dom_element, add_child_mutation: self.parent&.add_child_mutation || :append)
449
449
  else
450
450
  reattach(old_element)
451
451
  end
@@ -458,14 +458,29 @@ module Glimmer
458
458
  end
459
459
  alias rerender render
460
460
 
461
- def attach(the_parent_dom_element)
462
- the_parent_dom_element.append(@dom)
461
+ def attach(the_parent_dom_element, add_child_mutation: :append)
462
+ if add_child_mutation == :prepend
463
+ the_parent_dom_element.prepend(@dom)
464
+ else
465
+ the_parent_dom_element.append(@dom)
466
+ end
463
467
  end
464
468
 
465
469
  def reattach(old_element)
466
470
  old_element.replace_with(@dom)
467
471
  end
468
472
 
473
+ def focused
474
+ @focused = `#{dom_element}[0] == document.activeElement`
475
+ @focused
476
+ end
477
+
478
+ def focused=(value)
479
+ @focused = value
480
+ focus if @focused
481
+ @focused
482
+ end
483
+
469
484
  def mark_rendered
470
485
  @rendered = true
471
486
  children.each(&:mark_rendered) if bulk_render?
@@ -545,15 +560,27 @@ module Glimmer
545
560
  html_options
546
561
  end
547
562
 
548
- def content(bulk_render: false, &block)
563
+ def add_child_mutation
564
+ @add_child_mutation || :append
565
+ end
566
+
567
+ def add_child_mutation=(value)
568
+ @add_child_mutation = value
569
+ end
570
+
571
+ def content(bulk_render: false, add_child_mutation: :append, &block)
549
572
  original_bulk_render = options[:bulk_render]
550
573
  options[:bulk_render] = bulk_render if rendered?
551
- return_value = Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, &block)
574
+ return_value = Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Web::ElementExpression.new, keyword, add_child_mutation:, &block)
552
575
  options[:bulk_render] = original_bulk_render if rendered?
553
576
  return_value
554
577
  end
555
578
  alias append content
556
579
 
580
+ def prepend(bulk_render: false, &block)
581
+ content(bulk_render:, add_child_mutation: :prepend, &block)
582
+ end
583
+
557
584
  # Subclasses must override with their own mappings
558
585
  def observation_request_to_event_mapping
559
586
  {}
@@ -38,131 +38,135 @@ end
38
38
  # generates a new Glimmer HTML DSL keyword that matches the lowercase underscored version
39
39
  # of the name of the class. AddressForm generates address_form_with_slots keyword, which can be used
40
40
  # elsewhere in Glimmer HTML DSL code as done inside HelloComponentSlots below.
41
- class AddressFormWithSlots
42
- include Glimmer::Web::Component
43
-
44
- option :address
45
-
46
- # markup block provides the content of the
47
- markup {
48
- div {
49
- # designate this div as a slot with the slot name :address_header to enable
50
- # consumers to contribute elements to `address_header {...}` slot
51
- div(slot: :address_header, class: 'address-form-header')
52
-
53
- div(class: 'address-field-container', style: {display: :grid, grid_auto_columns: '80px 260px'}) {
54
- label('Full Name: ', for: 'full-name-field')
55
- input(id: 'full-name-field') {
56
- value <=> [address, :full_name]
57
- }
58
-
59
- @somelabel = label('Street: ', for: 'street-field')
60
- input(id: 'street-field') {
61
- value <=> [address, :street]
62
- }
63
-
64
- label('Street 2: ', for: 'street2-field')
65
- textarea(id: 'street2-field') {
66
- value <=> [address, :street2]
67
- }
41
+ unless Object.const_defined?(:AddressFormWithSlots)
42
+ class AddressFormWithSlots
43
+ include Glimmer::Web::Component
44
+
45
+ option :address
46
+
47
+ # markup block provides the content of the
48
+ markup {
49
+ div {
50
+ # designate this div as a slot with the slot name :address_header to enable
51
+ # consumers to contribute elements to `address_header {...}` slot
52
+ div(slot: :address_header, class: 'address-form-header')
68
53
 
69
- label('City: ', for: 'city-field')
70
- input(id: 'city-field') {
71
- value <=> [address, :city]
54
+ div(class: 'address-field-container', style: {display: :grid, grid_auto_columns: '80px 260px'}) {
55
+ label('Full Name: ', for: 'full-name-field')
56
+ input(id: 'full-name-field') {
57
+ value <=> [address, :full_name]
58
+ }
59
+
60
+ @somelabel = label('Street: ', for: 'street-field')
61
+ input(id: 'street-field') {
62
+ value <=> [address, :street]
63
+ }
64
+
65
+ label('Street 2: ', for: 'street2-field')
66
+ textarea(id: 'street2-field') {
67
+ value <=> [address, :street2]
68
+ }
69
+
70
+ label('City: ', for: 'city-field')
71
+ input(id: 'city-field') {
72
+ value <=> [address, :city]
73
+ }
74
+
75
+ label('State: ', for: 'state-field')
76
+ select(id: 'state-field') {
77
+ Address::STATES.each do |state_code, state|
78
+ option(value: state_code) { state }
79
+ end
80
+
81
+ value <=> [address, :state_code]
82
+ }
83
+
84
+ label('Zip Code: ', for: 'zip-code-field')
85
+ input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
86
+ value <=> [address, :zip_code,
87
+ on_write: :to_s,
88
+ ]
89
+ }
72
90
  }
73
91
 
74
- label('State: ', for: 'state-field')
75
- select(id: 'state-field') {
76
- Address::STATES.each do |state_code, state|
77
- option(value: state_code) { state }
78
- end
79
-
80
- value <=> [address, :state_code]
92
+ div(style: {margin: 5}) {
93
+ inner_text <= [address, :summary,
94
+ computed_by: address.members + ['state_code'],
95
+ ]
81
96
  }
82
97
 
83
- label('Zip Code: ', for: 'zip-code-field')
84
- input(id: 'zip-code-field', type: 'number', min: '0', max: '99999') {
85
- value <=> [address, :zip_code,
86
- on_write: :to_s,
87
- ]
88
- }
89
- }
90
-
91
- div(style: {margin: 5}) {
92
- inner_text <= [address, :summary,
93
- computed_by: address.members + ['state_code'],
94
- ]
98
+ # designate this div as a slot with the slot name :address_footer to enable
99
+ # consumers to contribute elements to `address_footer {...}` slot
100
+ div(slot: :address_footer, class: 'address-form-footer')
95
101
  }
96
-
97
- # designate this div as a slot with the slot name :address_footer to enable
98
- # consumers to contribute elements to `address_footer {...}` slot
99
- div(slot: :address_footer, class: 'address-form-footer')
100
- }
101
- }
102
-
103
- style {
104
- r('.address-field-container *') {
105
- margin 5
106
102
  }
107
- r('.address-field-container input, .address-field-container select') {
108
- grid_column '2'
103
+
104
+ style {
105
+ r('.address-field-container *') {
106
+ margin 5
107
+ }
108
+ r('.address-field-container input, .address-field-container select') {
109
+ grid_column '2'
110
+ }
109
111
  }
110
- }
112
+ end
111
113
  end
112
114
 
113
115
  # HelloComponentSlots Glimmer Web Component (View component)
114
116
  #
115
117
  # This View component represents the main page being rendered,
116
118
  # as done by its `render` class method below
117
- class HelloComponentSlots
118
- include Glimmer::Web::Component
119
-
120
- before_render do
121
- @shipping_address = Address.new(
122
- full_name: 'Johnny Doe',
123
- street: '3922 Park Ave',
124
- street2: 'PO BOX 8382',
125
- city: 'San Diego',
126
- state: 'California',
127
- zip_code: '91913',
128
- )
129
- @billing_address = Address.new(
130
- full_name: 'John C Doe',
131
- street: '123 Main St',
132
- street2: 'Apartment 3C',
133
- city: 'San Diego',
134
- state: 'California',
135
- zip_code: '91911',
136
- )
137
- end
138
-
139
- markup {
140
- div {
141
- address_form_with_slots(address: @shipping_address) {
142
- address_header { # contribute elements to the address_header component slot
143
- h1('Shipping Address')
144
- legend('This is the address that is used for shipping your purchase.', style: {margin_bottom: 10})
145
- }
146
- address_footer { # contribute elements to the address_footer component slot
147
- p {
148
- sub("#{strong('Note:')} #{em('Purchase will be returned if the Shipping Address does not accept it in one week.')}")
119
+ unless Object.const_defined?(:HelloComponentSlots)
120
+ class HelloComponentSlots
121
+ include Glimmer::Web::Component
122
+
123
+ before_render do
124
+ @shipping_address = Address.new(
125
+ full_name: 'Johnny Doe',
126
+ street: '3922 Park Ave',
127
+ street2: 'PO BOX 8382',
128
+ city: 'San Diego',
129
+ state: 'California',
130
+ zip_code: '91913',
131
+ )
132
+ @billing_address = Address.new(
133
+ full_name: 'John C Doe',
134
+ street: '123 Main St',
135
+ street2: 'Apartment 3C',
136
+ city: 'San Diego',
137
+ state: 'California',
138
+ zip_code: '91911',
139
+ )
140
+ end
141
+
142
+ markup {
143
+ div {
144
+ address_form_with_slots(address: @shipping_address) {
145
+ address_header { # contribute elements to the address_header component slot
146
+ h1('Shipping Address')
147
+ legend('This is the address that is used for shipping your purchase.', style: {margin_bottom: 10})
148
+ }
149
+ address_footer { # contribute elements to the address_footer component slot
150
+ p {
151
+ sub("#{strong('Note:')} #{em('Purchase will be returned if the Shipping Address does not accept it in one week.')}")
152
+ }
149
153
  }
150
154
  }
151
- }
152
-
153
- address_form_with_slots(address: @billing_address) {
154
- address_header { # contribute elements to the address_header component slot
155
- h1('Billing Address')
156
- legend('This is the address that is used for your billing method (e.g. credit card).', style: {margin_bottom: 10})
157
- }
158
- address_footer { # contribute elements to the address_footer component slot
159
- p {
160
- sub("#{strong('Note:')} #{em('Payment will fail if payment method does not match the Billing Address.')}")
155
+
156
+ address_form_with_slots(address: @billing_address) {
157
+ address_header { # contribute elements to the address_header component slot
158
+ h1('Billing Address')
159
+ legend('This is the address that is used for your billing method (e.g. credit card).', style: {margin_bottom: 10})
160
+ }
161
+ address_footer { # contribute elements to the address_footer component slot
162
+ p {
163
+ sub("#{strong('Note:')} #{em('Payment will fail if payment method does not match the Billing Address.')}")
164
+ }
161
165
  }
162
166
  }
163
167
  }
164
168
  }
165
- }
169
+ end
166
170
  end
167
171
 
168
172
  Document.ready? do
@@ -0,0 +1,51 @@
1
+ require 'glimmer-dsl-web'
2
+
3
+ class HelloMutation
4
+ include Glimmer::Web::Component
5
+
6
+ markup {
7
+ div {
8
+ h1('Hello, Mutation!')
9
+
10
+ p {
11
+ 'Add items to a list via element DOM mutations.'
12
+ }
13
+
14
+ div(class: 'actions') {
15
+ @input = input(placeholder: 'Enter list item content')
16
+
17
+ button('Append list item') {
18
+ onclick do
19
+ @list.append {
20
+ li { @input.value }
21
+ }
22
+ @input.value = ''
23
+ @input.focus
24
+ end
25
+ }
26
+
27
+ button('Prepend list item') {
28
+ onclick do
29
+ @list.prepend {
30
+ li { @input.value }
31
+ }
32
+ @input.value = ''
33
+ @input.focus
34
+ end
35
+ }
36
+ }
37
+
38
+ @list = ul
39
+ }
40
+ }
41
+
42
+ style {
43
+ r('.actions input, .actions button') {
44
+ margin '10px 10px 10px 0'
45
+ }
46
+ }
47
+ end
48
+
49
+ Document.ready? do
50
+ HelloMutation.render
51
+ end
@@ -0,0 +1,83 @@
1
+ require 'glimmer-dsl-web'
2
+
3
+ class ListPresenter
4
+ attr_accessor :list, :new_list_item
5
+
6
+ def initialize
7
+ @list = []
8
+ @new_list_item = ''
9
+ end
10
+
11
+ def append_list_item
12
+ list.push(new_list_item)
13
+ clear_new_list_item
14
+ end
15
+
16
+ def prepend_list_item
17
+ list.prepend(new_list_item)
18
+ clear_new_list_item
19
+ end
20
+
21
+ def clear_new_list_item
22
+ self.new_list_item = ''
23
+ end
24
+
25
+ def entering_new_list_item?
26
+ new_list_item.empty?
27
+ end
28
+ end
29
+
30
+ class HelloMutationContentDataBinding
31
+ include Glimmer::Web::Component
32
+
33
+ before_render do
34
+ @list_presenter = ListPresenter.new
35
+ end
36
+
37
+ markup {
38
+ div {
39
+ h1('Hello, Mutation Content Data-Binding!')
40
+
41
+ p {
42
+ 'Add items to a list via content data-binding (which performs implicit DOM mutations).'
43
+ }
44
+
45
+ div(class: 'actions') {
46
+ @input = input(placeholder: 'Enter list item content') {
47
+ value <=> [@list_presenter, :new_list_item]
48
+ focused <= [@list_presenter, :entering_new_list_item?, computed_by: :new_list_item]
49
+ }
50
+
51
+ button('Append list item') {
52
+ onclick do
53
+ @list_presenter.append_list_item
54
+ end
55
+ }
56
+
57
+ button('Prepend list item') {
58
+ onclick do
59
+ @list_presenter.prepend_list_item
60
+ end
61
+ }
62
+ }
63
+
64
+ ul {
65
+ content(@list_presenter, :list) {
66
+ @list_presenter.list.each do |list_item|
67
+ li { list_item }
68
+ end
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ style {
75
+ r('.actions input, .actions button') {
76
+ margin '10px 10px 10px 0'
77
+ }
78
+ }
79
+ end
80
+
81
+ Document.ready? do
82
+ HelloMutationContentDataBinding.render
83
+ 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.10.2
4
+ version: 0.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
@@ -323,6 +323,8 @@ files:
323
323
  - lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb
324
324
  - lib/glimmer-dsl-web/samples/hello/hello_input_date_time_data_binding.rb
325
325
  - lib/glimmer-dsl-web/samples/hello/hello_modal.rb
326
+ - lib/glimmer-dsl-web/samples/hello/hello_mutation.rb
327
+ - lib/glimmer-dsl-web/samples/hello/hello_mutation_content_data_binding.rb
326
328
  - lib/glimmer-dsl-web/samples/hello/hello_observer.rb
327
329
  - lib/glimmer-dsl-web/samples/hello/hello_observer_data_binding.rb
328
330
  - lib/glimmer-dsl-web/samples/hello/hello_page_component_link/conference_talk_page.rb