phlex-forms 0.2.5 → 0.2.6

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: '0038bad89aee72fb6b4e162bfc83dc9afbaadbc2280f3ea23be2985e81774d41'
4
- data.tar.gz: 9b35b71658c4ffbb14667a79e6e85e4a0437d6953b64921272ae629a443a0ac2
3
+ metadata.gz: fd321ccbe2bec3d9b8966d0eda0e1036e4143af38b4aa55dec702ce8c6d23137
4
+ data.tar.gz: 2abe25e3c63dd5d689a6c6d762df3ed6204c0dca0ef6b7d569f6e8c3da4ee1d7
5
5
  SHA512:
6
- metadata.gz: b8ada79a4a87a79096b16f82d000ea0e5620096ebe4b8fff9560ae5e556ff982ff6e13ccb52c77e0d7ea72e0d094055595870468de9c40d9f498350bab399eb4
7
- data.tar.gz: bce76ac9a71f33cab8c15f6f4b529b636da88d802c4b86ba07ee90948daaa1a2e1c0637f898a11be75e37ab4b8dd7abb8f42ce654a9481907b5cbcc5a299263a
6
+ metadata.gz: fb8b8768817fef4b18e0dacadbfb966cf7356339badfce5e96ea04ccc7e0d5bcc60686e65288413376bbe7873e17993746b35dceefb917a58132505ef88f9094
7
+ data.tar.gz: e7f775c0c15db6e595f9ac5c93449fb49b12d032f1a615f231b5e23f1aa2ac188fb2eb21a6afccf073758227a3e100a69def8f24185baadd63ddc19e923a48e0
data/CHANGELOG.md CHANGED
@@ -23,6 +23,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
23
  the Control's own visible `label:` / `hint:` get stable ids and the field wires
24
24
  `aria-labelledby` / `aria-describedby` at them (no duplicate markup). Absent a
25
25
  name, output is unchanged.
26
+ - **`checkbox_group` `item_label:`** — the per-item text accessor, so a
27
+ `f.field(:tags, as: :checkbox_group, label: "Tags", item_label: ->(t){…})`
28
+ renders a visible group heading (`label:`, via the Control) AND custom item
29
+ labels at once — previously `label:` on the `f.field` path was eaten by the
30
+ heading and items fell back to `to_s`. `item_label:` wins over `label:` for the
31
+ item text; on the bare verb `label:` stays the item accessor and `item_label:`
32
+ is an alias. Absent it, behavior is unchanged.
26
33
 
27
34
  ### Changed
28
35
 
data/README.md CHANGED
@@ -368,9 +368,13 @@ f.checkbox_group(:tag_ids, Tag.all, value: :id,
368
368
  variant: :pill, # :stack (default) | :inline | :pill
369
369
  size: :sm, # daisyUI checkbox size
370
370
  aria: { label: "Tags" }) # names the group for screen readers
371
- # ...or through field inference (the field's label/hint name the group):
371
+ # ...or through field inference. Here `label:`/`hint:` are the field's VISIBLE
372
+ # heading + description (rendered by the Control, which also names the group);
373
+ # `item_label:` gives the per-item text, so you get a heading AND custom item
374
+ # labels at once:
372
375
  f.field :tag_ids, as: :checkbox_group, collection: Tag.all, value: :id,
373
- label: "Tags", hint: "Pick any"
376
+ label: "Tags", hint: "Pick any",
377
+ item_label: ->(t) { t.name.presence || t.slug }, variant: :pill
374
378
 
375
379
  f.collection_select(:country_id, Country.all, :id, :name, prompt: "Select…")
