yummy-guide-generic-administrate 0.6.0 → 0.6.2
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 +13 -2
- data/app/assets/javascripts/yummy_guide_administrate/filter_form.js +70 -1
- data/lib/yummy_guide/administrate/filters.rb +33 -0
- data/lib/yummy_guide/administrate/version.rb +1 -1
- data/spec/yummy_guide/administrate/filter_controls_helper_spec.rb +32 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19e487e3834d936ad510df92a00a47f99447a31efd61145fd31798b5a44cbaec
|
|
4
|
+
data.tar.gz: ec73a8e829f7533ac79ebe3d4cca687b0d519c6e048940e2978b3ad25d44f0cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d3da1b8ec25f8a3b0e4c01ef625f35a9e2d369c8a2656cb98f0813ec6ffe6343c375cfa91d42038c4d3527eafb3b61cdff8446cb508124a68c585a80fd5f834
|
|
7
|
+
data.tar.gz: d590ff5f0b427417f86a5951bdb514ab16ee27659ddccbfd22b84737a4f34ce7b78964ae5dc4bd10b4ba075df6363c634f87f274b2489945589fb1df44100561
|
data/README.md
CHANGED
|
@@ -355,13 +355,14 @@ dashboard の `FILTER_PATH` / `FILTER_CLEAR_PATH` より優先されます。
|
|
|
355
355
|
) %>
|
|
356
356
|
```
|
|
357
357
|
|
|
358
|
-
標準 Field は `Text`, `Select`, `Checkbox`, `RadioGroup`, `
|
|
359
|
-
`DateRange`, `DatetimeRange`, `DatetimeLocalRange`, `Custom` です。
|
|
358
|
+
標準 Field は `Text`, `Select`, `Checkbox`, `RadioGroup`, `BooleanRadioGroup`,
|
|
359
|
+
`CheckboxGroup`, `DateRange`, `DatetimeRange`, `DatetimeLocalRange`, `Custom` です。
|
|
360
360
|
主な option は次のとおりです。
|
|
361
361
|
|
|
362
362
|
- 共通: `label`, `default`, `if`, `class`, `id`, `placeholder`, `inputmode`, `pattern`
|
|
363
363
|
- 選択系: `collection` または `options`, `select_options`
|
|
364
364
|
- checkbox: `checked_value`, `unchecked_value`
|
|
365
|
+
- boolean radio group: `unspecified_label`, `true_label`, `false_label`
|
|
365
366
|
- checkbox group: `group`
|
|
366
367
|
- range: `from`, `to`, `from_default`, `to_default`, `css_class`
|
|
367
368
|
|
|
@@ -379,6 +380,12 @@ FILTER_ATTRIBUTES = {
|
|
|
379
380
|
label: "Status",
|
|
380
381
|
collection: ->(_view, locals) { locals[:status_collection] },
|
|
381
382
|
select_options: { include_blank: true }
|
|
383
|
+
),
|
|
384
|
+
visible: YummyGuide::Administrate::Filters::BooleanRadioGroup.with_options(
|
|
385
|
+
label: "Visible",
|
|
386
|
+
unspecified_label: "-",
|
|
387
|
+
true_label: "Visible",
|
|
388
|
+
false_label: "Hidden"
|
|
382
389
|
)
|
|
383
390
|
}.freeze
|
|
384
391
|
```
|
|
@@ -409,6 +416,10 @@ FILTER_ATTRIBUTES = {
|
|
|
409
416
|
</tr>
|
|
410
417
|
```
|
|
411
418
|
|
|
419
|
+
`filter_form.js` を読み込んでいる場合、`data-behavior="filter-field-clear"` を持つ
|
|
420
|
+
button は同じ `tr` 内の input / select / textarea / checkbox / radio / datetime
|
|
421
|
+
filter だけをクリアします。
|
|
422
|
+
|
|
412
423
|
複数画面で再利用する filter 型は `YummyGuide::Administrate::Filters::Base` を継承して作ります。
|
|
413
424
|
単一 input なら `input` を実装し、行全体を制御したい場合は `row` または `input_cell` を上書きします。
|
|
414
425
|
|
|
@@ -43,6 +43,12 @@
|
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
formEl.querySelectorAll('[data-behavior="filter-field-clear"]').forEach(function(buttonEl) {
|
|
47
|
+
buttonEl.addEventListener("click", function(event) {
|
|
48
|
+
clearFilterField(formEl, event);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
46
52
|
formEl.querySelectorAll('[data-behavior="checkbox-group-select-all"]').forEach(function(buttonEl) {
|
|
47
53
|
buttonEl.addEventListener("click", function() {
|
|
48
54
|
setCheckboxGroupState(formEl, buttonEl.dataset.target, true);
|
|
@@ -58,6 +64,19 @@
|
|
|
58
64
|
syncDatetimeFilterFields(formEl);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
function clearFilterField(formEl, event) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
|
|
70
|
+
var rowEl = event.currentTarget.closest("tr");
|
|
71
|
+
if (!rowEl) return;
|
|
72
|
+
|
|
73
|
+
clearTextControls(rowEl);
|
|
74
|
+
clearChoiceControls(rowEl);
|
|
75
|
+
clearSelectControls(rowEl);
|
|
76
|
+
clearDatetimeFilters(rowEl);
|
|
77
|
+
syncDatetimeFilterFields(formEl);
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
function clearFormFields(formEl) {
|
|
62
81
|
formEl.querySelectorAll("input, select, textarea").forEach(function(fieldEl) {
|
|
63
82
|
if (fieldEl.disabled || fieldEl.type === "hidden" || fieldEl.type === "submit") {
|
|
@@ -86,6 +105,57 @@
|
|
|
86
105
|
syncDatetimeFilterFields(formEl);
|
|
87
106
|
}
|
|
88
107
|
|
|
108
|
+
function clearTextControls(rowEl) {
|
|
109
|
+
rowEl.querySelectorAll("input, textarea").forEach(function(inputEl) {
|
|
110
|
+
var inputType = (inputEl.getAttribute("type") || "").toLowerCase();
|
|
111
|
+
|
|
112
|
+
if (inputType === "checkbox" || inputType === "radio") return;
|
|
113
|
+
if (inputType === "hidden" && inputEl.dataset.datetimePart !== "combined") return;
|
|
114
|
+
|
|
115
|
+
inputEl.value = "";
|
|
116
|
+
inputEl.setAttribute("value", "");
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function clearChoiceControls(rowEl) {
|
|
121
|
+
rowEl.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach(function(inputEl) {
|
|
122
|
+
inputEl.checked = false;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function clearSelectControls(rowEl) {
|
|
127
|
+
rowEl.querySelectorAll("select").forEach(function(selectEl) {
|
|
128
|
+
if (selectEl.querySelector('option[value=""]')) {
|
|
129
|
+
selectEl.value = "";
|
|
130
|
+
} else if (selectEl.querySelector('option[value="all"]')) {
|
|
131
|
+
selectEl.value = "all";
|
|
132
|
+
} else {
|
|
133
|
+
selectEl.selectedIndex = -1;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function clearDatetimeFilters(rowEl) {
|
|
139
|
+
rowEl.querySelectorAll("[data-datetime-filter]").forEach(function(groupEl) {
|
|
140
|
+
var combinedEl = groupEl.querySelector('[data-datetime-part="combined"]');
|
|
141
|
+
var dateEl = groupEl.querySelector('[data-datetime-part="date"]');
|
|
142
|
+
|
|
143
|
+
if (combinedEl) {
|
|
144
|
+
combinedEl.value = "";
|
|
145
|
+
combinedEl.setAttribute("value", "");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (dateEl) {
|
|
149
|
+
dateEl.value = "";
|
|
150
|
+
dateEl.setAttribute("value", "");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
clearDatetimeTimeParts(groupEl);
|
|
154
|
+
syncDatetimeTimeDisabledState(groupEl);
|
|
155
|
+
syncBlankMinuteOptionState(groupEl);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
89
159
|
function setCheckboxGroupState(formEl, groupName, checked) {
|
|
90
160
|
if (!groupName) return;
|
|
91
161
|
|
|
@@ -216,4 +286,3 @@
|
|
|
216
286
|
|
|
217
287
|
document.addEventListener("turbo:load", initializeFromDocument);
|
|
218
288
|
})();
|
|
219
|
-
|
|
@@ -152,6 +152,39 @@ module YummyGuide
|
|
|
152
152
|
end
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
+
class BooleanRadioGroup < Base
|
|
156
|
+
protected
|
|
157
|
+
|
|
158
|
+
def input_cell(view_context, _form, scope, current_values, locals)
|
|
159
|
+
selected = current_value(current_values).to_s
|
|
160
|
+
controls = boolean_options(view_context, locals).map do |label, value|
|
|
161
|
+
value_string = value.to_s
|
|
162
|
+
id = "#{scope}_#{name}_#{value_string.presence || "unspecified"}".parameterize(separator: "_")
|
|
163
|
+
|
|
164
|
+
view_context.content_tag(:label, style: "display: inline-flex; align-items: center; gap: 6px;") do
|
|
165
|
+
view_context.safe_join([
|
|
166
|
+
view_context.radio_button_tag("#{scope}[#{name}]", value, selected == value_string, id: id),
|
|
167
|
+
view_context.content_tag(:span, label)
|
|
168
|
+
])
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
view_context.content_tag(:td) do
|
|
173
|
+
view_context.content_tag(:div, view_context.safe_join(controls), style: "display: flex; align-items: center; gap: 12px; flex-wrap: wrap;")
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
def boolean_options(view_context, locals)
|
|
180
|
+
[
|
|
181
|
+
[evaluate_option(options.fetch(:unspecified_label, "Unspecified"), view_context, locals), ""],
|
|
182
|
+
[evaluate_option(options.fetch(:true_label, "true"), view_context, locals), "true"],
|
|
183
|
+
[evaluate_option(options.fetch(:false_label, "false"), view_context, locals), "false"]
|
|
184
|
+
]
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
155
188
|
class CheckboxGroup < Base
|
|
156
189
|
protected
|
|
157
190
|
|
|
@@ -49,6 +49,38 @@ RSpec.describe YummyGuide::Administrate::FilterControlsHelper do
|
|
|
49
49
|
expect(document.at_css('select[name="search_options[status]"] option[selected]')["value"]).to eq("closed")
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# boolean radio group が未指定・true・falseを横並びで描画し、ラベルと選択状態を反映することを確認する
|
|
53
|
+
it "renders boolean radio group controls with custom labels" do
|
|
54
|
+
dashboard = Class.new
|
|
55
|
+
dashboard.const_set(
|
|
56
|
+
:FILTER_ATTRIBUTES,
|
|
57
|
+
{
|
|
58
|
+
visible: YummyGuide::Administrate::Filters::BooleanRadioGroup.with_options(
|
|
59
|
+
label: "Visible",
|
|
60
|
+
unspecified_label: "-",
|
|
61
|
+
true_label: "Visible",
|
|
62
|
+
false_label: "Hidden"
|
|
63
|
+
)
|
|
64
|
+
}.freeze
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
html = helper_host.admin_filter_controls(
|
|
68
|
+
dashboard: dashboard,
|
|
69
|
+
path: "/admin/resources",
|
|
70
|
+
search_options: { visible: "false" }
|
|
71
|
+
)
|
|
72
|
+
document = fragment(html)
|
|
73
|
+
|
|
74
|
+
wrapper = document.at_css('input[name="search_options[visible]"]').ancestors("div").first
|
|
75
|
+
|
|
76
|
+
expect(document.at_css('input[type="radio"][name="search_options[visible]"][value=""]')).to be_present
|
|
77
|
+
expect(document.at_css('input[type="radio"][name="search_options[visible]"][value="true"]')).to be_present
|
|
78
|
+
expect(document.at_css('input[type="radio"][name="search_options[visible]"][value="false"]')).to be_present
|
|
79
|
+
expect(document.at_css('input[type="radio"][name="search_options[visible]"][value="false"]')["checked"]).to eq("checked")
|
|
80
|
+
expect(wrapper["style"]).to include("display: flex")
|
|
81
|
+
expect(document.text).to include("-", "Visible", "Hidden")
|
|
82
|
+
end
|
|
83
|
+
|
|
52
84
|
# dashboard に定義した FILTER_PATH をフィルター送信先として利用できることを確認する
|
|
53
85
|
it "uses dashboard filter path when no explicit path is passed" do
|
|
54
86
|
dashboard = Class.new
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yummy-guide-generic-administrate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- akatsuki-kk
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: administrate
|