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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8906e8c09072b22eebdaeaa49b0fb57a395aa30693bf6f9986ebc3c0687e254d
4
- data.tar.gz: 802a739018ffc6e269a688a459464e17e213c6d6ee195d7aacd6e396aa74f97a
3
+ metadata.gz: 19e487e3834d936ad510df92a00a47f99447a31efd61145fd31798b5a44cbaec
4
+ data.tar.gz: ec73a8e829f7533ac79ebe3d4cca687b0d519c6e048940e2978b3ad25d44f0cc
5
5
  SHA512:
6
- metadata.gz: b37b6a8dfbe765ab42af40cc6c2db86a7f568c049af2a6c3a47d3ef5871bacb2351896b15b9f1368ad446a97f6d9e1a3eab44cbd1d045b1ab2efd68a2fbfbb31
7
- data.tar.gz: 84255b48f37a7b0926e74228cd24b1ff0d5080f7c7dedc89b7b7ecedf86eff3a871299bbd002882eefe9ad3f0b4800ea3b706eae7ddd39806ca11d2317fecdf0
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`, `CheckboxGroup`,
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
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module YummyGuide
4
4
  module Administrate
5
- VERSION = "0.6.0"
5
+ VERSION = "0.6.2"
6
6
  end
7
7
  end
@@ -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.0
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-25 00:00:00.000000000 Z
11
+ date: 2026-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: administrate