376
380
  ```
@@ -381,6 +385,13 @@ set comes from the model's current value matched by each item's resolved
381
385
  `value:` — re-rendering an edit form pre-checks the right boxes. The `:pill`
382
386
  variant styles the active chip with Tailwind's `has-[:checked]:` (no JS).
383
387
 
388
+ **Two labels, no collision.** Through `f.field`, `label:` is the field's visible
389
+ group heading (the Control renders it); the per-item text comes from `item_label:`
390
+ (a Symbol method or Proc). On the bare `f.checkbox_group` verb there is no Control
391
+ heading, so `label:` *is* the per-item accessor (and `item_label:` is accepted as
392
+ an alias). Either way `value:` is the submitted value; `item_label:` wins over
393
+ `label:` for the item text when both are present.
394
+
384
395
  A `role="group"` needs an **accessible name** for assistive tech. The verb has
385
396
  no bespoke naming option — HTML/ARIA attributes pass straight through to the
386
397
  group, so name it with plain `aria:` (`aria: { label: "Tags" }` for a literal
@@ -8,10 +8,13 @@ module Forms
8
8
  # resolved value: of its item against the model's current set.
9
9
  #
10
10
  # f.checkbox_group(:tag_ids, Tag.all, value: :id,
11
- # label: ->(t) { t.name.presence || t.slug }, variant: :pill, size: :sm)
11
+ # item_label: ->(t) { t.name.presence || t.slug }, variant: :pill, size: :sm)
12
12
  #
13
- # value: method or proc -> the submitted value of each item (default :id)
14
- # label: method or proc -> the visible text of each item (default :to_s)
13
+ # The item value/text accessors (value:/label:/item_label:) live on the BUILDER
14
+ # (Forms::Field#checkbox_group), which pre-resolves each item to
15
+ # { value:, label:, checked:, id: } before this leaf renders. This leaf is
16
+ # presentation-only — it receives the resolved options: array, never the raw
17
+ # accessors.
15
18
  # variant: :stack (default) | :inline | :pill — layout only, zero JS
16
19
  # size: daisyUI checkbox size modifier (:xs :sm :md :lg :xl)
17
20
  #
data/lib/forms/field.rb CHANGED
@@ -112,8 +112,20 @@ module Forms
112
112
  #
113
113
  # field.checkbox_group(Tag.all, value: :id, label: ->(t) { t.name })
114
114
  #
115
- # value:/label: are a method name (Symbol) or a proc taking the item.
116
- def checkbox_group(collection, value: :id, label: :to_s, **)
115
+ # value: is a method name (Symbol) or a proc taking the item -> its submitted
116
+ # value. The per-item visible text comes from item_label: (Symbol/Proc/String)
117
+ # if given, else label:; when NEITHER is given each item is labelled by the
118
+ # first of name/title/label/to_s it responds to (the same LABEL_METHODS chain
119
+ # Inference uses for association choices) — so a plain
120
+ # `f.field(:tags, as: :checkbox_group, label: "Tags")` shows readable item
121
+ # text without an explicit accessor.
122
+ #
123
+ # item_label: exists so the `f.field` path can pass a visible group heading as
124
+ # `label:` (consumed by the Control) AND still customize the per-item text
125
+ # here — the two no longer collide. item_label: is consumed here; it never
126
+ # leaks to the group div.
127
+ def checkbox_group(collection, value: :id, label: nil, item_label: nil, **)
128
+ text = item_label || label
117
129
  # The model's current value is already the raw values (e.g. record.tag_ids
118
130
  # => [1, 3]), so compare against them directly — don't re-resolve value:.
119
131
  selected = Array(field_value)
@@ -121,7 +133,7 @@ module Forms
121
133
  item_value = resolve_item(item, value)
122
134
  {
123
135
  value: item_value,
124
- label: resolve_item(item, label),
136
+ label: text ? resolve_item(item, text) : infer_item_label(item),
125
137
  checked: selected.include?(item_value),
126
138
  id: "#{field_id}_#{item_value}"
127
139
  }
@@ -256,9 +268,25 @@ module Forms
256
268
  validator.options.key?(:if) || validator.options.key?(:unless) || validator.options.key?(:on)
257
269
  end
258
270
 
259
- # value:/label: for checkbox_group: a Proc taking the item, or a method name.
271
+ # value:/label:/item_label: for checkbox_group. A Proc is called with the
272
+ # item; a String is literal text (the same for every item — no method
273
+ # dispatch, so a stray string can't NoMethodError); anything else (a Symbol)
274
+ # is sent to the item as a method name.
260
275
  def resolve_item(item, accessor)
261
- accessor.respond_to?(:call) ? accessor.call(item) : item.public_send(accessor)
276
+ case accessor
277
+ when Proc then accessor.call(item)
278
+ when String then accessor
279
+ else item.public_send(accessor)
280
+ end
281
+ end
282
+
283
+ # Default per-item label when no label:/item_label: was given: the first of
284
+ # name/title/label/to_s the item responds to (mirrors PhlexForms::Inference's
285
+ # LABEL_METHODS for association choices, so option text is picked the same way
286
+ # across the gem).
287
+ def infer_item_label(item)
288
+ method = PhlexForms::Inference::LABEL_METHODS.find { |m| item.respond_to?(m) }
289
+ item.public_send(method || :to_s)
262
290
  end
263
291
 
264
292
  def field_attributes
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexForms
4
- VERSION = "0.2.5"
4
+ VERSION = "0.2.6"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phlex-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson