superform 0.6.1 → 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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -3
  3. data/CLAUDE.md +46 -0
  4. data/Gemfile.lock +6 -1
  5. data/README.md +208 -75
  6. data/examples/basic_form.rb +21 -0
  7. data/examples/checkbox_form.rb +28 -0
  8. data/examples/date_time_form.rb +18 -0
  9. data/examples/example_form.rb +10 -0
  10. data/examples/select_form.rb +26 -0
  11. data/examples/special_inputs_form.rb +23 -0
  12. data/examples/textarea_form.rb +15 -0
  13. data/lib/generators/superform/install/templates/base.rb +33 -7
  14. data/lib/superform/dom.rb +13 -1
  15. data/lib/superform/field.rb +14 -31
  16. data/lib/superform/rails/choices/choice.rb +39 -0
  17. data/lib/superform/rails/choices/mapper.rb +41 -0
  18. data/lib/superform/rails/choices.rb +6 -0
  19. data/lib/superform/rails/components/base.rb +9 -2
  20. data/lib/superform/rails/components/checkbox.rb +34 -7
  21. data/lib/superform/rails/components/checkboxes.rb +38 -0
  22. data/lib/superform/rails/components/datalist.rb +34 -0
  23. data/lib/superform/rails/components/input.rb +1 -1
  24. data/lib/superform/rails/components/label.rb +1 -1
  25. data/lib/superform/rails/components/radio.rb +21 -0
  26. data/lib/superform/rails/components/radios.rb +38 -0
  27. data/lib/superform/rails/components/select.rb +52 -8
  28. data/lib/superform/rails/field.rb +91 -44
  29. data/lib/superform/version.rb +1 -1
  30. data/server/components/breadcrumb.rb +11 -0
  31. data/server/components/form_card.rb +13 -0
  32. data/server/components/layout.rb +23 -0
  33. data/server/controllers/forms_controller.rb +94 -0
  34. data/server/models/example.rb +31 -0
  35. data/server/public/styles.css +282 -0
  36. data/server/rails.rb +56 -0
  37. data/superform.gemspec +37 -0
  38. metadata +26 -4
  39. data/lib/superform/rails/option_mapper.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: accaaea2d3dd94355732e750093516e12c4dd1b113fcaecf6aae4f3cce30139d
4
- data.tar.gz: 9696d2b2de96dda5d6a7a4c80c23aaed95e3db27223daf6b1744bd5cbe842417
3
+ metadata.gz: 41ba2a10e93238ddfea45963bbc08a28c884231e128b39becdb184ceebd9c9f9
4
+ data.tar.gz: 994b8448c1e72182bb5638682f4d45b42a5b2b04b6d85ec78b1cb8cab55f9263
5
5
  SHA512:
6
- metadata.gz: c6bfa1a54025fce5d9caabafce96fdad1564bbdc95eaf1e7b8959d3fd7c021c595ac66de5da05cc511631f85f1791ed501a0ec7c973705712b227fa04cc4ba04
7
- data.tar.gz: c908864725edfc61446f4bbf678758b4efb4a171dd2634ea96d423876e3b36b3356842b075372c9559201bef26d170c3f10e696d26928841ca413d7476106593
6
+ metadata.gz: ba4d34fc66ef3fae89763ed7cf091a1d41c54df4f585b650a62ba0bee14038c2a1cdf5aa8f15b15bf5f73c201b96113f71bc13e25b3ccb85b45329e3c80844fa
7
+ data.tar.gz: b39fdedafc3f12a84ec753e0abf34f38b28632b801f0ace6a020c8d07946931d07ba6bb15f54414f13a1c065768df3e1291ef39431c3770fe8e2ddb8765af988
data/CHANGELOG.md CHANGED
@@ -1,4 +1,67 @@
1
- ## [Unreleased]
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: 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.6.1)
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