tam_select 1.0.0 → 1.1.1
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/README.md +88 -5
- data/lib/generators/tam_select/install_generator.rb +1 -0
- data/lib/generators/tam_select/templates/tam_select_remote.rb +54 -0
- data/lib/generators/tam_select/templates/tam_select_remote_controller.rb +3 -41
- data/lib/tam_select/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3cdee3c229caaeda5d7f572395009c1ffd03e58c1f5376c215ced0e751c35fb9
|
|
4
|
+
data.tar.gz: 9896dd22aa1dc9af3727596c1bee1d82ca6b8f1462a3e24a5177b10306853888
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 271d3b88383037c504189080665c0c15e19a640e6f8efe0d580683d9f543a0a56ff496a667064b788fbd8a8f1ddc8f48512d755cfc10985ec043d36c042891ce
|
|
7
|
+
data.tar.gz: be82081ad386749cb1ff40f1cd48ca80417104712c417de25dccc969082f3f2edfce09a912bce53467c34e170b1a0235245caede8d53717b7fff5d3b21d8f072
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
the # Tam Select
|
|
2
2
|
|
|
3
3
|
[](LICENSE)
|
|
4
|
-
[](https://github.com/tamiru/tam_select)
|
|
5
5
|
|
|
6
6
|
**Tam Select** is an accessible, searchable select component for Ruby on Rails, built for Simple Form, Stimulus, Turbo, and Tailwind CSS. It keeps the native `<select>` as the source of truth, so Rails form submission, validation, selected values, and browser autofill continue to work.
|
|
7
7
|
|
|
@@ -36,6 +36,7 @@ app/javascript/tam_select/tam_select.js
|
|
|
36
36
|
app/javascript/controllers/tam_select_controller.js
|
|
37
37
|
app/inputs/tam_select_input.rb
|
|
38
38
|
app/controllers/concerns/tam_select_paginatable.rb
|
|
39
|
+
app/controllers/concerns/tam_select_remote.rb
|
|
39
40
|
app/controllers/tam_select_remote_controller.rb
|
|
40
41
|
app/helpers/tam_select_helper.rb
|
|
41
42
|
```
|
|
@@ -131,7 +132,9 @@ Stimulus destroys generated markup when Turbo removes the select and recreates i
|
|
|
131
132
|
|
|
132
133
|
## Simple Form
|
|
133
134
|
|
|
134
|
-
The install generator creates `app/inputs/tam_select_input.rb`. Use
|
|
135
|
+
The install generator creates `app/inputs/tam_select_input.rb`. Use `as: :tam_select` with any Simple Form collection input.
|
|
136
|
+
|
|
137
|
+
### Local collection
|
|
135
138
|
|
|
136
139
|
```erb
|
|
137
140
|
<%= form.input :region_id,
|
|
@@ -139,16 +142,49 @@ The install generator creates `app/inputs/tam_select_input.rb`. Use it like any
|
|
|
139
142
|
collection: Region.order(:name),
|
|
140
143
|
label_method: :name,
|
|
141
144
|
value_method: :id,
|
|
145
|
+
prompt: "Select region" %>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Remote collection
|
|
149
|
+
|
|
150
|
+
Keep the currently selected record in the initial collection so edit forms can display its label before the AJAX request completes:
|
|
151
|
+
|
|
152
|
+
```erb
|
|
153
|
+
<%= form.input :region_id,
|
|
154
|
+
as: :tam_select,
|
|
155
|
+
collection: [form.object.region].compact,
|
|
156
|
+
label_method: :name,
|
|
157
|
+
value_method: :id,
|
|
142
158
|
prompt: "Select region",
|
|
143
159
|
input_html: {
|
|
144
160
|
tam_options: {
|
|
145
|
-
remoteUrl:
|
|
146
|
-
minQueryLength: 1
|
|
161
|
+
remoteUrl: tam_select_options_regions_path(format: :json),
|
|
162
|
+
minQueryLength: 1,
|
|
163
|
+
debounce: 250
|
|
147
164
|
}
|
|
148
165
|
} %>
|
|
149
166
|
```
|
|
150
167
|
|
|
151
|
-
|
|
168
|
+
The concern and route required by this example are described in [Add remote search to an existing controller](#add-remote-search-to-an-existing-controller).
|
|
169
|
+
|
|
170
|
+
### Multiple selection
|
|
171
|
+
|
|
172
|
+
```erb
|
|
173
|
+
<%= form.input :skill_ids,
|
|
174
|
+
as: :tam_select,
|
|
175
|
+
collection: Skill.order(:name),
|
|
176
|
+
label_method: :name,
|
|
177
|
+
value_method: :id,
|
|
178
|
+
input_html: {
|
|
179
|
+
multiple: true,
|
|
180
|
+
tam_options: {
|
|
181
|
+
searchable: true,
|
|
182
|
+
closeAfterSelect: false
|
|
183
|
+
}
|
|
184
|
+
} %>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Options are passed under `input_html[:tam_options]`. Common options include `searchable`, `creatable`, `clearable`, `placeholder`, `searchPlaceholder`, `remoteUrl`, `minQueryLength`, and `debounce`. See [Main options](#main-options) for defaults.
|
|
152
188
|
|
|
153
189
|
## Remote API contract
|
|
154
190
|
|
|
@@ -172,6 +208,53 @@ The generator installs `TamSelectPaginatable` as a Rails response helper. A comp
|
|
|
172
208
|
|
|
173
209
|
Secure remote endpoints exactly like other Rails JSON endpoints. Scope records by the current user's permissions and never trust a submitted value merely because it appeared in the dropdown.
|
|
174
210
|
|
|
211
|
+
### Add remote search to an existing controller
|
|
212
|
+
|
|
213
|
+
Include `TamSelectRemote` and declare the allowed model and searchable fields. This adds the public `tam_select_options` action to the controller:
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
# app/controllers/regions_controller.rb
|
|
217
|
+
class RegionsController < ApplicationController
|
|
218
|
+
include TamSelectRemote
|
|
219
|
+
|
|
220
|
+
tam_select_remote(
|
|
221
|
+
model: Region,
|
|
222
|
+
label: :name,
|
|
223
|
+
value: :id,
|
|
224
|
+
search_by: %i[name code],
|
|
225
|
+
scope: -> { Region.order(:name) },
|
|
226
|
+
per_page: 20
|
|
227
|
+
)
|
|
228
|
+
end
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The optional `scope` lambda runs in the controller context, so it can use `current_user`, `current_account`, or an authorization policy.
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
# config/routes.rb
|
|
235
|
+
resources :regions do
|
|
236
|
+
get :tam_select_options, on: :collection, defaults: { format: :json }
|
|
237
|
+
end
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Point Simple Form to that collection action:
|
|
241
|
+
|
|
242
|
+
```erb
|
|
243
|
+
<%= form.input :region_id,
|
|
244
|
+
as: :tam_select,
|
|
245
|
+
collection: [form.object.region].compact,
|
|
246
|
+
label_method: :name,
|
|
247
|
+
value_method: :id,
|
|
248
|
+
input_html: {
|
|
249
|
+
tam_options: {
|
|
250
|
+
remoteUrl: tam_select_options_regions_path(format: :json),
|
|
251
|
+
minQueryLength: 1
|
|
252
|
+
}
|
|
253
|
+
} %>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Typing sends `GET /regions/tam_select_options.json?q=addis&page=1` and receives the standard Tam Select JSON payload.
|
|
257
|
+
|
|
175
258
|
### Generic remote controller
|
|
176
259
|
|
|
177
260
|
The installer generates `TamSelectRemoteController`. Create a small subclass for each allowed remote source:
|
|
@@ -18,6 +18,7 @@ module TamSelect
|
|
|
18
18
|
|
|
19
19
|
def copy_controller_concern
|
|
20
20
|
copy_file "lib/generators/tam_select/templates/tam_select_paginatable.rb", "app/controllers/concerns/tam_select_paginatable.rb"
|
|
21
|
+
copy_file "lib/generators/tam_select/templates/tam_select_remote.rb", "app/controllers/concerns/tam_select_remote.rb"
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def copy_remote_controller
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module TamSelectRemote
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
include TamSelectPaginatable
|
|
6
|
+
class_attribute :tam_select_remote_config, instance_writer: false
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class_methods do
|
|
10
|
+
def tam_select_remote(model:, label:, value: :id, search_by: nil, scope: nil, per_page: 20)
|
|
11
|
+
self.tam_select_remote_config = {
|
|
12
|
+
model: model,
|
|
13
|
+
label: label,
|
|
14
|
+
value: value,
|
|
15
|
+
search_by: Array(search_by || label),
|
|
16
|
+
scope: scope,
|
|
17
|
+
per_page: per_page
|
|
18
|
+
}.freeze
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def tam_select_options
|
|
23
|
+
config = tam_select_remote_config!
|
|
24
|
+
records = tam_select_remote_search(tam_select_remote_scope(config), config)
|
|
25
|
+
|
|
26
|
+
render json: tam_select_payload(
|
|
27
|
+
records,
|
|
28
|
+
label: config[:label],
|
|
29
|
+
value: config[:value],
|
|
30
|
+
per_page: config[:per_page]
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def tam_select_remote_config!
|
|
37
|
+
self.class.tam_select_remote_config ||
|
|
38
|
+
raise("Configure #{self.class.name} with tam_select_remote(...)")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def tam_select_remote_scope(config)
|
|
42
|
+
config[:scope] ? instance_exec(&config[:scope]) : config[:model].all
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def tam_select_remote_search(records, config)
|
|
46
|
+
query = params[:q].to_s.strip
|
|
47
|
+
return records if query.blank?
|
|
48
|
+
|
|
49
|
+
model = config[:model]
|
|
50
|
+
pattern = "%#{model.sanitize_sql_like(query)}%"
|
|
51
|
+
predicates = config[:search_by].map { |field| model.arel_table[field].matches(pattern) }
|
|
52
|
+
records.where(predicates.reduce(&:or))
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -1,44 +1,6 @@
|
|
|
1
1
|
class TamSelectRemoteController < ApplicationController
|
|
2
|
-
include
|
|
2
|
+
include TamSelectRemote
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def self.tam_select(model:, label:, value: :id, search_by: nil, per_page: 20)
|
|
7
|
-
self.tam_select_config = {
|
|
8
|
-
model: model,
|
|
9
|
-
label: label,
|
|
10
|
-
value: value,
|
|
11
|
-
search_by: Array(search_by || label),
|
|
12
|
-
per_page: per_page
|
|
13
|
-
}.freeze
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def index
|
|
17
|
-
config = self.class.tam_select_config
|
|
18
|
-
raise "Configure #{self.class.name} with tam_select(...)" unless config
|
|
19
|
-
|
|
20
|
-
records = search(tam_select_scope(config), config)
|
|
21
|
-
render json: tam_select_payload(
|
|
22
|
-
records,
|
|
23
|
-
label: config[:label],
|
|
24
|
-
value: config[:value],
|
|
25
|
-
per_page: config[:per_page]
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def tam_select_scope(config)
|
|
32
|
-
config[:model].all
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def search(records, config)
|
|
36
|
-
query = params[:q].to_s.strip
|
|
37
|
-
return records if query.blank?
|
|
38
|
-
|
|
39
|
-
model = config[:model]
|
|
40
|
-
pattern = "%#{model.sanitize_sql_like(query)}%"
|
|
41
|
-
predicates = config[:search_by].map { |field| model.arel_table[field].matches(pattern) }
|
|
42
|
-
records.where(predicates.reduce(&:or))
|
|
43
|
-
end
|
|
4
|
+
# Configure this base controller in a subclass, or include TamSelectRemote
|
|
5
|
+
# directly in an existing resource controller. See the Tam Select README.
|
|
44
6
|
end
|
data/lib/tam_select/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tam_select
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tamiru Hailu
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- lib/generators/tam_select/templates/tam_select_helper.rb
|
|
63
63
|
- lib/generators/tam_select/templates/tam_select_input.rb
|
|
64
64
|
- lib/generators/tam_select/templates/tam_select_paginatable.rb
|
|
65
|
+
- lib/generators/tam_select/templates/tam_select_remote.rb
|
|
65
66
|
- lib/generators/tam_select/templates/tam_select_remote_controller.rb
|
|
66
67
|
- lib/tam_select.rb
|
|
67
68
|
- lib/tam_select/engine.rb
|