phlex-forms 0.3.0 → 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 +4 -4
- data/CHANGELOG.md +58 -0
- data/README.md +5 -7
- data/lib/forms/choices_select.rb +1 -1
- data/lib/forms/collection_check_box.rb +1 -1
- data/lib/forms/field_error.rb +1 -1
- data/lib/forms/field_hint.rb +1 -1
- data/lib/forms/form.rb +5 -6
- data/lib/forms/form_control.rb +2 -3
- data/lib/forms/rich_textarea.rb +36 -3
- data/lib/forms/row.rb +1 -2
- data/lib/forms/tag_field.rb +1 -1
- data/lib/phlex_forms/configuration.rb +2 -2
- data/lib/phlex_forms/delegated_field.rb +3 -4
- data/lib/phlex_forms/theme.rb +1 -8
- data/lib/phlex_forms/version.rb +1 -1
- data/lib/phlex_forms.rb +2 -9
- metadata +19 -6
- data/lib/phlex_forms/class_merge.rb +0 -66
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26a87406065dfe0b6d5b023949661d89548c3ad6379af9d0e4d74c4162f49070
|
|
4
|
+
data.tar.gz: b36430b1faf88ad5095a358709bce31a72869c40619dc527bc3e04be10450c0f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f9e5a95dfea504c6b487c23a0b5683a4a68391b4e07ff33192b548a40a5b7eff3a0562e0af1097a20bbeca3762017254fe626fb1fe7079408996815f8b1d7a71
|
|
7
|
+
data.tar.gz: 1ae2b903bfe9057f1705b3d3a6e788c16acf1c4d837c0bdbd723434aca71c45432d194ebf638ce17c14d597826751a6c4bb124e92f02e7b3d01fdf29ac6bbf34
|
data/CHANGELOG.md
CHANGED
|
@@ -7,8 +7,66 @@ 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
|
|
|
54
|
+
- **`rich_textarea` forwards its block into `<lexxy-editor>`**: Lexxy configures
|
|
55
|
+
an editor through CHILD elements — `<lexxy-prompt>` for @mentions,
|
|
56
|
+
`<lexxy-code-language-picker>` for code blocks — but the component rendered
|
|
57
|
+
the element with no block and `Form#rich_textarea` never forwarded one, so
|
|
58
|
+
`f.rich_textarea(:content) { ... }` silently dropped its content. A host
|
|
59
|
+
could wire @mentions and get an editor that never prompts, with no error
|
|
60
|
+
anywhere (a host app's mention endpoint 500'd for months unnoticed, because
|
|
61
|
+
nothing ever called it).
|
|
62
|
+
- **`rich_textarea` wires Active Storage's direct-upload endpoints**: Lexxy
|
|
63
|
+
uploads attachments through `data-direct-upload-url` /
|
|
64
|
+
`data-blob-url-template` on the element. Lexxy's own Rails tag helper sets
|
|
65
|
+
them; this component built the element itself and did not, so rich text
|
|
66
|
+
editors silently lost image uploads. Set as PATHS (a multi-host app must not
|
|
67
|
+
pin uploads to whichever host rendered the page first), fully guarded — no
|
|
68
|
+
Rails dependency is introduced, and a caller-supplied value always wins.
|
|
69
|
+
|
|
12
70
|
- **`FileInput` multiple uploads submit an array** (Rails `file_field` parity):
|
|
13
71
|
with `multiple:` set, the input name now gets `[]` appended (unless already
|
|
14
72
|
present), for the standalone component, the `Plain` variant, and the form
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A model-bound form builder for [Phlex](https://www.phlex.fun) — forms as
|
|
|
4
4
|
first-class Phlex classes, types inferred from your model, DaisyUI styling by
|
|
5
5
|
default (and a plain-HTML theme when you want none), and optional
|
|
6
6
|
**server-truth live validation** over
|
|
7
|
-
[phlex-reactive](https://github.com/
|
|
7
|
+
[phlex-reactive](https://github.com/zoolutions/phlex-reactive).
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
class UserForm < Forms::Base
|
|
@@ -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
|
|
|
@@ -158,7 +157,7 @@ Both work on inline forms (`f.row { … }`) and inside `fields_for` builders.
|
|
|
158
157
|
|
|
159
158
|
`as: :tags` renders a polished tag/chip input — label + error/hint chrome and
|
|
160
159
|
daisyUI styling on top of
|
|
161
|
-
[phlex-reactive](https://github.com/
|
|
160
|
+
[phlex-reactive](https://github.com/zoolutions/phlex-reactive)'s client-only tag
|
|
162
161
|
primitives (form state, no server round trips). **Requires phlex-reactive** (the `:tags` role is
|
|
163
162
|
registered only when it's loaded).
|
|
164
163
|
|
|
@@ -254,11 +253,10 @@ f.field(:search).wrapped_input(:primary) do
|
|
|
254
253
|
end
|
|
255
254
|
```
|
|
256
255
|
|
|
257
|
-
## Themes — using phlex-forms without
|
|
256
|
+
## Themes — using phlex-forms without daisyUI styling
|
|
258
257
|
|
|
259
|
-
Every component resolves through a theme (a role → component-class map).
|
|
260
|
-
the
|
|
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.
|
data/lib/forms/choices_select.rb
CHANGED
|
@@ -44,7 +44,7 @@ module Forms
|
|
|
44
44
|
a = {
|
|
45
45
|
name: select_name,
|
|
46
46
|
id: @id,
|
|
47
|
-
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
|
}
|
data/lib/forms/field_error.rb
CHANGED
data/lib/forms/field_hint.rb
CHANGED
data/lib/forms/form.rb
CHANGED
|
@@ -78,8 +78,8 @@ module Forms
|
|
|
78
78
|
render theme[:submit].new(*, model: @model, **, &)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
def rich_textarea(name, *modifiers,
|
|
82
|
-
render field_object(name).rich_textarea(*modifiers, **)
|
|
81
|
+
def rich_textarea(name, *modifiers, **, &)
|
|
82
|
+
render field_object(name).rich_textarea(*modifiers, **), &
|
|
83
83
|
end
|
|
84
84
|
alias rich_text_area rich_textarea
|
|
85
85
|
|
|
@@ -224,10 +224,9 @@ module Forms
|
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
def form_classes
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
data/lib/forms/form_control.rb
CHANGED
|
@@ -40,9 +40,8 @@ module Forms
|
|
|
40
40
|
private
|
|
41
41
|
|
|
42
42
|
def control_classes
|
|
43
|
-
|
|
44
|
-
|
|
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
|
data/lib/forms/rich_textarea.rb
CHANGED
|
@@ -14,21 +14,54 @@ module Forms
|
|
|
14
14
|
@id = id || name.to_s.gsub(/[\[\]]/, "_").gsub(/_+$/, "")
|
|
15
15
|
@value = value
|
|
16
16
|
@options = options
|
|
17
|
+
apply_direct_upload_defaults
|
|
17
18
|
super()
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
# Lexxy configures the editor through CHILD elements — `<lexxy-prompt>` for
|
|
22
|
+
# @mentions, `<lexxy-code-language-picker>` for code blocks — so the block
|
|
23
|
+
# has to reach the element, not be dropped.
|
|
24
|
+
def view_template(&)
|
|
21
25
|
lexxy_editor(
|
|
22
26
|
name: @name,
|
|
23
27
|
id: @id,
|
|
24
28
|
value: formatted_value,
|
|
25
29
|
class: editor_classes,
|
|
26
|
-
**@options.except(:class, :error)
|
|
30
|
+
**@options.except(:class, :error),
|
|
31
|
+
&
|
|
27
32
|
)
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
private
|
|
31
36
|
|
|
37
|
+
# Lexxy uploads attachments through Active Storage's direct-upload
|
|
38
|
+
# endpoints, read off the element. Lexxy's own Rails tag helper sets them;
|
|
39
|
+
# this component builds the element itself, so without these an editor
|
|
40
|
+
# silently loses image uploads. Paths, not URLs: a multi-host app must not
|
|
41
|
+
# pin uploads to whichever host rendered the page first.
|
|
42
|
+
#
|
|
43
|
+
# Fully guarded — the gem has no Rails dependency, and a host without
|
|
44
|
+
# Active Storage (or with its own values) is left alone.
|
|
45
|
+
def apply_direct_upload_defaults
|
|
46
|
+
helpers = rails_url_helpers or return
|
|
47
|
+
|
|
48
|
+
data = (@options[:data] ||= {})
|
|
49
|
+
data[:direct_upload_url] ||= helpers.rails_direct_uploads_path
|
|
50
|
+
data[:blob_url_template] ||= helpers.rails_service_blob_path(":signed_id", ":filename")
|
|
51
|
+
rescue StandardError
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def rails_url_helpers
|
|
56
|
+
return unless defined?(Rails) && Rails.respond_to?(:application)
|
|
57
|
+
|
|
58
|
+
helpers = Rails.application&.routes&.url_helpers
|
|
59
|
+
return unless helpers.respond_to?(:rails_direct_uploads_path)
|
|
60
|
+
return unless helpers.respond_to?(:rails_service_blob_path)
|
|
61
|
+
|
|
62
|
+
helpers
|
|
63
|
+
end
|
|
64
|
+
|
|
32
65
|
def formatted_value
|
|
33
66
|
return nil if @value.nil? || (@value.respond_to?(:blank?) && @value.blank?)
|
|
34
67
|
|
|
@@ -37,7 +70,7 @@ module Forms
|
|
|
37
70
|
end
|
|
38
71
|
|
|
39
72
|
def editor_classes
|
|
40
|
-
|
|
73
|
+
DaisyUI::ClassMerge.merge(@options[:class], "lexxy-content")
|
|
41
74
|
end
|
|
42
75
|
end
|
|
43
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
|
-
|
|
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
|
data/lib/forms/tag_field.rb
CHANGED
|
@@ -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 =
|
|
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
|
|
54
|
+
# Defaults to daisy.
|
|
55
55
|
attr_writer :theme
|
|
56
56
|
|
|
57
57
|
def theme
|
|
58
|
-
@theme ||=
|
|
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
|
-
#
|
|
46
|
-
#
|
|
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
|
-
|
|
50
|
+
["w-full", *@attributes[:class]]
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
# Unstyled variant of binding_attributes for the Plain theme: caller classes
|
data/lib/phlex_forms/theme.rb
CHANGED
|
@@ -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
|
|
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,
|
data/lib/phlex_forms/version.rb
CHANGED
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,
|
|
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.
|
|
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
|
|
@@ -179,13 +192,13 @@ files:
|
|
|
179
192
|
- lib/phlex_forms/version.rb
|
|
180
193
|
- lib/rubocop/phlex_forms/cop/legacy_form_method.rb
|
|
181
194
|
- lib/rubocop/phlex_forms/cop/raw_form.rb
|
|
182
|
-
homepage: https://github.com/
|
|
195
|
+
homepage: https://github.com/zoolutions/phlex-forms
|
|
183
196
|
licenses:
|
|
184
197
|
- MIT
|
|
185
198
|
metadata:
|
|
186
|
-
source_code_uri: https://github.com/
|
|
187
|
-
changelog_uri: https://github.com/
|
|
188
|
-
bug_tracker_uri: https://github.com/
|
|
199
|
+
source_code_uri: https://github.com/zoolutions/phlex-forms
|
|
200
|
+
changelog_uri: https://github.com/zoolutions/phlex-forms/blob/main/CHANGELOG.md
|
|
201
|
+
bug_tracker_uri: https://github.com/zoolutions/phlex-forms/issues
|
|
189
202
|
rubygems_mfa_required: 'true'
|
|
190
203
|
rdoc_options: []
|
|
191
204
|
require_paths:
|
|
@@ -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
|