phlex-forms 0.3.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9541561e98ecd2afda34daf77d2eeebd725aac0badde75fdbf51401628d081f6
4
- data.tar.gz: 5d4008e8e6e5748ae12fdbcd12a667d016ab30549da71498dbc2e286fe202d81
3
+ metadata.gz: 26a87406065dfe0b6d5b023949661d89548c3ad6379af9d0e4d74c4162f49070
4
+ data.tar.gz: b36430b1faf88ad5095a358709bce31a72869c40619dc527bc3e04be10450c0f
5
5
  SHA512:
6
- metadata.gz: f10fd9155f6ff9841bdae9c34fb2ac89dbbdbc49df33c012af7efad526fa914d21c6379430e1ee66a1aadeff387dcc9bab353d33cb017ce8b07798dd8058dd2c
7
- data.tar.gz: 7ea1bdaa7fea101bde79b7975759c79b30bc502fb50c2b18e279a5cf4032b294745055e23f2f66ed22cba33c4e43c637c6499ea3c62059f425a3b3aec141ad83
6
+ metadata.gz: f9e5a95dfea504c6b487c23a0b5683a4a68391b4e07ff33192b548a40a5b7eff3a0562e0af1097a20bbeca3762017254fe626fb1fe7079408996815f8b1d7a71
7
+ data.tar.gz: 1ae2b903bfe9057f1705b3d3a6e788c16acf1c4d837c0bdbd723434aca71c45432d194ebf638ce17c14d597826751a6c4bb124e92f02e7b3d01fdf29ac6bbf34
data/CHANGELOG.md CHANGED
@@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Upgrading to 0.4
11
+
12
+ phlex-forms now depends on **daisyui `~> 2.0`** and merges every default +
13
+ caller `class:` through `DaisyUI::ClassMerge` (a port of tailwind_merge 1.5.6
14
+ plus daisyUI's modifier groups). `PhlexForms::ClassMerge` is gone.
15
+
16
+ - **Bundle daisyui 2.** It's a hard dependency now, so a `gem "daisyui", "~> 1.x"`
17
+ pin in your Gemfile must move to 2.x (or be dropped).
18
+ - **Register your custom Tailwind utilities.** The old merger only knew daisy
19
+ sizes/colors and `w-*`, and passed everything else through. The new one
20
+ classifies every utility, so an unregistered custom one can be dropped as a
21
+ conflict. For example, a `text-h1` you pass next to `FieldError`'s
22
+ `text-error` reads as a colour:
23
+
24
+ ```ruby
25
+ # config/initializers/daisy_ui.rb
26
+ DaisyUI.configure do |config|
27
+ config.class_merge.utility("text-h1", like: "text-lg")
28
+ end
29
+ ```
30
+
31
+ - **`CollectionCheckBox` merges a caller `class:` instead of replacing its
32
+ base class.** `class: "checkbox-primary"` now renders `checkbox
33
+ checkbox-primary` (it used to drop `checkbox`). A caller who used `class:` to
34
+ swap the base class entirely (`class: "toggle"`) now gets both classes.
35
+ - **More conflicts resolve now.** A caller's `mt-4` / `text-xs` replaces a
36
+ component's default `mt-1` / `text-sm` instead of stacking beside it.
37
+ - **Daisy leaves lean on daisyui's own merge.** Input, Select, Textarea and the
38
+ other delegated leaves hand `class:` to the daisyui component, so
39
+ `DaisyUI.configure { |c| c.class_merge.enabled = false }` applies to them too.
40
+ The components that render plain HTML (FieldHint, FieldError, Row, Form,
41
+ FormControl, TagField, ChoicesSelect) merge through `DaisyUI::ClassMerge.merge`
42
+ directly, which that switch doesn't affect.
43
+
44
+ ### Removed
45
+
46
+ - **`PhlexForms::ClassMerge`**: use `DaisyUI::ClassMerge.merge`.
47
+ - **The daisyui soft-dependency path.** `require "phlex_forms"` loads daisyui
48
+ unconditionally, the daisy theme is always the default, and `Theme.daisy` no
49
+ longer raises `FeatureUnavailable`. The Plain theme is unchanged and still
50
+ emits zero daisy classes.
51
+
10
52
  ### Fixed
11
53
 
12
54
  - **`rich_textarea` forwards its block into `<lexxy-editor>`**: Lexxy configures
data/README.md CHANGED
@@ -42,7 +42,6 @@ end
42
42
  ```ruby
43
43
  # Gemfile
44
44
  gem "phlex-forms"
45
- gem "daisyui" # optional — the daisy theme; omit for plain semantic HTML
46
45
  gem "phlex-reactive" # optional — the `live` server-truth validation
47
46
  ```
48
47
 
@@ -254,11 +253,10 @@ f.field(:search).wrapped_input(:primary) do
254
253
  end
255
254
  ```
256
255
 
257
- ## Themes — using phlex-forms without daisyui
256
+ ## Themes — using phlex-forms without daisyUI styling
258
257
 
259
- Every component resolves through a theme (a role → component-class map). With
260
- the daisyui gem loaded, the daisy theme is the default. Without it — or on
261
- demand — the **Plain theme** renders bare semantic HTML: the same binding
258
+ Every component resolves through a theme (a role → component-class map). The
259
+ daisy theme is the default. On demand, the **Plain theme** renders bare semantic HTML: the same binding
262
260
  (names, ids, values, required, errors), variants accepted and ignored, no
263
261
  styling classes, and stable hooks (`aria-invalid`, `role="alert"`,
264
262
  `data-field-error`, `data-field-hint`, `data-form-row`) for your own CSS.
@@ -44,7 +44,7 @@ module Forms
44
44
  a = {
45
45
  name: select_name,
46
46
  id: @id,
47
- class: PhlexForms::ClassMerge.merge("choices-select w-full", @options[:class]),
47
+ class: DaisyUI::ClassMerge.merge("choices-select w-full", @options[:class]),
48
48
  data: stimulus_data,
49
49
  **@options.except(:class, :value, :data)
50
50
  }
@@ -18,7 +18,7 @@ module Forms
18
18
  name: @name,
19
19
  id: @id,
20
20
  value: @value,
21
- class: @options[:class] || "checkbox",
21
+ class: DaisyUI::ClassMerge.merge("checkbox", @options[:class]),
22
22
  checked: @checked || nil
23
23
  )
24
24
  end
@@ -20,7 +20,7 @@ module Forms
20
20
  private
21
21
 
22
22
  def classes
23
- PhlexForms::ClassMerge.merge("text-error text-sm mt-1", @options[:class])
23
+ DaisyUI::ClassMerge.merge("text-error text-sm mt-1", @options[:class])
24
24
  end
25
25
  end
26
26
  end
@@ -18,7 +18,7 @@ module Forms
18
18
  private
19
19
 
20
20
  def classes
21
- PhlexForms::ClassMerge.merge("text-base-content/60 text-sm mt-1", @options[:class])
21
+ DaisyUI::ClassMerge.merge("text-base-content/60 text-sm mt-1", @options[:class])
22
22
  end
23
23
  end
24
24
  end
data/lib/forms/form.rb CHANGED
@@ -224,10 +224,9 @@ module Forms
224
224
  end
225
225
 
226
226
  def form_classes
227
- classes = []
228
- classes << "space-y-4" if @base_modifiers.include?(:spaced)
229
- classes << "space-y-6" if @base_modifiers.include?(:spacious)
230
- PhlexForms::ClassMerge.merge(classes.join(" "), @options[:class]).presence
227
+ spaced = "space-y-4" if @base_modifiers.include?(:spaced)
228
+ spacious = "space-y-6" if @base_modifiers.include?(:spacious)
229
+ DaisyUI::ClassMerge.merge(spaced, spacious, @options[:class]).presence
231
230
  end
232
231
 
233
232
  def authenticity_token_field
@@ -40,9 +40,8 @@ module Forms
40
40
  private
41
41
 
42
42
  def control_classes
43
- base = %w[form-control w-full]
44
- base << "flex flex-row items-center gap-4" if @modifiers.include?(:horizontal)
45
- PhlexForms::ClassMerge.merge(base.join(" "), @options[:class])
43
+ horizontal = "flex flex-row items-center gap-4" if @modifiers.include?(:horizontal)
44
+ DaisyUI::ClassMerge.merge("form-control w-full", horizontal, @options[:class])
46
45
  end
47
46
  end
48
47
  end
@@ -70,7 +70,7 @@ module Forms
70
70
  end
71
71
 
72
72
  def editor_classes
73
- [@options[:class], "lexxy-content"].compact.join(" ")
73
+ DaisyUI::ClassMerge.merge(@options[:class], "lexxy-content")
74
74
  end
75
75
  end
76
76
  end
data/lib/forms/row.rb CHANGED
@@ -27,8 +27,7 @@ module Forms
27
27
  private
28
28
 
29
29
  def row_classes
30
- base = ["grid grid-cols-1 gap-4", COLUMN_CLASSES[@columns]].compact.join(" ")
31
- PhlexForms::ClassMerge.merge(base, @options[:class])
30
+ DaisyUI::ClassMerge.merge("grid grid-cols-1 gap-4", COLUMN_CLASSES[@columns], @options[:class])
32
31
  end
33
32
  end
34
33
  end
@@ -135,6 +135,6 @@ module Forms
135
135
  def option_classes = nil
136
136
  def chip_classes = "badge badge-primary gap-1"
137
137
  def remove_classes = "cursor-pointer"
138
- def input_classes = PhlexForms::ClassMerge.merge("input w-full", @attributes[:class])
138
+ def input_classes = DaisyUI::ClassMerge.merge("input w-full", @attributes[:class])
139
139
  end
140
140
  end
@@ -51,11 +51,11 @@ module PhlexForms
51
51
  end
52
52
 
53
53
  # The default theme: :daisy, :plain, or a PhlexForms::Theme instance.
54
- # Defaults to daisy when the daisyui gem is loaded, plain otherwise.
54
+ # Defaults to daisy.
55
55
  attr_writer :theme
56
56
 
57
57
  def theme
58
- @theme ||= defined?(DaisyUI) ? Theme.daisy : Theme.plain
58
+ @theme ||= Theme.daisy
59
59
  end
60
60
 
61
61
  def infer_from_model
@@ -42,13 +42,12 @@ module PhlexForms
42
42
  attrs.compact
43
43
  end
44
44
 
45
- # Merged via ClassMerge so a caller width (`w-36`) REPLACES the default
46
- # instead of stacking with it (`w-full w-36` leaves the winner to stylesheet
47
- # source order — that's how admin filter selects went full-width, zazu#2934).
45
+ # Handed to the daisyui component as parts: its class merge lets a caller
46
+ # width (`w-36`) REPLACE the default instead of stacking with it (zazu#2934).
48
47
  def width_class
49
48
  return @attributes[:class] unless @full_width
50
49
 
51
- ClassMerge.merge("w-full", @attributes[:class])
50
+ ["w-full", *@attributes[:class]]
52
51
  end
53
52
 
54
53
  # Unstyled variant of binding_attributes for the Plain theme: caller classes
@@ -9,8 +9,7 @@ module PhlexForms
9
9
  # visual variant nor an error state.
10
10
  #
11
11
  # Built-ins:
12
- # Theme.daisy — the DaisyUI-delegating Forms::* leaves (default when the
13
- # daisyui gem is loaded)
12
+ # Theme.daisy — the DaisyUI-delegating Forms::* leaves (the default)
14
13
  # Theme.plain — bare semantic HTML (Forms::Plain::*): variants accepted and
15
14
  # ignored, zero styling classes, aria/data hooks only
16
15
  #
@@ -47,12 +46,6 @@ module PhlexForms
47
46
  end
48
47
 
49
48
  def daisy
50
- unless defined?(DaisyUI)
51
- raise PhlexForms::FeatureUnavailable,
52
- "the daisy theme requires the daisyui gem. Add `gem \"daisyui\"` to your " \
53
- "Gemfile, or use the plain theme."
54
- end
55
-
56
49
  @daisy ||= new({
57
50
  input: Forms::Input, select: Forms::Select, choices_select: Forms::ChoicesSelect,
58
51
  textarea: Forms::Textarea, rich_textarea: Forms::RichTextarea,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexForms
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/phlex_forms.rb CHANGED
@@ -7,14 +7,7 @@ require "active_support/core_ext/string" # underscore/pluralize/humanize (scope/
7
7
  require "phlex"
8
8
  require "glyphs"
9
9
  require "zeitwerk"
10
-
11
- begin
12
- require "daisy_ui"
13
- rescue LoadError
14
- # daisyui is a soft dependency: without it the Plain theme is the default.
15
- # The daisy leaf components only reference DaisyUI inside view_template, so
16
- # they load fine — they just must not be rendered (Theme.daisy raises).
17
- end
10
+ require "daisy_ui"
18
11
 
19
12
  begin
20
13
  require "phlex/reactive"
@@ -75,7 +68,7 @@ loader.inflector.inflect(
75
68
  )
76
69
 
77
70
  # Two sibling (non-nested) autoload roots:
78
- # lib/phlex_forms -> PhlexForms:: (gem internals: config, class_merge, ...)
71
+ # lib/phlex_forms -> PhlexForms:: (gem internals: config, inference, theme, ...)
79
72
  # lib/forms -> Forms:: (the component kit)
80
73
  # Keeping them as separate top-level dirs avoids the ambiguity of nesting one
81
74
  # namespace root inside another.
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.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -29,6 +29,20 @@ dependencies:
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '9'
32
+ - !ruby/object:Gem::Dependency
33
+ name: daisyui
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '2.0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '2.0'
32
46
  - !ruby/object:Gem::Dependency
33
47
  name: glyphs
34
48
  requirement: !ruby/object:Gem::Requirement
@@ -168,7 +182,6 @@ files:
168
182
  - lib/phlex-forms.rb
169
183
  - lib/phlex_forms.rb
170
184
  - lib/phlex_forms/builder.rb
171
- - lib/phlex_forms/class_merge.rb
172
185
  - lib/phlex_forms/configuration.rb
173
186
  - lib/phlex_forms/delegated_field.rb
174
187
  - lib/phlex_forms/engine.rb
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PhlexForms
4
- # Class-string merging for phlex-forms components.
5
- #
6
- # DaisyUI component-class families are mutually exclusive: an element can't be
7
- # both `input-sm` and `input-lg`, or both `input-primary` and `input-error`.
8
- # When a component's default classes and a caller's `class:` override both name
9
- # the same family, the LAST occurrence wins — matching Tailwind/daisyui
10
- # intuition (the later class overrides the earlier).
11
- #
12
- # Width utilities (`w-*`) are the one core-Tailwind family this gem injects a
13
- # default for (DelegatedField's `w-full`), so they conflict the same way: a
14
- # caller's `w-36` must REPLACE the default, or both land on the element and
15
- # stylesheet source order — not author intent — decides which applies.
16
- #
17
- # We deliberately do NOT depend on tailwind_merge: daisyui modifier families
18
- # and the width family are the only conflicts that occur on form fields, so
19
- # tailwind_merge would add a runtime dependency for conflicts this handles.
20
- # Other non-conflicting utilities (`py-0`, `min-w-32`, ...) pass through in
21
- # order.
22
- module ClassMerge
23
- SIZE = /-(xs|sm|md|lg|xl)\z/
24
- COLOR = /-(primary|secondary|accent|neutral|info|success|warning|error)\z/
25
- # Anchored so min-w-*/max-w-* (different CSS properties) stay out of the family.
26
- WIDTH = /\Aw-/
27
-
28
- module_function
29
-
30
- # Merge any number of class strings/nil into one deduped string. Preserves
31
- # order; within a daisyui size/color family only the last token survives.
32
- def merge(*parts)
33
- tokens = parts.flat_map { |p| p.to_s.split }.reject(&:empty?)
34
- dedupe_daisyui_families(tokens).join(" ")
35
- end
36
-
37
- # For each (component-prefix, family) pair, keep only the last token.
38
- def dedupe_daisyui_families(tokens)
39
- last_index = {}
40
- tokens.each_with_index do |tok, idx|
41
- key = family_key(tok)
42
- last_index[key] = idx if key
43
- end
44
- tokens.each_with_index.filter_map do |tok, idx|
45
- key = family_key(tok)
46
- next tok if key.nil? # not a daisyui family token — keep
47
- next tok if last_index[key] == idx # last in its family — keep
48
-
49
- nil # earlier duplicate in the family — drop
50
- end
51
- end
52
-
53
- # A stable bucket key like "width" / "input:size" / "select:color", or nil if
54
- # the token is not a recognized conflicting family. WIDTH is checked first so
55
- # a `w-*` token can never be mis-bucketed as a daisyui modifier.
56
- def family_key(token)
57
- return "width" if WIDTH.match?(token)
58
-
59
- if (m = token.match(SIZE))
60
- "#{token[0...m.begin(0)]}:size"
61
- elsif (m = token.match(COLOR))
62
- "#{token[0...m.begin(0)]}:color"
63
- end
64
- end
65
- end
66
- end