phlex-forms 0.2.2 → 0.2.3

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: c814b751d3d8dd050595b4d29aa7a261dd0b5dbb15787912a58e676fe09d76a9
4
- data.tar.gz: 1c9bb1ff0df9584b34a7e9e67a4698a04569d985b0406f9e9d79e60d2abe160f
3
+ metadata.gz: 536c63a40e17d971808a285a30d6bd8a96a0f0049ecdd1fc2472e39d2359337b
4
+ data.tar.gz: 81460e049eccf72d1181420e705834b111bcd64aa5e4865f04fa811350c423f1
5
5
  SHA512:
6
- metadata.gz: 6d86f0ca8fa5497a118b5e137e9bd23b67a68b4db6076cad19a38d6dc4bad4e3d42a5b3958077666e5f1370658b6069dc9787d816c3904c4717c5f5725d7cac0
7
- data.tar.gz: fbe09ab98824da11331b9a6d4abaf502356bb617157b12a68a82096eab790b2d5e794bd2457e8ccd94651e418b69dbab76317e3bfb6542f7dfe7d60d37479d25
6
+ metadata.gz: 1479141adae7a0f38ed945d1cd572b6a1ad6c0504964d6690e0f4db1ed1a901f8f9ba99c6e2af497020bce01adbca8fbe1ab36c46f59815296c6b46bc894f482
7
+ data.tar.gz: a72f1a2367fc0e0ddcc155241805e5c70943c7d11ebe5a08ad88f2e33f2094e7a03b14ecbd61fc4f205072f0473f3e6ba9cf5413ed3ce3a8313a920db5309d1e
data/README.md CHANGED
@@ -90,7 +90,7 @@ control wrapping the label, the input, and an error (or hint).
90
90
  | --- | --- |
91
91
  | `label:` | Label text. Defaults to the model's humanized attribute name. `label: false` omits it. |
92
92
  | `hint:` | Help text shown when there is no error. |
93
- | `as:` | Override the control: `:select`, `:textarea`, `:toggle`, `:checkbox`, `:file`, `:radio`, `:hidden`, `:rich_textarea`, or any text-like type. |
93
+ | `as:` | Override the control: `:select`, `:textarea`, `:toggle`, `:checkbox`, `:file`, `:radio`, `:hidden`, `:rich_textarea`, `:tags` (see [Tag fields](#tag-fields)), or any text-like type. |
94
94
  | `required:` | Force the required flag. Otherwise inferred from the model's presence validators. |
95
95
  | `choices:` | Choices for a select (implies `as: :select`). |
96
96
  | positional modifiers | daisyui variants — `:primary`, `:lg`, `:ghost`, … — stacked onto the input. |
@@ -154,6 +154,67 @@ end
154
154
 
155
155
  Both work on inline forms (`f.row { … }`) and inside `fields_for` builders.
156
156
 
157
+ ### Tag fields
158
+
159
+ `as: :tags` renders a polished tag/chip input — label + error/hint chrome and
160
+ daisyUI styling on top of
161
+ [phlex-reactive](https://github.com/mhenrixon/phlex-reactive)'s client-only tag
162
+ primitives (form state, no server round trips). **Requires phlex-reactive** (the `:tags` role is
163
+ registered only when it's loaded).
164
+
165
+ ```ruby
166
+ f.field :tags, as: :tags, suggestions: %w[Ruby Rails Hotwire Postgres]
167
+
168
+ # Hash form: the value is a "haystack" of synonyms the type-ahead filter matches
169
+ f.field :tags, as: :tags, suggestions: { "Postgres" => "postgres database db sql" }
170
+ ```
171
+
172
+ The widget submits **one comma-joined param** (`user[tags] = "Ruby,Rails"`) — the
173
+ primitive's wire contract. The visible type-ahead input carries **no `name`**, so
174
+ it never posts a stray param; only a hidden field does.
175
+
176
+ Have the model split the comma-joined string back into an array:
177
+
178
+ ```ruby
179
+ class Post < ApplicationRecord
180
+ attribute :tags, default: [] # a text[] / JSONB column, say
181
+
182
+ # accept the widget's "Ruby,Rails" and a normal Array alike
183
+ def tags=(value)
184
+ super(value.is_a?(String) ? value.split(",").map(&:strip).reject(&:empty?) : value)
185
+ end
186
+ end
187
+ ```
188
+
189
+ Notes:
190
+
191
+ - **Custom styling** — the leaf reads daisyUI classes from overridable seams
192
+ (`root_classes`, `chip_classes`, `menu_classes`, …). The plain theme's twin
193
+ (`Forms::Plain::TagField`) keeps the full client wire contract but ships zero
194
+ styling classes; the invalid state rides `aria-invalid` on the query input.
195
+ - **Inside a `live` form**, declare `live_tags` so the outer form validates the
196
+ tags too. A standalone tag widget is a *nested* reactive root, so the live
197
+ root would skip its hidden field — `live_tags` lifts the widget's wire
198
+ attributes onto the `<form>` root and renders the widget rootless, so the form
199
+ owns the hidden field and `:validate` sees the value:
200
+
201
+ ```ruby
202
+ class PostForm < Forms::Base
203
+ live model: Post
204
+ live_tags :tags, suggestions: %w[Ruby Rails Hotwire] # lift onto the form root
205
+
206
+ def fields
207
+ field :title
208
+ field :tags, as: :tags # renders rootless; the form validates it
209
+ submit :primary
210
+ end
211
+ end
212
+ ```
213
+
214
+ phlex-reactive's tag controller reads **one** tag field per root, so a live
215
+ form lifts **at most one** — a second `live_tags` raises. A second tag input
216
+ must stay a standalone (non-live) `field :other, as: :tags`.
217
+
157
218
  ## Escape hatches & custom widgets
158
219
 
159
220
  The lower-level component methods are always available with stable signatures:
data/lib/forms/field.rb CHANGED
@@ -91,6 +91,17 @@ module Forms
91
91
  end
92
92
  end
93
93
 
94
+ # A model-bound tag/chip input (phlex-reactive client-only primitives).
95
+ # suggestions: an Array of tags or a Hash of tag => haystack (synonyms the
96
+ # filter matches). Submits one comma-joined param under the field name.
97
+ def tag_field(*modifiers, suggestions: [], **)
98
+ theme[:tag_field].new(
99
+ *modifiers,
100
+ name: field_name, id: field_id, value: field_value,
101
+ suggestions:, error: invalid?, **
102
+ )
103
+ end
104
+
94
105
  def label(text = nil, *modifiers, **, &block)
95
106
  theme[:label].new(*modifiers, text: text || (block ? nil : field_label), for: field_id, **, &block)
96
107
  end
@@ -17,6 +17,30 @@ module Forms
17
17
 
18
18
  merged.merge(data: merge_data(merged[:data], @live_trigger[:data]))
19
19
  end
20
+
21
+ # When THIS field is the form's declared `live_tags` field, render the
22
+ # ROOTLESS variant: no nested reactive root, so the outer <form> DOM-owns
23
+ # the hidden tags field and live :validate collects it. Its wire attrs were
24
+ # hoisted onto the form root by Forms::Live#form_attributes. Any other tag
25
+ # field falls through to the standard (self-rooted, non-live) widget.
26
+ def tag_field(*modifiers, suggestions: [], **)
27
+ declaration = @form.class.respond_to?(:live_tags_declaration) && @form.class.live_tags_declaration
28
+ return super unless declaration && declaration[:name] == @name
29
+
30
+ # Call-site suggestions win; otherwise fall back to the declaration's.
31
+ suggestions = declaration[:suggestions] if blank_suggestions?(suggestions)
32
+ theme[:rootless_tag_field].new(
33
+ *modifiers,
34
+ name: field_name, id: field_id, value: field_value,
35
+ suggestions:, error: invalid?, **
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ def blank_suggestions?(suggestions)
42
+ suggestions.respond_to?(:empty?) ? suggestions.empty? : suggestions.nil?
43
+ end
20
44
  end
21
45
  end
22
46
  end
data/lib/forms/live.rb CHANGED
@@ -67,8 +67,38 @@ module Forms
67
67
  def live_permit(*attrs) = @live_permit = attrs.map(&:to_s)
68
68
  def live_deny(*attrs) = @live_deny = attrs.map(&:to_s)
69
69
 
70
+ # Lift a tag field onto the form's reactive root so live :validate sees its
71
+ # comma-joined value. The widget renders ROOTLESS (no nested reactive root),
72
+ # so the outer <form> DOM-owns the hidden tags field; its wire attrs ride on
73
+ # the form root (see #form_attributes). The declared tag name is
74
+ # auto-permitted for :validate assignment.
75
+ #
76
+ # class PostForm < Forms::Base
77
+ # live model: Post
78
+ # live_tags :tags, suggestions: %w[Ruby Rails]
79
+ # def fields = field(:tags, as: :tags)
80
+ # end
81
+ #
82
+ # phlex-reactive's tag controller reads ONE data-reactive-tags-field per
83
+ # root, so a live form lifts AT MOST ONE tag field — a second raises.
84
+ def live_tags(name, suggestions: [])
85
+ if @live_tags
86
+ raise ArgumentError,
87
+ "a live form can lift at most one tag field onto its reactive root " \
88
+ "(already declared live_tags #{@live_tags[:name].inspect}); render the " \
89
+ "second as a standalone `field #{name.inspect}, as: :tags` (it stays " \
90
+ "non-live)."
91
+ end
92
+
93
+ @live_tags = { name: name.to_sym, suggestions: }
94
+ end
95
+
96
+ def live_tags_declaration = @live_tags || inherited_live(:live_tags_declaration)
97
+
70
98
  def live_permitted_attributes(model)
71
99
  permitted = @live_permit || derived_live_attributes(model)
100
+ tag = live_tags_declaration
101
+ permitted |= [tag[:name].to_s] if tag
72
102
  permitted - (@live_deny || [])
73
103
  end
74
104
 
@@ -129,11 +159,19 @@ module Forms
129
159
  # per-element).
130
160
  def form_attributes
131
161
  attrs = super
132
- mix(
162
+ attrs = mix(
133
163
  attrs,
134
164
  reactive_root(id: attrs[:id] || id),
135
165
  on(:validate, event: "input", debounce: self.class.live_debounce)
136
166
  )
167
+ # Hoist a declared tag field's wire attrs onto the form root so the rootless
168
+ # widget (rendered in the block) is driven by this root, which then DOM-owns
169
+ # its hidden field. Name/id derived through field_name/field_id — the same
170
+ # path the rootless render uses, so the [name=…]/#…_query selectors match.
171
+ tag = self.class.live_tags_declaration
172
+ return attrs unless tag
173
+
174
+ mix(attrs, Forms::TagField.root_tag_attributes(name: field_name(tag[:name]), id: field_id(tag[:name])))
137
175
  end
138
176
 
139
177
  # Untouched fields get no error set, so nothing flashes before the user
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forms
4
+ module Plain
5
+ # The rootless tag field under the plain theme: the wire contract of
6
+ # Forms::RootlessTagField with the bare-styling seams of Forms::Plain::TagField.
7
+ # Multiple-inheritance-free: subclass the rootless variant and re-apply the
8
+ # plain seam overrides.
9
+ class RootlessTagField < Forms::RootlessTagField
10
+ private
11
+
12
+ def root_classes = "tag-field"
13
+ def list_classes = nil
14
+ def menu_classes = nil
15
+ def option_classes = nil
16
+ def chip_classes = nil
17
+ def remove_classes = nil
18
+ def input_classes = @attributes[:class]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forms
4
+ module Plain
5
+ # Bare tag/chip input. Inherits the ENTIRE reactive wire contract from
6
+ # Forms::TagField (the client behavior must still work under the plain theme)
7
+ # and overrides only the styling seams to ship zero daisyUI classes. The
8
+ # invalid state rides aria-invalid on the query input, never a color class.
9
+ #
10
+ # Like its parent, this file autoloads only when Phlex::Reactive is present
11
+ # (it inherits from a ClientBindings-including class).
12
+ class TagField < Forms::TagField
13
+ private
14
+
15
+ def root_classes = "tag-field"
16
+ def list_classes = nil
17
+ def menu_classes = nil
18
+ def option_classes = nil
19
+ def chip_classes = nil
20
+ def remove_classes = nil
21
+ def input_classes = @attributes[:class]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forms
4
+ # A tag field WITHOUT its own reactive root — for use inside a Forms::Live form,
5
+ # which is itself the reactive root and carries the tag wire attributes
6
+ # (data-reactive-tags-field / data-reactive-filter-input, hoisted by
7
+ # Forms::Live#form_attributes). Emitting no nested root means the hidden tags
8
+ # field's nearest reactive-root ancestor is the <form>, so the outer form OWNS
9
+ # it and live :validate collects it (phlex-reactive #ownsField, issue #15).
10
+ #
11
+ # Reuses Forms::TagField's #body verbatim (chip/template/suggestion markup), so
12
+ # the two never drift; it only drops the root <div> wrapper.
13
+ #
14
+ # Autoloaded only when Phlex::Reactive is present (it inherits from a
15
+ # ClientBindings-including class).
16
+ class RootlessTagField < Forms::TagField
17
+ def view_template
18
+ # No reactive_root wrapper: a bare container that groups the body. The tag
19
+ # attrs live on the ancestor <form>, not here.
20
+ div(class: root_classes) { body }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Forms
4
+ # A model-bound tag/chip input, composed from phlex-reactive's CLIENT-ONLY tag
5
+ # primitives (form state, no token, zero round trips). phlex-forms owns the
6
+ # polished chrome — daisyUI styling, model binding, the hidden comma-joined
7
+ # field — while phlex-reactive owns the behavior contract.
8
+ #
9
+ # f.field :tags, as: :tags, suggestions: %w[Ruby Rails Hotwire Postgres]
10
+ # f.field :tags, as: :tags, suggestions: { "Postgres" => "postgres database db sql" }
11
+ #
12
+ # Submits ONE comma-joined param (`user[tags] = "Ruby,Rails"`), the primitive's
13
+ # wire contract — the model splits it (an `attribute :tags` + a `tags=` writer,
14
+ # or an ActiveModel array type).
15
+ #
16
+ # This file is autoloaded ONLY when Phlex::Reactive is present (the Forms::Live
17
+ # gate in lib/phlex_forms.rb) because it includes ClientBindings at class level.
18
+ # The reactive_tags_* client helpers require phlex-reactive >= 0.11.4.
19
+ #
20
+ # It uses the reactive_tags_add/option/remove helpers for the chip/query/option
21
+ # behavior, but emits the ROOT's `data-reactive-tags-field` raw rather than via
22
+ # reactive_tags(:tags): that helper compiles a SYMBOL through the class-level
23
+ # reactive_scope, but a form builder's wire name is per-instance ("user[tags]").
24
+ # The data attribute IS the public contract; any CSS selector works (issue #6
25
+ # Caveats 1 & 2). Likewise the query input targets by #id so it never submits.
26
+ class TagField < Phlex::HTML
27
+ include Phlex::Reactive::ClientBindings
28
+
29
+ def initialize(*modifiers, name:, id:, value: nil, suggestions: [], error: false,
30
+ placeholder: "Add a tag…", **attributes)
31
+ @modifiers = modifiers
32
+ @name = name # "user[tags]" — instance-dynamic
33
+ @id = id
34
+ @value = value.is_a?(Array) ? value.compact.join(",") : value.to_s
35
+ @suggestions = normalize_suggestions(suggestions)
36
+ @error = error
37
+ @placeholder = placeholder
38
+ @attributes = attributes
39
+ super()
40
+ end
41
+
42
+ def view_template
43
+ # Standalone: THIS div is the reactive root. Inside a Forms::Live form the
44
+ # widget renders rootless (Forms::RootlessTagField) — the outer <form> is
45
+ # the root and carries these tag attrs — so the form owns the hidden field.
46
+ div(**mix(reactive_root(id: "#{@id}_widget"), root_tag_attributes, class: root_classes)) do
47
+ body
48
+ end
49
+ end
50
+
51
+ # The root's tag wire attrs. Raw, not reactive_tags(:tags)/reactive_filter(:q)
52
+ # (Caveats 1 & 2): target the hidden field by [name=…] and the query input by
53
+ # #id (an id selector means the query input never submits a stray param).
54
+ # Public so Forms::Live can hoist these onto the <form> root when the widget
55
+ # is lifted rootless.
56
+ def self.root_tag_attributes(name:, id:)
57
+ { data: {
58
+ reactive_tags_field: %([name="#{name}"]),
59
+ reactive_filter_input: "##{id}_query"
60
+ } }
61
+ end
62
+
63
+ private
64
+
65
+ def root_tag_attributes = self.class.root_tag_attributes(name: @name, id: @id)
66
+
67
+ # The widget body WITHOUT its root wrapper — shared with the rootless variant
68
+ # so chip/template/suggestion markup never drifts between the two.
69
+ def body
70
+ input(type: :hidden, name: @name, id: @id, value: @value)
71
+
72
+ div(class: list_classes, data: { reactive_tags_list: true }) do
73
+ current_tags.each { |tag| chip(tag) } # server-rendered first paint
74
+ end
75
+ template(data: { reactive_tags_template: true }) { chip }
76
+
77
+ # Enter adds free text; mix AFTER reactive_listnav so Enter prefers a
78
+ # highlighted option. NO name → never submits.
79
+ input(**mix(reactive_listnav, reactive_tags_add, query_attributes))
80
+
81
+ ul(class: menu_classes) do
82
+ @suggestions.each { |tag, haystack| suggestion(tag, haystack) }
83
+ end
84
+ end
85
+
86
+ def normalize_suggestions(suggestions)
87
+ return suggestions if suggestions.is_a?(Hash)
88
+
89
+ Array(suggestions).to_h { |tag| [tag, tag.to_s.downcase] }
90
+ end
91
+
92
+ def current_tags = @value.split(",").map(&:strip).reject(&:empty?)
93
+
94
+ def query_attributes
95
+ {
96
+ id: "#{@id}_query", type: "search", autocomplete: "off",
97
+ placeholder: @placeholder, class: input_classes,
98
+ "aria-invalid": @error || nil
99
+ }.compact
100
+ end
101
+
102
+ # A preloaded suggestion that adds its tag on click (reactive_tags_option
103
+ # forces type="button" + role="option" + the tagsPick action + the tag
104
+ # param). The filter haystack rides alongside via mix.
105
+ def suggestion(tag, haystack)
106
+ li do
107
+ button(**mix(reactive_tags_option(tag),
108
+ { class: option_classes, data: { reactive_filter_text: haystack } })) { tag }
109
+ end
110
+ end
111
+
112
+ # One method, both forms: with a tag = a server-rendered chip (its remove
113
+ # button carries the tag param); without = the <template> prototype (the
114
+ # client fills the text node + the remove button's tag param per clone).
115
+ def chip(tag = nil)
116
+ span(class: chip_classes, data: { reactive_tag: tag }) do
117
+ span(data: { reactive_tag_text: true }) { tag }
118
+ button(**mix(reactive_tags_remove(tag),
119
+ { class: remove_classes, aria: { label: "Remove" } })) { "×" }
120
+ end
121
+ end
122
+
123
+ # --- styling seams (the Plain twin overrides these to bare/empty) ---
124
+
125
+ def root_classes = "tag-field flex flex-col gap-2"
126
+ def list_classes = "flex flex-wrap gap-1"
127
+ def menu_classes = "menu bg-base-200 rounded-box"
128
+ def option_classes = nil
129
+ def chip_classes = "badge badge-primary gap-1"
130
+ def remove_classes = "cursor-pointer"
131
+ def input_classes = PhlexForms::ClassMerge.merge("input w-full", @attributes[:class])
132
+ end
133
+ end
@@ -168,6 +168,9 @@ module PhlexForms
168
168
  when :file then render fo.file(*modifiers, required:, **)
169
169
  when :hidden then render fo.hidden(**)
170
170
  when :rich_textarea then render fo.rich_textarea(*modifiers, **)
171
+ # The tag widget's value lives in a hidden field; `required` on it can't be
172
+ # satisfied by the browser, so drop it (validate server-side instead).
173
+ when :tags then render fo.tag_field(*modifiers, **)
171
174
  else
172
175
  type = kind == :datetime ? :"datetime-local" : kind
173
176
  render fo.input(*(modifiers - INPUT_TYPE_MODIFIERS), type:, required:, **)
@@ -51,29 +51,44 @@ module PhlexForms
51
51
  "Gemfile, or use the plain theme."
52
52
  end
53
53
 
54
- @daisy ||= new(
54
+ @daisy ||= new({
55
55
  input: Forms::Input, select: Forms::Select, choices_select: Forms::ChoicesSelect,
56
56
  textarea: Forms::Textarea, rich_textarea: Forms::RichTextarea,
57
57
  checkbox: Forms::Checkbox, toggle: Forms::Toggle, radio: Forms::Radio,
58
58
  file: Forms::FileInput, wrapped_input: Forms::WrappedInput,
59
59
  control: Forms::FormControl, label: Forms::Label,
60
60
  field_error: Forms::FieldError, field_hint: Forms::FieldHint,
61
- submit: Forms::Submit, row: Forms::Row, group: Forms::Group
62
- )
61
+ submit: Forms::Submit, row: Forms::Row, group: Forms::Group,
62
+ # tag_field is phlex-reactive-gated (ClientBindings); only registered
63
+ # when the soft dep is present, else :tag_field raises a clear KeyError.
64
+ **reactive_roles(Forms::TagField, Forms::RootlessTagField)
65
+ })
63
66
  end
64
67
 
65
68
  # choices_select degrades to the native plain select (no choices.js) and
66
69
  # rich_textarea to a plain textarea; toggle renders as a checkbox.
67
70
  def plain
68
- @plain ||= new(
71
+ @plain ||= new({
69
72
  input: Forms::Plain::Input, select: Forms::Plain::Select, choices_select: Forms::Plain::Select,
70
73
  textarea: Forms::Plain::Textarea, rich_textarea: Forms::Plain::Textarea,
71
74
  checkbox: Forms::Plain::Checkbox, toggle: Forms::Plain::Checkbox, radio: Forms::Plain::Radio,
72
75
  file: Forms::Plain::FileInput, wrapped_input: Forms::Plain::WrappedInput,
73
76
  control: Forms::Plain::Control, label: Forms::Plain::Label,
74
77
  field_error: Forms::Plain::FieldError, field_hint: Forms::Plain::FieldHint,
75
- submit: Forms::Plain::Submit, row: Forms::Plain::Row, group: Forms::Plain::Group
76
- )
78
+ submit: Forms::Plain::Submit, row: Forms::Plain::Row, group: Forms::Plain::Group,
79
+ **reactive_roles(Forms::Plain::TagField, Forms::Plain::RootlessTagField)
80
+ })
81
+ end
82
+
83
+ private
84
+
85
+ # phlex-reactive-gated roles: mapped only when the soft dep is present. When
86
+ # absent, the leaf classes aren't autoloaded, so these roles are simply not
87
+ # registered (fetching one raises the theme's own KeyError with the list).
88
+ def reactive_roles(tag_field_class, rootless_tag_field_class)
89
+ return {} unless defined?(Phlex::Reactive)
90
+
91
+ { tag_field: tag_field_class, rootless_tag_field: rootless_tag_field_class }
77
92
  end
78
93
  end
79
94
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PhlexForms
4
- VERSION = "0.2.2"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/phlex_forms.rb CHANGED
@@ -91,10 +91,15 @@ loader.ignore("#{__dir__}/phlex_forms/rubocop.rb") # cop entry point (RuboCop::
91
91
  loader.ignore("#{__dir__}/phlex_forms/engine.rb")
92
92
 
93
93
  # The live-validation layer includes Phlex::Reactive::Component at class level,
94
- # so it can only load when the phlex-reactive soft dependency is present.
94
+ # and the tag_field leaf includes Phlex::Reactive::ClientBindings both can only
95
+ # load when the phlex-reactive soft dependency is present.
95
96
  unless defined?(Phlex::Reactive)
96
97
  loader.ignore("#{__dir__}/forms/live.rb")
97
98
  loader.ignore("#{__dir__}/forms/live")
99
+ loader.ignore("#{__dir__}/forms/tag_field.rb")
100
+ loader.ignore("#{__dir__}/forms/plain/tag_field.rb")
101
+ loader.ignore("#{__dir__}/forms/rootless_tag_field.rb")
102
+ loader.ignore("#{__dir__}/forms/plain/rootless_tag_field.rb")
98
103
  end
99
104
 
100
105
  loader.setup
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.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -141,17 +141,21 @@ files:
141
141
  - lib/forms/plain/input.rb
142
142
  - lib/forms/plain/label.rb
143
143
  - lib/forms/plain/radio.rb
144
+ - lib/forms/plain/rootless_tag_field.rb
144
145
  - lib/forms/plain/row.rb
145
146
  - lib/forms/plain/select.rb
146
147
  - lib/forms/plain/submit.rb
148
+ - lib/forms/plain/tag_field.rb
147
149
  - lib/forms/plain/textarea.rb
148
150
  - lib/forms/plain/wrapped_input.rb
149
151
  - lib/forms/radio.rb
150
152
  - lib/forms/range.rb
151
153
  - lib/forms/rich_textarea.rb
154
+ - lib/forms/rootless_tag_field.rb
152
155
  - lib/forms/row.rb
153
156
  - lib/forms/select.rb
154
157
  - lib/forms/submit.rb
158
+ - lib/forms/tag_field.rb
155
159
  - lib/forms/textarea.rb
156
160
  - lib/forms/time_zone_select.rb
157
161
  - lib/forms/toggle.rb