superform 0.6.0 → 0.7.0
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 +100 -3
- data/CLAUDE.md +46 -0
- data/Gemfile.lock +6 -1
- data/README.md +208 -75
- data/examples/basic_form.rb +21 -0
- data/examples/checkbox_form.rb +28 -0
- data/examples/date_time_form.rb +18 -0
- data/examples/example_form.rb +10 -0
- data/examples/select_form.rb +26 -0
- data/examples/special_inputs_form.rb +23 -0
- data/examples/textarea_form.rb +15 -0
- data/lib/generators/superform/install/templates/base.rb +33 -7
- data/lib/superform/dom.rb +13 -1
- data/lib/superform/field.rb +14 -9
- data/lib/superform/rails/choices/choice.rb +39 -0
- data/lib/superform/rails/choices/mapper.rb +41 -0
- data/lib/superform/rails/choices.rb +6 -0
- data/lib/superform/rails/components/base.rb +9 -2
- data/lib/superform/rails/components/checkbox.rb +34 -7
- data/lib/superform/rails/components/checkboxes.rb +38 -0
- data/lib/superform/rails/components/datalist.rb +34 -0
- data/lib/superform/rails/components/input.rb +1 -1
- data/lib/superform/rails/components/label.rb +1 -1
- data/lib/superform/rails/components/radio.rb +21 -0
- data/lib/superform/rails/components/radios.rb +38 -0
- data/lib/superform/rails/components/select.rb +52 -8
- data/lib/superform/rails/field.rb +184 -0
- data/lib/superform/rails/form.rb +18 -129
- data/lib/superform/version.rb +1 -1
- data/server/components/breadcrumb.rb +11 -0
- data/server/components/form_card.rb +13 -0
- data/server/components/layout.rb +23 -0
- data/server/controllers/forms_controller.rb +94 -0
- data/server/models/example.rb +31 -0
- data/server/public/styles.css +282 -0
- data/server/rails.rb +56 -0
- data/superform.gemspec +37 -0
- metadata +28 -5
- data/lib/superform/rails/option_mapper.rb +0 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41ba2a10e93238ddfea45963bbc08a28c884231e128b39becdb184ceebd9c9f9
|
|
4
|
+
data.tar.gz: 994b8448c1e72182bb5638682f4d45b42a5b2b04b6d85ec78b1cb8cab55f9263
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba4d34fc66ef3fae89763ed7cf091a1d41c54df4f585b650a62ba0bee14038c2a1cdf5aa8f15b15bf5f73c201b96113f71bc13e25b3ccb85b45329e3c80844fa
|
|
7
|
+
data.tar.gz: b39fdedafc3f12a84ec753e0abf34f38b28632b801f0ace6a020c8d07946931d07ba6bb15f54414f13a1c065768df3e1291ef39431c3770fe8e2ddb8765af988
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,67 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.7.0] - 2026-03-01
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **Radio and checkbox groups** via `Field(:plan).radios(...)` and `Field(:roles).checkboxes(...)`.
|
|
6
|
+
Return renderable Phlex components (like `input`, `select`) so they work as one-liners
|
|
7
|
+
via Kit. Without a block, renders default `<label><input> Text</label>` markup per choice.
|
|
8
|
+
With a block, yields each `Choice` for custom markup — `choice.input` and
|
|
9
|
+
`choice.label` render directly into the component's output. Accepts the same option formats as
|
|
10
|
+
`select`. Auto-detects Rails enums when called with no arguments.
|
|
11
|
+
`choice.label` without a block defaults to rendering `choice.text`.
|
|
12
|
+
Subclass `Components::Radios` or `Components::Checkboxes` to customize defaults.
|
|
13
|
+
- **Hash options** for `select`, `radios`, and `checkboxes` — e.g. `radios(1 => "Basic", 2 => "Pro")`.
|
|
14
|
+
- **Radio component** with `field(:gender).radio("male")` API. Automatically handles name, value, and checked state. Each radio gets a unique DOM id based on its value (e.g. `user_gender_male`).
|
|
15
|
+
- **Checkbox collection support** — three modes:
|
|
16
|
+
- **Boolean** (on/off toggle): `Field(:featured).checkbox` renders with hidden "0" input
|
|
17
|
+
- **All-options** (pick from known set): `Field(:role_ids).checkbox(value: role.id)` with `[]` name and unique ids per value
|
|
18
|
+
- **Field collection** (from existing array): `field(:role_ids).collection { |r| r.checkbox }` for values already on the model
|
|
19
|
+
- **Choices module** (`Superform::Rails::Choices`) — `Choices::Choice` holds per-option state,
|
|
20
|
+
`Choices::Mapper` (renamed from `OptionMapper`) maps option args to `(value, text)` pairs.
|
|
21
|
+
- **Unique DOM ids** for radio and checkbox groups via `DOM#id(*suffixes)`. Prevents duplicate ids in valid HTML and allows labels to target individual inputs.
|
|
22
|
+
- **Datalist component** with `Field(:time_zone).datalist(*ActiveSupport::TimeZone.all.map(&:name))`.
|
|
23
|
+
Renders a native `<input>` + `<datalist>` for free-text input with autocomplete suggestions —
|
|
24
|
+
no JavaScript required. Accepts the same option formats as `select`. Block form available
|
|
25
|
+
for custom options.
|
|
26
|
+
- **Select improvements**: blank options (`nil`) at any position, `multiple: true` support with hidden input for empty submissions, ActiveRecord relations as options.
|
|
27
|
+
- **Preview server** — run `bin/preview` to view example forms at localhost:3000 with hot-reloading.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `OptionMapper` renamed to `Choices::Mapper`. If you referenced `Superform::Rails::OptionMapper` directly, update to `Superform::Rails::Choices::Mapper`.
|
|
32
|
+
- **Deprecation**: Components now accept HTML attributes as keyword arguments directly instead of wrapping them in `attributes:`. The old `attributes:` keyword still works but emits a deprecation warning and will be removed in a future version.
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
# Before
|
|
36
|
+
MyInput.new(field, attributes: { class: "form-input" })
|
|
37
|
+
|
|
38
|
+
# After
|
|
39
|
+
MyInput.new(field, class: "form-input")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If you have custom components that override `initialize`, update them to use `**attributes`:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
# Before
|
|
46
|
+
class MyRadio < Superform::Rails::Components::Field
|
|
47
|
+
def initialize(field, value:, attributes: {})
|
|
48
|
+
super(field, attributes: attributes)
|
|
49
|
+
@value = value
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# After
|
|
54
|
+
class MyRadio < Superform::Rails::Components::Field
|
|
55
|
+
def initialize(field, value:, **attributes)
|
|
56
|
+
super(field, **attributes)
|
|
57
|
+
@value = value
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Required Ruby version bumped to 2.7.0.
|
|
63
|
+
|
|
64
|
+
## [0.6.1] - 2025-08-28
|
|
2
65
|
|
|
3
66
|
### Breaking Changes
|
|
4
67
|
|
|
@@ -41,6 +104,40 @@ Rails classes have been moved into separate files for better organization:
|
|
|
41
104
|
|
|
42
105
|
### How to Upgrade
|
|
43
106
|
|
|
107
|
+
#### Phlex 2.x compatibility
|
|
108
|
+
|
|
109
|
+
The `ApplicationForm` file should be moved to `Components::Form` at `./app/components/form.rb` to better match Phlex 2.x conventions.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
# Before (0.5.x)
|
|
113
|
+
class ApplicationForm < Superform::Rails::Form
|
|
114
|
+
# ...
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
# After (0.6.0)
|
|
120
|
+
class Components::Form < Superform::Rails::Form
|
|
121
|
+
# ...
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Form variants may organized in the `./app/components/forms/` directory.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
# Before (0.5.x)
|
|
129
|
+
class Forms::User < ApplicationForm
|
|
130
|
+
# ...
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# After (0.6.0)
|
|
136
|
+
class Forms::User < Components::Form
|
|
137
|
+
# ...
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
44
141
|
#### Custom Form Classes
|
|
45
142
|
|
|
46
143
|
Custom form classes with Rails now automatically pass themselves as form instances (no changes needed for basic usage).
|
|
@@ -70,7 +167,7 @@ Update component class names in your custom form classes:
|
|
|
70
167
|
|
|
71
168
|
class Field < Superform::Rails::Form::Field
|
|
72
169
|
def input(**attributes)
|
|
73
|
-
MyInput.new(self, attributes
|
|
170
|
+
MyInput.new(self, **attributes)
|
|
74
171
|
end
|
|
75
172
|
end
|
|
76
173
|
```
|
|
@@ -107,7 +204,7 @@ Run bundle update to update dependencies:
|
|
|
107
204
|
- Safe mass assignment protection against unauthorized attributes
|
|
108
205
|
- Field input type helper methods for Rails forms:
|
|
109
206
|
- `field.email` for email input type
|
|
110
|
-
- `field.password` for password input type
|
|
207
|
+
- `field.password` for password input type
|
|
111
208
|
- `field.url` for URL input type
|
|
112
209
|
- `field.tel` (with `phone` alias) for telephone input type
|
|
113
210
|
- `field.number` for number input type
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Superform Development Guide
|
|
2
|
+
|
|
3
|
+
## Component Argument Convention
|
|
4
|
+
|
|
5
|
+
HTML components follow a strict convention for arguments:
|
|
6
|
+
|
|
7
|
+
- **Positional/named keyword arguments** are for component configuration (non-HTML concerns)
|
|
8
|
+
- **`**kwargs`** are for HTML attributes that pass through to the rendered element
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
# field is positional, value: is config, **attributes are HTML
|
|
12
|
+
class Radio < Field
|
|
13
|
+
def initialize(field, value:, **attributes)
|
|
14
|
+
super(field, **attributes)
|
|
15
|
+
@value = value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# options: and multiple: are config, **attributes are HTML
|
|
20
|
+
class Select < Field
|
|
21
|
+
def initialize(field, options:, multiple: false, **attributes)
|
|
22
|
+
super(field, **attributes)
|
|
23
|
+
@options = options
|
|
24
|
+
@multiple = multiple
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The Field methods mirror this: config args are explicit, HTML attributes flow through as `**kwargs`.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
def radio(value, **attributes)
|
|
33
|
+
Components::Radio.new(field, value:, **attributes)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def select(*options, multiple: false, **attributes, &)
|
|
37
|
+
Components::Select.new(field, options:, multiple:, **attributes, &)
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Key Architecture
|
|
42
|
+
|
|
43
|
+
- **Field** is a data binding object (model, key, value, DOM name) — not HTML
|
|
44
|
+
- **Components** are HTML elements — their kwargs should be HTML attributes
|
|
45
|
+
- **Form** is a Phlex component that contains fields and renders the `<form>` tag
|
|
46
|
+
- Customization is through subclassing, not runtime options
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
superform (0.
|
|
4
|
+
superform (0.7.0)
|
|
5
5
|
phlex-rails (~> 2.0)
|
|
6
6
|
zeitwerk (~> 2.6)
|
|
7
7
|
|
|
@@ -92,6 +92,7 @@ GEM
|
|
|
92
92
|
drb (2.2.1)
|
|
93
93
|
erubi (1.13.1)
|
|
94
94
|
ffi (1.17.1-arm64-darwin)
|
|
95
|
+
ffi (1.17.1-x86_64-darwin)
|
|
95
96
|
ffi (1.17.1-x86_64-linux-gnu)
|
|
96
97
|
formatador (1.1.0)
|
|
97
98
|
globalid (1.2.1)
|
|
@@ -146,6 +147,8 @@ GEM
|
|
|
146
147
|
nio4r (2.7.4)
|
|
147
148
|
nokogiri (1.18.9-arm64-darwin)
|
|
148
149
|
racc (~> 1.4)
|
|
150
|
+
nokogiri (1.18.9-x86_64-darwin)
|
|
151
|
+
racc (~> 1.4)
|
|
149
152
|
nokogiri (1.18.9-x86_64-linux-gnu)
|
|
150
153
|
racc (~> 1.4)
|
|
151
154
|
notiffany (0.1.3)
|
|
@@ -233,6 +236,7 @@ GEM
|
|
|
233
236
|
securerandom (0.3.1)
|
|
234
237
|
shellany (0.0.1)
|
|
235
238
|
sqlite3 (2.7.3-arm64-darwin)
|
|
239
|
+
sqlite3 (2.7.3-x86_64-darwin)
|
|
236
240
|
sqlite3 (2.7.3-x86_64-linux-gnu)
|
|
237
241
|
stringio (3.1.1)
|
|
238
242
|
thor (1.3.2)
|
|
@@ -252,6 +256,7 @@ PLATFORMS
|
|
|
252
256
|
arm64-darwin-22
|
|
253
257
|
arm64-darwin-23
|
|
254
258
|
arm64-darwin-24
|
|
259
|
+
x86_64-darwin-21
|
|
255
260
|
x86_64-linux
|
|
256
261
|
|
|
257
262
|
DEPENDENCIES
|