glimmer-dsl-web 0.8.1 → 0.8.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 +11 -1
- data/LICENSE.txt +1 -1
- data/README.md +20 -12
- 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/component.rb +5 -0
- data/lib/glimmer/web/element_proxy.rb +61 -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: 2875fc22d0ceddd1882af12356ce4d9cbacb2cc8bc77b11fd0a3b736b621e37b
|
|
4
|
+
data.tar.gz: 8476f4388e5f02088328865b2a5609dad2306d5e0dc2412d4ee74cd1377bbf41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b971fce2ac7742dc27ee0b75ea79c21e699fe2bce442f962ee0f1ecfabb437af31863174523b0dc3a15266130efadae6e17ea4ad16055e66d741323656c3e623
|
|
7
|
+
data.tar.gz: af8aa0386ddef4569f621aa2db14174684a9f09f1fc256f6c3ee424accfe3fa2506aa949b07b7c65233105617cb91ef1127ba43b3b8734a90e8363d6aa341290
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 0.8.3
|
|
4
|
+
|
|
5
|
+
- Raise an error when attempting to define a component that shadows an HTML element
|
|
6
|
+
- Raise an error when attempting to define a component slot that shadows an HTML element
|
|
7
|
+
|
|
8
|
+
## 0.8.2
|
|
9
|
+
|
|
10
|
+
- 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
|
|
11
|
+
- Fix issue in Hello, Component Slots! that was causing multiple renders of address_footer
|
|
12
|
+
|
|
3
13
|
## 0.8.1
|
|
4
14
|
|
|
5
15
|
- 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 +18,7 @@
|
|
|
8
18
|
## 0.8.0
|
|
9
19
|
|
|
10
20
|
- Support formatting-paragraph-elements (e.g. 'br', 'strong', 'em') as stand-alone elements (not inside p)
|
|
11
|
-
- Render void tags (
|
|
21
|
+
- Render void tags (\<br\>, \<hr\>, \<img\>, \<input\>, \<meta\>, \<link\>) as a single tag without generating a closing tag (e.g. NOT \<br\>\</br\>).
|
|
12
22
|
|
|
13
23
|
## 0.7.3
|
|
14
24
|
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
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.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!!!
|
|
5
5
|
[](http://badge.fury.io/rb/glimmer-dsl-web)
|
|
6
6
|
[](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
7
7
|
|
|
8
|
+
**UPCOMING APRIL 17, 2026 WORKSHOP: "Building Rails SPAs in Frontend Ruby with Glimmer DSL for Web" at [wroclove.rb 2026 Ruby Conference](https://wrocloverb.com/), in Wroclaw, Poland**
|
|
9
|
+
|
|
10
|
+
**UPCOMING MAY 30, 2026 TALK: "Frontend Ruby on Rails with Glimmer DSL for Web" at [RubyConf Austria 2026](https://rubyconf.at/), in Vienna, Austria**
|
|
11
|
+
|
|
8
12
|
**(Based on Original [Glimmer](https://github.com/AndyObtiva/glimmer) Library Handling World’s Ruby GUI Needs Since 2007. Beware of Imitators!)**
|
|
9
13
|
|
|
10
14
|
**([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
15
|
|
|
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))**
|
|
16
|
+
**(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
17
|
|
|
14
18
|
**(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
19
|
|
|
@@ -121,12 +125,12 @@ Document.ready? do
|
|
|
121
125
|
form {
|
|
122
126
|
div {
|
|
123
127
|
label('Name: ', for: 'name-field')
|
|
124
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
128
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field')
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
div {
|
|
128
132
|
label('Email: ', for: 'email-field')
|
|
129
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
133
|
+
@email_input = input(:required, type: 'email', id: 'email-field')
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
div {
|
|
@@ -1418,7 +1422,7 @@ rails new glimmer_app_server
|
|
|
1418
1422
|
Add the following to `Gemfile`:
|
|
1419
1423
|
|
|
1420
1424
|
```
|
|
1421
|
-
gem 'glimmer-dsl-web', '~> 0.8.
|
|
1425
|
+
gem 'glimmer-dsl-web', '~> 0.8.3'
|
|
1422
1426
|
```
|
|
1423
1427
|
|
|
1424
1428
|
Run:
|
|
@@ -1642,9 +1646,9 @@ Under the hood, HTML element DSL keywords are invoked as Ruby methods.
|
|
|
1642
1646
|
|
|
1643
1647
|
2- **Arguments (HTML Attributes + Text Content)**
|
|
1644
1648
|
|
|
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'
|
|
1649
|
+
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
1650
|
|
|
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')`
|
|
1651
|
+
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
1652
|
|
|
1649
1653
|
3- **Content Block (Properties + Listeners + Nested Elements + Text Content)**
|
|
1650
1654
|
|
|
@@ -1714,8 +1718,8 @@ the `Rails::ResourceService` class source code to find out what its API is. It c
|
|
|
1714
1718
|
if Developers would rather not write the Backend by hand.
|
|
1715
1719
|
|
|
1716
1720
|
`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| ... }`
|
|
1721
|
+
- `index(resource: nil, resource_class: nil, singular_resource_name: nil, plural_resource_name: nil, index_resource_url: nil, params: nil) { |response, resources| ... }`
|
|
1722
|
+
- `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
1723
|
- `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
1724
|
- `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
1725
|
- `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 +1900,12 @@ Document.ready? do
|
|
|
1896
1900
|
form {
|
|
1897
1901
|
div {
|
|
1898
1902
|
label('Name: ', for: 'name-field')
|
|
1899
|
-
@name_input = input(type: 'text', id: 'name-field'
|
|
1903
|
+
@name_input = input(:required, :autofocus, type: 'text', id: 'name-field')
|
|
1900
1904
|
}
|
|
1901
1905
|
|
|
1902
1906
|
div {
|
|
1903
1907
|
label('Email: ', for: 'email-field')
|
|
1904
|
-
@email_input = input(type: 'email', id: 'email-field'
|
|
1908
|
+
@email_input = input(:required, type: 'email', id: 'email-field')
|
|
1905
1909
|
}
|
|
1906
1910
|
|
|
1907
1911
|
div {
|
|
@@ -4807,6 +4811,10 @@ Learn more by reading the [GPG](https://github.com/AndyObtiva/glimmer/blob/maste
|
|
|
4807
4811
|
|
|
4808
4812
|
F.A.Q. (Frequently Asked Questions):
|
|
4809
4813
|
|
|
4814
|
+
#### Can I build a modern UI with a modern style/look/feel using Glimmer DSL for Web?
|
|
4815
|
+
|
|
4816
|
+
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.
|
|
4817
|
+
|
|
4810
4818
|
#### Can I reuse JavaScript libraries from Glimmer DSL for Web in Ruby?
|
|
4811
4819
|
|
|
4812
4820
|
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.
|
|
@@ -4869,7 +4877,7 @@ These features have been suggested. You might see them in a future version of Gl
|
|
|
4869
4877
|
|
|
4870
4878
|
[MIT](https://opensource.org/licenses/MIT)
|
|
4871
4879
|
|
|
4872
|
-
Copyright (c) 2023-
|
|
4880
|
+
Copyright (c) 2023-2026 - Andy Maleh.
|
|
4873
4881
|
See [LICENSE.txt](LICENSE.txt) for further details.
|
|
4874
4882
|
|
|
4875
4883
|
--
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.8.
|
|
1
|
+
0.8.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.8.
|
|
5
|
+
# stub: glimmer-dsl-web 0.8.3 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.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]
|
|
@@ -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
|
|
@@ -171,6 +171,11 @@ module Glimmer
|
|
|
171
171
|
class << self
|
|
172
172
|
def included(klass)
|
|
173
173
|
if !klass.ancestors.include?(GlimmerSupersedable)
|
|
174
|
+
if !klass.to_s.include?('::') && Glimmer::Web::ElementProxy::ELEMENT_KEYWORDS.include?(klass.to_s.underscore)
|
|
175
|
+
raise "Cannot define the Glimmer::Web::Component class \"#{klass.to_s}\" because it shadows the HTML element \"#{klass.to_s.underscore}\"! " +
|
|
176
|
+
"Either rename the class (e.g. \"MyApp#{klass.to_s}\") to avoid conflicting with an existing HTML element name or " +
|
|
177
|
+
"nest it within a namespace module/class (e.g. \"MyApp::#{klass.to_s}\")!"
|
|
178
|
+
end
|
|
174
179
|
klass.extend(ClassMethods)
|
|
175
180
|
klass.include(Glimmer)
|
|
176
181
|
klass.prepend(GlimmerSupersedable)
|
|
@@ -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',
|
|
@@ -191,6 +221,7 @@ module Glimmer
|
|
|
191
221
|
end
|
|
192
222
|
@slot = options['slot'] || options[:slot]
|
|
193
223
|
@slot = @slot.to_sym if @slot
|
|
224
|
+
validate_slot!
|
|
194
225
|
ancestor_component.slot_elements[@slot] = self if @slot && ancestor_component
|
|
195
226
|
@args = args
|
|
196
227
|
@block = block
|
|
@@ -489,9 +520,23 @@ module Glimmer
|
|
|
489
520
|
# TODO auto-convert known glimmer attributes like parent to data attributes like data-parent
|
|
490
521
|
# TODO check if we need to avoid rendering content block if no content is available
|
|
491
522
|
@dom ||= begin
|
|
492
|
-
|
|
523
|
+
if args.first.is_a?(String)
|
|
524
|
+
if !Glimmer::Web::ElementProxy.element_boolean_attribute?(keyword, args.first)
|
|
525
|
+
content = args.first
|
|
526
|
+
remaining_args = args[1, args.size - 1]
|
|
527
|
+
else
|
|
528
|
+
content = ''
|
|
529
|
+
remaining_args = args
|
|
530
|
+
end
|
|
531
|
+
else
|
|
532
|
+
content = ''
|
|
533
|
+
remaining_args = args
|
|
534
|
+
end
|
|
535
|
+
remaining_args = remaining_args.to_a
|
|
493
536
|
content += children_dom_content if bulk_render?
|
|
494
|
-
|
|
537
|
+
remaining_args = remaining_args[0...-1] if remaining_args.last.is_a?(Hash)
|
|
538
|
+
boolean_attributes = remaining_args
|
|
539
|
+
ElementProxy.render_html(keyword, attributes: html_options, boolean_attributes:, content:)
|
|
495
540
|
end
|
|
496
541
|
end
|
|
497
542
|
|
|
@@ -1072,6 +1117,15 @@ module Glimmer
|
|
|
1072
1117
|
style_value.to_s
|
|
1073
1118
|
end
|
|
1074
1119
|
end
|
|
1120
|
+
|
|
1121
|
+
def validate_slot!
|
|
1122
|
+
slot_name = @slot.to_s
|
|
1123
|
+
if Glimmer::Web::ElementProxy::ELEMENT_KEYWORDS.include?(slot_name)
|
|
1124
|
+
raise "Cannot define the Component Slot \"#{slot_name}\" because it shadows the HTML element \"#{slot_name}\"! " +
|
|
1125
|
+
"Rename the Component Slot (e.g. \"my_app_#{slot_name}\") to avoid conflicting with an existing HTML element name!"
|
|
1126
|
+
end
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1075
1129
|
end
|
|
1076
1130
|
end
|
|
1077
1131
|
end
|
|
@@ -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
|
}
|