glimmer-dsl-web 0.8.1 → 0.8.2
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 +6 -1
- data/README.md +15 -11
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +2 -2
- data/lib/glimmer/dsl/web/element_expression.rb +17 -3
- data/lib/glimmer/web/element_proxy.rb +51 -7
- data/lib/glimmer/web/formatting_element_proxy.rb +11 -3
- data/lib/glimmer-dsl-web/samples/hello/hello_component_slots.rb +6 -2
- data/lib/glimmer-dsl-web/samples/hello/hello_form.rb +2 -2
- data/lib/glimmer-dsl-web/samples/hello/hello_form_mvp/views/contact_form.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c0a02a921d8cd4c8a63d77cca2780e2a117e5291ad3243915e5591174e5ebd26
|
|
4
|
+
data.tar.gz: adc5e55869e5996a41bff34b71725167e458a7d25d6e2dd10bca437502c7b34c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b84989eb95043c9f239d23f8605322ec8b96f0e19f32bbd327c9f1e8ea33571d755ba4764c758e6ee2471a5c01026374da185e57abbe1316acc2f8a1c19ff4d
|
|
7
|
+
data.tar.gz: a8c0153c7959af52e7fd8d4e98cad02eb7b9daa32bf2b0f1772d0b54cbefe54e6ee5e63f85c9ce837d337bee8b683836d1193947b04aa7fdb45d8a09fe74a58e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 0.8.2
|
|
4
|
+
|
|
5
|
+
- Support value-less boolean attributes in HTML elements (e.g. input(:required, :autofocus, id: 'name-field') / p('Hello', :hidden, id: 'product-description')), by passing as symbols before the attributes hash, but after content string if any
|
|
6
|
+
- Fix issue in Hello, Component Slots! that was causing multiple renders of address_footer
|
|
7
|
+
|
|
3
8
|
## 0.8.1
|
|
4
9
|
|
|
5
10
|
- Implement `Glimmer::Web::ElementProxy#inspect` method that includes element ID, keyword, args, and parent and prevents recursing through multiple parent levels to improve readability for troubleshooting.
|
|
@@ -8,7 +13,7 @@
|
|
|
8
13
|
## 0.8.0
|
|
9
14
|
|
|
10
15
|
- Support formatting-paragraph-elements (e.g. 'br', 'strong', 'em') as stand-alone elements (not inside p)
|
|
11
|
-
- Render void tags (
|
|
16
|
+
- Render void tags (\<br\>, \<hr\>, \<img\>, \<input\>, \<meta\>, \<link\>) as a single tag without generating a closing tag (e.g. NOT \<br\>\</br\>).
|
|
12
17
|
|
|
13
18
|
## 0.7.3
|
|
14
19
|
|
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.8.
|
|
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.8.2 (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!!!
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
**([Fukuoka Prefecture Future IT Initiative 2025 Money Forward Award Winner](https://andymaleh.blogspot.com/2025/01/glimmer-dsl-for-web-wins-in-fukuoka.html)) [(Award Announcement)](https://digitalfukuoka.jp/news/info/528/)**
|
|
11
11
|
|
|
12
|
-
**(Talk Videos: [Intro to Ruby in the Browser](https://youtu.be/4AdcfbI6A4c?si=MmxOrkhIXTDHQoYi) / [Frontend Ruby with Glimmer DSL for Web \[Montreal.rb\]](https://youtu.be/rIZ-ILUv9ME?si=raygUXVPd_7ypWuE) / [Frontend Ruby with Glimmer DSL for Web \[/dev/mtl 2024\]](https://www.youtube.com/watch?v=J2VIY9DMJo4))**
|
|
12
|
+
**(Talk Videos: [Intro to Ruby in the Browser](https://youtu.be/4AdcfbI6A4c?si=MmxOrkhIXTDHQoYi) / [Frontend Ruby with Glimmer DSL for Web \[Montreal.rb\]](https://youtu.be/rIZ-ILUv9ME?si=raygUXVPd_7ypWuE) / [Frontend Ruby with Glimmer DSL for Web \[/dev/mtl 2024\]](https://www.youtube.com/watch?v=J2VIY9DMJo4)) / [Frontend Ruby with Glimmer DSL for Web at Ruby on Rio 2025-06-06 Talk](https://www.youtube.com/watch?v=LY6ulYICuzE)**
|
|
13
13
|
|
|
14
14
|
**(Ruby Rogues Podcast: [Building Better Ruby Apps: Glimmer Component Slots and More](https://topenddevs.com/podcasts/ruby-rogues/episodes/building-better-ruby-apps-glimmer-s-component-slots-and-more-ruby-653))**
|
|
15
15
|
|
|
@@ -121,12 +121,12 @@ Document.ready? do
|
|
|
121
121
|
form {
|
|
122
122
|
div {
|
|
123
123
|
label('Name: ', for: 'name-field')
|
|
124
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
124
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field')
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
div {
|
|
128
128
|
label('Email: ', for: 'email-field')
|
|
129
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
129
|
+
@email_input = input(:required, type: 'email', id: 'email-field')
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
div {
|
|
@@ -1418,7 +1418,7 @@ rails new glimmer_app_server
|
|
|
1418
1418
|
Add the following to `Gemfile`:
|
|
1419
1419
|
|
|
1420
1420
|
```
|
|
1421
|
-
gem 'glimmer-dsl-web', '~> 0.8.
|
|
1421
|
+
gem 'glimmer-dsl-web', '~> 0.8.2'
|
|
1422
1422
|
```
|
|
1423
1423
|
|
|
1424
1424
|
Run:
|
|
@@ -1642,9 +1642,9 @@ Under the hood, HTML element DSL keywords are invoked as Ruby methods.
|
|
|
1642
1642
|
|
|
1643
1643
|
2- **Arguments (HTML Attributes + Text Content)**
|
|
1644
1644
|
|
|
1645
|
-
You can set any HTML element attributes by passing as keyword arguments to element methods like `div(id: 'container', class: 'stack')` or `input(type: 'email'
|
|
1645
|
+
You can set any HTML element attributes by passing as keyword arguments to element methods like `div(id: 'container', class: 'stack')` or `input(:required, type: 'email')`
|
|
1646
1646
|
|
|
1647
|
-
Also, if the element has a little bit of text content that can fit in one line, it can be passed as the 1st argument like `label('Name: ', for: 'name_field')`, `button('Calculate', class: 'round-button')`, or `span('Mr')`
|
|
1647
|
+
Also, if the element has a little bit of text content that can fit in one line, it can be passed as the 1st argument like `label('Name: ', for: 'name_field')`, `button('Calculate', :disabled, class: 'round-button')`, or `span('Mr')`
|
|
1648
1648
|
|
|
1649
1649
|
3- **Content Block (Properties + Listeners + Nested Elements + Text Content)**
|
|
1650
1650
|
|
|
@@ -1714,8 +1714,8 @@ the `Rails::ResourceService` class source code to find out what its API is. It c
|
|
|
1714
1714
|
if Developers would rather not write the Backend by hand.
|
|
1715
1715
|
|
|
1716
1716
|
`Rails::ResourceService` API:
|
|
1717
|
-
- `index(resource: nil, resource_class: nil, singular_resource_name: nil, plural_resource_name: nil, index_resource_url: nil, params: nil) { |response| ... }`
|
|
1718
|
-
- `show(resource: nil, resource_class: nil, resource_id: nil, singular_resource_name: nil, plural_resource_name: nil, show_resource_url: nil, params: nil) { |response| ... }`
|
|
1717
|
+
- `index(resource: nil, resource_class: nil, singular_resource_name: nil, plural_resource_name: nil, index_resource_url: nil, params: nil) { |response, resources| ... }`
|
|
1718
|
+
- `show(resource: nil, resource_class: nil, resource_id: nil, singular_resource_name: nil, plural_resource_name: nil, show_resource_url: nil, params: nil) { |response, resource| ... }`
|
|
1719
1719
|
- `create(resource: nil, resource_class: nil, resource_attributes: nil, singular_resource_name: nil, plural_resource_name: nil, create_resource_url: nil, params: nil) { |response, created_resource, errors| ... }`
|
|
1720
1720
|
- `update(resource: nil, resource_class: nil, resource_id: nil, resource_attributes: nil, singular_resource_name: nil, plural_resource_name: nil, update_resource_url: nil, params: nil) { |response, updated_resource, errors| ... }`
|
|
1721
1721
|
- `destroy(resource: nil, resource_class: nil, resource_id: nil, singular_resource_name: nil, plural_resource_name: nil, destroy_resource_url: nil, params: nil) { |response| ... }`
|
|
@@ -1896,12 +1896,12 @@ Document.ready? do
|
|
|
1896
1896
|
form {
|
|
1897
1897
|
div {
|
|
1898
1898
|
label('Name: ', for: 'name-field')
|
|
1899
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
1899
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field')
|
|
1900
1900
|
}
|
|
1901
1901
|
|
|
1902
1902
|
div {
|
|
1903
1903
|
label('Email: ', for: 'email-field')
|
|
1904
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
1904
|
+
@email_input = input(:required, type: 'email', id: 'email-field')
|
|
1905
1905
|
}
|
|
1906
1906
|
|
|
1907
1907
|
div {
|
|
@@ -4807,6 +4807,10 @@ Learn more by reading the [GPG](https://github.com/AndyObtiva/glimmer/blob/maste
|
|
|
4807
4807
|
|
|
4808
4808
|
F.A.Q. (Frequently Asked Questions):
|
|
4809
4809
|
|
|
4810
|
+
#### Can I build a modern UI with a modern style/look/feel using Glimmer DSL for Web?
|
|
4811
|
+
|
|
4812
|
+
Yes, 100%. Glimmer DSL for Web enables building highly advanced interactions with the simplest code possible. And, styling is just CSS, which is an orthogonal concern. You can apply any CSS style to Glimmer elements given that Glimmer produces real HTML, meaning 100% of what's possible in CSS outside of Glimmer is also possible within Glimmer. Glimmer DSL for Web supports applying CSS using CSS/SCSS/SASS files, embedding CSS inside Glimmer Web Components directly with a Ruby CSS DSL, and a hybrid approach when it is useful to combine multiple styling options (like keeping general styles in CSS files and adding specific styles in Glimmer Web Components). Also, you can integrate with any CSS framework (e.g. Tailwind/Bootstrap) if needed. As a result, you can create any modern UI with any modern style/look/feel when using Glimmer DSL for Web.
|
|
4813
|
+
|
|
4810
4814
|
#### Can I reuse JavaScript libraries from Glimmer DSL for Web in Ruby?
|
|
4811
4815
|
|
|
4812
4816
|
Absolutely. Glimmer DSL for Web can integrate with any JavaScript libraries. You can either load the JavaScript libraries in advance by linking to them in the Rails View/Layout (e.g. linking to JS library CDN URLs) or by including JavaScript files in the lookup directories of Opal Ruby, and adding a Ruby `require('path_to_js_lib')` call in the code. In Ruby, the `$$` global variable gives access to the top-level JavaScript global scope, which enables invocations on any JavaScript objects. For example, `$$.hljs` gives access to the loaded `window.hljs` object for the Highlight.js library, and that enables invoking any functions from that library as needed, like `$$.hljs.highlightAll` to activate code syntax highlighting.
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.8.
|
|
1
|
+
0.8.2
|
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.8.
|
|
5
|
+
# stub: glimmer-dsl-web 0.8.2 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "glimmer-dsl-web".freeze
|
|
9
|
-
s.version = "0.8.
|
|
9
|
+
s.version = "0.8.2".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]
|
|
@@ -9,6 +9,8 @@ module Glimmer
|
|
|
9
9
|
class ElementExpression < Expression
|
|
10
10
|
include GeneralElementExpression
|
|
11
11
|
|
|
12
|
+
REGEXP_ARG_TYPE_STRING = /(String)*(Hash)?/
|
|
13
|
+
|
|
12
14
|
def can_interpret?(parent, keyword, *args, &block)
|
|
13
15
|
slot = keyword.to_s.to_sym
|
|
14
16
|
Glimmer::Web::ElementProxy.keyword_supported?(keyword) &&
|
|
@@ -21,9 +23,7 @@ module Glimmer
|
|
|
21
23
|
!(parent.slot_elements.keys.include?(slot) || parent.slot_elements.keys.include?(slot.to_s))
|
|
22
24
|
)
|
|
23
25
|
) ||
|
|
24
|
-
|
|
25
|
-
args.size == 1 && args.first.is_a?(Hash) ||
|
|
26
|
-
args.size == 2 && args.first.is_a?(String) && args.last.is_a?(Hash)
|
|
26
|
+
valid_element_args?(args)
|
|
27
27
|
) &&
|
|
28
28
|
(
|
|
29
29
|
keyword != 'title' ||
|
|
@@ -36,6 +36,20 @@ module Glimmer
|
|
|
36
36
|
parent.find_ancestor(include_self: true) { |ancestor| ancestor.keyword == 'svg' }
|
|
37
37
|
)
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
def valid_element_args?(args)
|
|
41
|
+
arg_types = args.map do |arg|
|
|
42
|
+
if arg.is_a?(String)
|
|
43
|
+
'String'
|
|
44
|
+
elsif arg.is_a?(Hash)
|
|
45
|
+
'Hash'
|
|
46
|
+
else
|
|
47
|
+
'Unsupported'
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
arg_type_string = arg_types.join
|
|
51
|
+
arg_type_string.match(REGEXP_ARG_TYPE_STRING)
|
|
52
|
+
end
|
|
39
53
|
end
|
|
40
54
|
end
|
|
41
55
|
end
|
|
@@ -64,6 +64,13 @@ module Glimmer
|
|
|
64
64
|
Glimmer::Web::ElementProxy
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
def element_boolean_attribute?(keyword, attribute)
|
|
68
|
+
attribute = attribute.to_s
|
|
69
|
+
keyword = keyword.to_s
|
|
70
|
+
HTML_ELEMENT_GLOBAL_BOOLEAN_ATTRIBUTES.include?(attribute) ||
|
|
71
|
+
HTML_ELEMENT_BOOLEAN_ATTRIBUTES[keyword]&.include?(attribute)
|
|
72
|
+
end
|
|
73
|
+
|
|
67
74
|
def next_id_number_for(name)
|
|
68
75
|
@max_id_numbers[name] = max_id_number_for(name) + 1
|
|
69
76
|
end
|
|
@@ -88,16 +95,17 @@ module Glimmer
|
|
|
88
95
|
@@widget_handling_listener
|
|
89
96
|
end
|
|
90
97
|
|
|
91
|
-
def render_html(element, attributes, content
|
|
92
|
-
|
|
98
|
+
def render_html(element, attributes:, boolean_attributes: [], content: nil)
|
|
99
|
+
attributes_string = attributes.reduce('') do |output, option_pair|
|
|
93
100
|
attribute, value = option_pair
|
|
94
101
|
value = value.to_s.sub('"', '"').sub("'", ''')
|
|
95
102
|
output += " #{attribute}=\"#{value}\""
|
|
96
103
|
end
|
|
104
|
+
boolean_attributes_string = boolean_attributes.to_a.map(&:to_s).join(' ')
|
|
97
105
|
if html_void_keyword?(element)
|
|
98
|
-
"<#{element}#{
|
|
106
|
+
"<#{element}#{attributes_string} #{boolean_attributes_string}>"
|
|
99
107
|
else
|
|
100
|
-
"<#{element}#{
|
|
108
|
+
"<#{element}#{attributes_string} #{boolean_attributes_string}>#{content}</#{element}>"
|
|
101
109
|
end
|
|
102
110
|
end
|
|
103
111
|
|
|
@@ -148,7 +156,29 @@ module Glimmer
|
|
|
148
156
|
|
|
149
157
|
# title is a special attribute because it matches an element tag name (needs special treatment)
|
|
150
158
|
HTML_ELEMENT_SPECIAL_ATTRIBUTES = ['title']
|
|
151
|
-
|
|
159
|
+
|
|
160
|
+
HTML_ELEMENT_GLOBAL_BOOLEAN_ATTRIBUTES = ['hidden', 'inert', 'contenteditable', 'spellcheck']
|
|
161
|
+
HTML_ELEMENT_BOOLEAN_ATTRIBUTES = {
|
|
162
|
+
'audio' => ['autoplay', 'controls', 'loop', 'muted'],
|
|
163
|
+
'button' => ['autofocus', 'disabled', 'formnovalidate'],
|
|
164
|
+
'details' => ['open'],
|
|
165
|
+
'dialog' => ['open'],
|
|
166
|
+
'fieldset' => ['disabled'],
|
|
167
|
+
'form' => ['novalidate'],
|
|
168
|
+
'iframe' => ['allowfullscreen'],
|
|
169
|
+
'img' => ['ismap'],
|
|
170
|
+
'input' => ['alpha', 'autofocus', 'checked', 'disabled', 'formnovalidate', 'multiple', 'readonly', 'required'],
|
|
171
|
+
'ol' => ['reversed'],
|
|
172
|
+
'optgroup' => ['disabled'],
|
|
173
|
+
'option' => ['disabled', 'selected'],
|
|
174
|
+
'script' => ['async', 'defer', 'nomodule'],
|
|
175
|
+
'select' => ['autofocus', 'disabled', 'multiple', 'required'],
|
|
176
|
+
'template' => ['shadowrootclonable', 'shadowrootdelegatesfocus', 'shadowrootserializable', 'shadowrootcustomelementregistry'],
|
|
177
|
+
'textarea' => ['autofocus', 'disabled', 'readonly', 'required'],
|
|
178
|
+
'track' => ['default'],
|
|
179
|
+
'video' => ['autoplay', 'controls', 'loop', 'muted', 'playsinline']
|
|
180
|
+
}
|
|
181
|
+
|
|
152
182
|
GLIMMER_ATTRIBUTES = [:parent]
|
|
153
183
|
PROPERTY_ALIASES = {
|
|
154
184
|
'inner_html' => 'innerHTML',
|
|
@@ -489,9 +519,23 @@ module Glimmer
|
|
|
489
519
|
# TODO auto-convert known glimmer attributes like parent to data attributes like data-parent
|
|
490
520
|
# TODO check if we need to avoid rendering content block if no content is available
|
|
491
521
|
@dom ||= begin
|
|
492
|
-
|
|
522
|
+
if args.first.is_a?(String)
|
|
523
|
+
if !Glimmer::Web::ElementProxy.element_boolean_attribute?(keyword, args.first)
|
|
524
|
+
content = args.first
|
|
525
|
+
remaining_args = args[1, args.size - 1]
|
|
526
|
+
else
|
|
527
|
+
content = ''
|
|
528
|
+
remaining_args = args
|
|
529
|
+
end
|
|
530
|
+
else
|
|
531
|
+
content = ''
|
|
532
|
+
remaining_args = args
|
|
533
|
+
end
|
|
534
|
+
remaining_args = remaining_args.to_a
|
|
493
535
|
content += children_dom_content if bulk_render?
|
|
494
|
-
|
|
536
|
+
remaining_args = remaining_args[0...-1] if remaining_args.last.is_a?(Hash)
|
|
537
|
+
boolean_attributes = remaining_args
|
|
538
|
+
ElementProxy.render_html(keyword, attributes: html_options, boolean_attributes:, content:)
|
|
495
539
|
end
|
|
496
540
|
end
|
|
497
541
|
|
|
@@ -44,13 +44,21 @@ module Glimmer
|
|
|
44
44
|
|
|
45
45
|
def format(keyword, *args, &block)
|
|
46
46
|
content = nil
|
|
47
|
+
boolean_attributes = []
|
|
47
48
|
if block_given?
|
|
48
49
|
content = block.call.to_s
|
|
49
|
-
elsif args.any? && !args.first.is_a?(Hash)
|
|
50
|
+
elsif args.any? && !args.first.is_a?(Hash) && !Glimmer::Web::ElementProxy.element_boolean_attribute?(keyword, args.first)
|
|
50
51
|
content = args.first.to_s
|
|
52
|
+
args = args[1, args.size - 1]
|
|
51
53
|
end
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
if args.last.is_a?(Hash)
|
|
55
|
+
attribute_hash = args.last
|
|
56
|
+
boolean_attributes = args[0, args.size - 1]
|
|
57
|
+
else
|
|
58
|
+
attribute_hash = {}
|
|
59
|
+
boolean_attributes = args
|
|
60
|
+
end
|
|
61
|
+
ElementProxy.render_html(keyword, attributes: attribute_hash, boolean_attributes:, content:)
|
|
54
62
|
end
|
|
55
63
|
end
|
|
56
64
|
|
|
@@ -165,7 +165,9 @@ class HelloComponentSlots
|
|
|
165
165
|
legend('This is the address that is used for shipping your purchase.', style: {margin_bottom: 10})
|
|
166
166
|
}
|
|
167
167
|
address_footer { # contribute elements to the address_footer component slot
|
|
168
|
-
p
|
|
168
|
+
p {
|
|
169
|
+
sub("#{strong('Note:')} #{em('Purchase will be returned if the Shipping Address does not accept it in one week.')}")
|
|
170
|
+
}
|
|
169
171
|
}
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -175,7 +177,9 @@ class HelloComponentSlots
|
|
|
175
177
|
legend('This is the address that is used for your billing method (e.g. credit card).', style: {margin_bottom: 10})
|
|
176
178
|
}
|
|
177
179
|
address_footer { # contribute elements to the address_footer component slot
|
|
178
|
-
p
|
|
180
|
+
p {
|
|
181
|
+
sub("#{strong('Note:')} #{em('Payment will fail if payment method does not match the Billing Address.')}")
|
|
182
|
+
}
|
|
179
183
|
}
|
|
180
184
|
}
|
|
181
185
|
}
|
|
@@ -32,12 +32,12 @@ Document.ready? do
|
|
|
32
32
|
form {
|
|
33
33
|
div {
|
|
34
34
|
label('Name: ', for: 'name-field')
|
|
35
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
35
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
div {
|
|
39
39
|
label('Email: ', for: 'email-field')
|
|
40
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
40
|
+
@email_input = input(:required, type: 'email', id: 'email-field')
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
div {
|
|
@@ -7,14 +7,14 @@ class ContactForm
|
|
|
7
7
|
form {
|
|
8
8
|
div {
|
|
9
9
|
label('Name: ', for: 'name-field')
|
|
10
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
10
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field') {
|
|
11
11
|
value <=> [presenter.new_contact, :name]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
div {
|
|
16
16
|
label('Email: ', for: 'email-field')
|
|
17
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
17
|
+
@email_input = input(:required, type: 'email', id: 'email-field') {
|
|
18
18
|
value <=> [presenter.new_contact, :email]
|
|
19
19
|
}
|
|
20
20
|
}
|